博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
TeaFramework——AOP的实现
阅读量:5743 次
发布时间:2019-06-18

本文共 7340 字,大约阅读时间需要 24 分钟。

  hot3.png

    AOP是拦截的方法后要做对应的织入,那么先定义几种通知:前置通知、后置通知、异常通知、结束通知,代码如下:

public interface AopAdvice {	public void before(Proxy proxy);	public void after(Proxy proxy);	public void exception(Proxy proxy);	public void end(Proxy proxy);}

    接下来看,哪些类里的哪些方法要被拦截呢?我们需要定义一个表达式,这么使用正则表达式,相对来说比较简单,也是一个通用的规范。只有配到正则的方法,才会执行对应织入的增强。

@Target({ ElementType.TYPE })@Retention(RetentionPolicy.RUNTIME)public @interface Aspect {	public String classRegex();	public String beforeRegex() default "";	public String afterRegex() default "";	public String exceptionRegex() default "";	public String endRegex() default "";	public int order() default 0;}

    classRegex定义要拦截的class正则

    beforeRegex定义需要前置增强的方法的正则匹配

    afterRegex定义需要后置增强的方法的正则匹配

    exceptionRegex定义需要进行异常处理增强方法的正则匹配

    endRegex定义在方法执行完成之后需要额外处理方法的正则匹配

    order表示排序,有多个aop规则命中时,就需要排序,这里是升序排列,order越大,该aop越靠后执行

    AOP实现的本质是动态代理,那么我们需要定义一个代理接口

public interface Proxy {	public void invoke(Proxy proxy) throws Throwable;}

    这个接口里定义了一个方法invoke(Proxy proxy),参数是它自己,那么什么这样定义呢?这里就要谈到一种设计模式“责任链”,由于被cglib代理过的类不能再次被代理,所以这里才用责任链模式来解决。具体来看责任链在这里怎么运作的。

public class BeanProxy implements Proxy {	private Object obj;	private Object[] args;	private Method method;	private MethodProxy methodProxy;	private boolean invokeSuper = true;	private int proxyIndex = -1;	private Object result = null;	private InterfaceExecutor interfaceExecutor;	private List
proxyList = new ArrayList
(); public BeanProxy() { } public BeanProxy(Object obj, Object[] args, Method method, MethodProxy methodProxy) { this.obj = obj; this.args = args; this.method = method; this.methodProxy = methodProxy; } @Override public void invoke(Proxy proxy) throws Throwable { proxyIndex++; if (proxyIndex < proxyList.size()) { proxyList.get(proxyIndex).invoke(this); } else if (invokeSuper) { result = methodProxy.invokeSuper(obj, args); } else { result = interfaceExecutor.invoke(obj, method, args, methodProxy); } } public BeanProxy addProxyChain(Proxy proxy) { proxyList.add(proxy); return this; } public BeanProxy setInterfaceExecutor(InterfaceExecutor interfaceExecutor) { this.interfaceExecutor = interfaceExecutor; return this; }………………省略了一些set方法}

    BeanProxy实现了invoke方法,上游节点将调用权给下游节点,直到指针指向链的末尾,当指针指向链尾端是,如果设置的invokeSuper为true(invokeSuper表示是否执行被代理的父类方法),则执行父类方法,如果父类方法是接口,则一定要设置为false。在《TeaFramework——ORM框架的实现(一)》留了一个InterfaceExecutor的疑问,InterfaceExecutor是专门为被代理的类是接口的场景准备的,当被代理的类为接口,但是又想执行一些原本想实现的额外操作,就可以用InterfaceExecutor来实现,例如ORM中OrmProxy就是一个典型的例子。

    特别说明:对于链式调用,invoke方法之前的方法是正序调用,invoke方法之后是逆序。这一点和所有责任链调用过程一致,例如Filter、Struts2的拦截等。

    对于AOP代理,TeaFramework中实现了一个抽象代理类,要编写自己的AOP只需继承这个抽象类,实现对应的前置、后置等方法。

public abstract class AbstractProxy implements Proxy, AopAdvice {	private Pattern beforePattern;	private Pattern afterPattern;	private Pattern exceptionPattern;	private Pattern endPattern;	@Override	public void invoke(Proxy proxy) throws Throwable {		BeanProxy beanProxy = (BeanProxy) proxy;		try {			if (beforePattern != null && beforePattern.matcher(beanProxy.getMethod().getName()).find()) {				before(proxy);			}			proxy.invoke(proxy);			if (afterPattern != null && afterPattern.matcher(beanProxy.getMethod().getName()).find()) {				after(proxy);			}		} catch (Throwable e) {			if (exceptionPattern != null && exceptionPattern.matcher(beanProxy.getMethod().getName()).find()) {				exception(proxy);			}			throw e;		} finally {			if (endPattern != null && endPattern.matcher(beanProxy.getMethod().getName()).find()) {				end(beanProxy);			}		}	}	@Override	public void before(Proxy proxy) {	}	@Override	public void after(Proxy proxy) {	}	@Override	public void exception(Proxy proxy) {	}	@Override	public void end(Proxy proxy) {	}	public final void setBeforePattern(Pattern beforePattern) {		this.beforePattern = beforePattern;	}	public final void setAfterPattern(Pattern afterPattern) {		this.afterPattern = afterPattern;	}	public final void setExceptionPattern(Pattern exceptionPattern) {		this.exceptionPattern = exceptionPattern;	}	public void setEndPattern(Pattern endPattern) {		this.endPattern = endPattern;	}}

    有了这些工具,我们就可以实现自己的AOP业务了,看个例子,比方说对应新增和修改而言,我们需要把CCUU值填充进去,这个时候就可以用AOP了,请看下面的代码

