实现AOP流程:
Service s = new ServiceImpl(); //创建切入点 Pointcut pc = new JdkRegexpMethodPointcut();//JdkRegexpMethodPointcut是切入点实现类。spring提供7个切入点实现类@1 //创建通知 Advice advice = new SimpleAdvice();//SimpleAdvice实现了通知接口。srping提供5种通知接口@2 //创建通知者 Advisor advisor = new DefaultPointcutAdvisor(pc,advice);//DefaultPointcutAdvisor是通知者实现类。spring提供3个通知者实现类@3 //创建代理 ProxyFactory pf = new ProxyFactory();//spring支持两种代理@4 pf.addAdvisor(advisor); pf.setTarget(s); proxy = (Service)pf.getProxy(); proxy .doSomething();
1、七种PointCut实现
Perl5RegexpMethodPointcut 是一个最基本的正则表达式切入点, 它使用Perl 5正则表达式语法。Perl5RegexpMethodPointcut 依赖Jakarta ORO进行正则表达式匹配。
JdkRegexpMethodPointcut 类,它使用JDK 1.4或更高版本里提供的正则表达式支持。
RegexpMethodPointcutAdvisor , 它也允许我们引用一个通知(记住这里一个通知可以是拦截器,前置通知(before advice),异常通知(throws advice)等类型中的一个)。 在背后,如果使用J2SE 1.4或者以上版本,Spring将使用JdkRegexpMethodPointcut , 在之前版本的虚拟机上,Spring将退回去使用Perl5RegexpMethodPointcut 。 可以通过设置perl5 属性为true来强制使用Perl5RegexpMethodPointcut 。 使用RegexpMethodPointcutAdvisor 可以简化织入,因为一个bean可以同时作为切入点和通知器(advisor),如下所示:
<bean id="settersAndAbsquatulateAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref local="beanNameOfAopAllianceInterceptor"/> </property> <property name="patterns"> <list> <value>.*set.*</value> <value>.*absquatulate</value> </list> </property> </bean>
ControlFlowPointcut在运行时控制流切入点的开销是非常昂贵的,甚至与其它动态切入点比起来也是如此。在Java 1.4里, 它的开销差不多是其它动态切入点的5倍。
StaticMethodMatcherPointcut
因为静态切入点是最常用的,你可能会像下面那样继承StaticMethodMatcherPointcut。这只需要实现一个抽象方法 (虽然也可以覆盖其它方法来定制行为):
class TestStaticPointcut extends StaticMethodMatcherPointcut { public boolean matches(Method m, Class targetClass) { // return true if custom criteria match } }
ComposablePointCut可组合切入点。可通过union()和intersection()等操作组合两个以上的切入点。
DynamicMethodMatcherPointcut流程切入点。
动态切入和静态切入:
public interface Pointcut { ClassFilter getClassFilter(); MethodMatcher getMethodMatcher(); } public interface ClassFilter { boolean matches(Class clazz); } public interface MethodMatcher { boolean matches(Method m, Class targetClass); boolean isRuntime(); boolean matches(Method m, Class targetClass, Object[] args); }
matches(Method, Class) 方法用来测试这个切入点是否匹配目标类的指定方法。 这将在AOP代理被创建的时候进行运算,这样可以避免在每次方法调用的时候都运算。 如果matches(Method, Class) 对于一个给定的方法返回true,并且isRuntime() 也返回true, 那么matches(Method, Class, Object[]) 将在每个方法调用的时候被调用。 这使得切入点在通知将被执行前可以查看传入到方法的参数。
大多数MethodMatcher是静态的,这意味着isRuntime() 方法返回false。 在这种情况下,matches(Method, Class , Object[]) 永远不会被调用。
如果是静态切入,Spring会针对目标上的每一个方法调用一次matches(Method m, Class targetClass),其返回值被缓冲起来方便日后调用该方法时使用。这样,对每个方法的适用性测试只会进行一次,之后调用该方法时不全再调用matches()。
2、五种通知
MethodInterceptor拦截环绕通知
public interface MethodInterceptor extends Interceptor { Object invoke(MethodInvocation invocation) throws Throwable; }
BeforeAdvice 前置通知
public interface MethodBeforeAdvice extends BeforeAdvice { void before(Method m, Object[] args, Object target) throws Throwable; }
AfterReturningAdvice 后置通知
public interface AfterReturningAdvice extends Advice { void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable; }
ThrowsAdvice异常通知
public interface ThrowsAdviceextends AfterAdvice{ afterThrowing([Method, args, target], subclassOfThrowable) }
IntroductionInterceptor 引入通知
public interface IntroductionInterceptor extends MethodInterceptor { boolean implementsInterface(Class intf); }
调用的方法位于一个已经被引入接口里,这个引入拦截器将负责完成对这个方法的调用--因为它不能调用proceed() 方法。引入通知不能和任何切入点一起使用,因为它是应用在类级别而不是方法级别。 你只能通过
IntroductionAdvisor来使用引入通知,这个接口包括下面的方法:
public interface IntroductionAdvisor extends Advisor, IntroductionInfo { ClassFilter getClassFilter(); void validateInterfaces() throws IllegalArgumentException; } public interface IntroductionInfo { Class[] getInterfaces(); }
这里没有MethodMatcher 接口,因此也就没有 Pointcut 与引入通知相关联。这里只进行类过滤。
getInterfaces() 方法返回这个通知器所引入的接口。
validateInterfaces() 方法将被内部使用来查看被引入的接口是否能够由配置的IntroductionInterceptor 来实现。
3、三个通知者类
DefaultPointcutAdvisor
在Spring里,一个Advisor是一个仅仅包含一个通知对象和与之关联的切入点表达式的切面。
除了引入这种特殊形式,任何通知器(advisor)都可以和任何通知一起工作。 org.springframework.aop.support.DefaultPointcutAdvisor 是最常用的通知器类。例如,它可以和 MethodInterceptor ,BeforeAdvice 或者 ThrowsAdvice 一起使用。
在Spring里有可能在同一个AOP代理里混合通知器和通知类型。 例如,你可以在一个代理配置里使用一个拦截环绕通知,一个异常通知和一个前置通知:Spring将负责自动创建所需的拦截器链。
RegexpMethodPointcutAdvisor
StaticMethodMatcherPointcutAdvisor
4.1、三个代理实现类
BeanNameAutoProxyCreator为名字匹配字符串或者通配符的bean自动创建AOP代理。
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"> <value>jdk*,onlyJdk</value> </property> <property name="interceptorNames"> <list> <value>myInterceptor</value> </list> </property> </bean>
DefaultAdvisorAutoProxyCreator
它自动应用当前上下文中适当的通知器,无需在自动代理通知器的bean定义中包括bean的名字。 比起BeanNameAutoProxyCreator ,它提供了同样关于一致性配置的优点而避免了前者的重复性。
使用这个功能将涉及:
Specifying a DefaultAdvisorAutoProxyCreator bean definition.
说明一个 DefaultAdvisorAutoProxyCreator 的bean定义
在同一个或者相关的上下文中说明任意数量的通知器。注意这些必须 是通知器而不仅仅是拦截器或者其它通知。 这点是必要的因为必须有一个切入点被评估,以便检查每个通知候选bean定义的合适性。
DefaultAdvisorAutoProxyCreator 将自动评估包括在每个通知器中的切入点,
AbstractAdvisorAutoProxyCreator
这是DefaultAdvisorAutoProxyCreator的父类。如果在某些情况下框架提供的DefaultAdvisorAutoProxyCreator 不能满足你的需要,你可以通过继承这个类来创建你自己的自动代理创建器。
4.2、两种代理支持
JKD动态代理
java.lang.reflect.Proxy.getProxyClass(loader, interfaces). getConstructor(new Class[] { InvocationHandler.class }). newInstance(new Object[] { handler });CGLIB基于ASM框架生成字节码
1、首次创建CGLIB代理时,CGLIB会询问Spring每个方法应该如何处理。这意味着很多决定在JDK代理中每次invoke()调用时都要进行,但在CGLIB中只需要进行一次。CGLIB可以直接调用未被通知的方法,但JDK动态代理要去判断。
2、对于固定通知链的通知,CGLIB可以优化执行。
3、CGLIB对于被代理的方法,也使用了生成字节码的能力,执行被代理方法效率比JDK代理略微高。