AOP是攔截的方法後要作對應的織入,那麼先定義幾種通知:前置通知、後置通知、異常通知、結束通知,代碼以下:java
public interface AopAdvice { public void before(Proxy proxy); public void after(Proxy proxy); public void exception(Proxy proxy); public void end(Proxy proxy); }
接下來看,哪些類裏的哪些方法要被攔截呢?咱們須要定義一個表達式,這麼使用正則表達式,相對來講比較簡單,也是一個通用的規範。只有配到正則的方法,纔會執行對應織入的加強。git
@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定義須要後置加強的方法的正則匹配app
exceptionRegex定義須要進行異常處理加強方法的正則匹配框架
endRegex定義在方法執行完成以後須要額外處理方法的正則匹配dom
order表示排序,有多個aop規則命中時,就須要排序,這裏是升序排列,order越大,該aop越靠後執行ide
AOP實現的本質是動態代理,那麼咱們須要定義一個代理接口工具
public interface Proxy { public void invoke(Proxy proxy) throws Throwable; }
這個接口裏定義了一個方法invoke(Proxy proxy),參數是它本身,那麼什麼這樣定義呢?這裏就要談到一種設計模式「責任鏈」,因爲被cglib代理過的類不能再次被代理,因此這裏才用責任鏈模式來解決。具體來看責任鏈在這裏怎麼運做的。this
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<Proxy> proxyList = new ArrayList<Proxy>(); 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<AspectClassMapping> AOP_BEAN_LIST = new ArrayList<AspectClassMapping>(); public static final Map<Class<?>, Pattern> PATTERN_MAPPING = new HashMap<Class<?>, 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<AspectClassMapping>() { 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://git.oschina.net/lxkm/teaframework
博客:https://my.oschina.net/u/1778239/blog