@Aspect(classRegex = "org.teaframework.erp.*.dao.*", beforeRegex = "add.*|update.*")public class DomainAspect extends AbstractProxy {	@Override	public void before(Proxy proxy) {		BeanProxy beanProxy = (BeanProxy) proxy;		if (beanProxy.getArgs() != null && beanProxy.getArgs()[0] instanceof BaseDomain) {			BaseDomain domain = (BaseDomain) beanProxy.getArgs()[0];			domain.setCreateDate(new Date());			domain.setCreateUser(ThreadVariable.getUser().getUserName());			domain.setUpdateDate(new Date());			domain.setUpdateUser(ThreadVariable.getUser().getUserName());		}	}}

    classRegex定义了要拦截所有dao包里的类,beforeRegex定义具体拦截add或者upate开头的方法进行前置增强织入,重写AbstractProxy的before方法即可实现前置增强了。

    接下来问题来了,DomainAspect等自己实现的AOP类,怎么和bean容器的bean关联起来的呢?我们接着看。

    框架启动时,有一个AopBeanInitialization,这个类将所有加了Aspect注解的类扫描到,然后实例化,根据order升序排列,放到一个Map里。

public class AopBeanInitialization implements Initialization {	public static final List
AOP_BEAN_LIST = new ArrayList
(); public static final Map
, Pattern> PATTERN_MAPPING = new HashMap
, Pattern>(); @Override public void init() throws Exception { for (String classPath : ScanPackageInitialization.classPaths) {// aop Class
clazz = ClassLoaderUtil.loadClass(classPath); if (clazz.isAnnotationPresent(Aspect.class)) { AbstractProxy aspectBean = (AbstractProxy) clazz.newInstance(); Aspect aspect = clazz.getAnnotation(Aspect.class); PATTERN_MAPPING.put(clazz, Pattern.compile(aspect.classRegex())); if (!"".equals(aspect.beforeRegex())) { aspectBean.setBeforePattern(Pattern.compile(aspect.beforeRegex())); } if (!"".equals(aspect.afterRegex())) { aspectBean.setAfterPattern(Pattern.compile(aspect.afterRegex())); } if (!"".equals(aspect.exceptionRegex())) { aspectBean.setExceptionPattern(Pattern.compile(aspect.exceptionRegex())); } if (!"".equals(aspect.endRegex())) { aspectBean.setEndPattern(Pattern.compile(aspect.endRegex())); } AOP_BEAN_LIST.add(new AspectClassMapping(clazz, aspectBean, aspect.order())); } } Collections.sort(AOP_BEAN_LIST, new Comparator
() { public int compare(AspectClassMapping o1, AspectClassMapping o2) { return o2.getOrder() - o1.getOrder(); } }); }}

    AopBeanInitialization执行完成之后,BeanContainerInitialization开始执行,对于命中了正则规则的类就需要动态代理了,然后将对应的AOP代理对象add到代理链中,请看下来的代码

private void addProxy(BeanProxy beanProxy, Class
clazz) { for (AspectClassMapping aspectClassMapping : AopBeanInitialization.AOP_BEAN_LIST) { Class
proxyClass = aspectClassMapping.getClazz(); Matcher matcher = AopBeanInitialization.PATTERN_MAPPING.get(proxyClass).matcher(clazz.getName()); if (matcher.find()) { beanProxy.addProxyChain(aspectClassMapping.getAspectBean()); } } if (clazz.isAnnotationPresent(Transcation.class)) { beanProxy.addProxyChain(transcationProxy); } else { Method[] methods = clazz.getMethods(); for (Method method : methods) { if (method.isAnnotationPresent(Transcation.class)) { beanProxy.addProxyChain(transcationProxy); break; } } } }

    一旦AOP代理对象被放入对应的代理链中,就可以链式调用了,便发挥出了AOP的作用,上面的代码还有关于事物Transcation的代码,本篇博客先不做介绍,将会有单独的一篇博客讲事物控制的设计。 

 项目地址:

 博客:

转载于:https://my.oschina.net/u/1778239/blog/1584781

你可能感兴趣的文章
python读excel写入mysql小工具
查看>>
如何学习区块链
查看>>
搜索问题的办法
查看>>
微信分销系统商城营销5大重点
查看>>
求职准备 - 收藏集 - 掘金
查看>>
htm5新特性(转)
查看>>
Linux-Centos启动流程
查看>>
php 设计模式
查看>>
后端技术精选 - 收藏集 - 掘金
查看>>
Laravel 服务容器
查看>>
mac安装kubernetes并运行echoserver
查看>>
多页架构的前后端分离方案(webpack+express)
查看>>
算法(第4版) Chapter 1
查看>>
前端技术选型的遗憾和经验教训
查看>>
“亲切照料”下的领域驱动设计
查看>>
SRE工程师到底是做什么的?
查看>>
解读:Red Hat为什么收购Ansible
查看>>
Ossim下的安全合规管理
查看>>
DelphiWebMVC框架下BPL热部署实现
查看>>
C++与MySQL的冲突
查看>>