![]() ![]() ![]() ![]() ![]() ![]() Spring只支持方法的JoinPoints,也就是Adivices将在方法执行的前后被引用,Spring不支持Field成员的Joinpoints,这是因为Spring的设计哲学中认为,支持Field成员的Joinpoints会破坏对象的封装性。 Advices Advices实现了Aspectj的真正逻辑,根据蒋这些通知织入Targets的时机不同,Spring提供了几种不同的通知,比如,Before、After、Around、Throw。 1.Before /** * 前置通知,在目标方法调用之前执行 * @author Administrator * */ public class BeforeAdvicesTest implements MethodBeforeAdvice{ public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("before execute"); } } <bean id="before" class="com.bhsc.AOP.BeforeAdvicesTest"></bean> <bean id="target" class="com.bhsc.AOP.target.TargetImple"></bean> <bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces" 、 value="com.bhsc.AOP.target.TargetInterface"/> <property name="target" ref="target"/> <property name="interceptorNames"> <list> <value>before</value> </list> </property> </bean> 通知实现了MethodBeforeAdvice,在配置中建立了Advice,Target,还使用了ProxyFactoryBean,这个类会被BeanFactory,ApplicationContext,用来建立代理对象,并配置了实现的接口,在不指定目标方法时,Before Advice会被织入到接口上的所有方法。 Advice的Target并不是动态代理中的代理对象,而是真实的对象,也就是动态代理中传入的真正的类。 拿动态代理来对比下: Object proxy = Proxy.newProxyInstance(Monkey.class.getClassLoader(), Monkey.class.getInterfaces(), new MyInvocationHandler( new Monkey())); 可以看到动态代理中,真正的类是new时候传入的,而Spring则配置在文件中注入了。 2.After 和Before类似 3.Around 如果要在方法执行的前后,加入Advices的逻辑,可以直接通过实现org.aopalliance.intercept.MethodInterceptor接口,与Before和After不同的是,在MethodInterceptor的invoke方法中,我们可以自行决定是否使用它的proceed()方法来执行目标对象的方法,proceed会回传方法执行后的Object结果,所以在invoke结束之前,有机会修改对象,或是回传一个完全不相干的对象。
/** * Around通知,在真正执行某个方法前,会先插入Interceptor,如果有多个,则会一个个执行,然后执行method的方法,将执行流程转给下一个Interceptor,最后一层层返回堆栈,最后离开堆栈返回应用程序本身的流程。 * @author Administrator * */ public class MethodInterceptorTest implements MethodInterceptor{ public Object invoke(MethodInvocation methodInvocation) throws Throwable { System.out.println("execute method:"+methodInvocation.getMethod()); Object result=null; result=methodInvocation.proceed(); return result; } } <!-- around --> <bean id="around" class="com.bhsc.AOP.MethodInterceptorTest"></bean> <bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces" value="com.bhsc.AOP.target.TargetInterface"/> <property name="target" ref="target"/> <property name="interceptorNames"> <list> <value>around</value> </list> </property> </bean> 1.Throw 如果抛出的异常类型和发生的类型相同,那么就会执行通知方法。 前面的Advice,一定程度上也包括切入点,也就是一个类的所有方法,不过这些都是粗粒度的,事实上,我们可以定义更细粒度的,定义需要的方法,那就是PointCut。 PointCutAdvisor PointCut定义了Joinpoint的集合,表示Advice应用的时机(方法)。在Spring中,用PointcutAdvisor表示PointCut和Advisor的结合,就是aspect。PointcutAdvisor是Advisor的子接口: public interface Advisor { Advice getAdvice(); /** * @return whether this advice is associated with a particular target instance */ boolean isPerInstance(); }
public interface PointcutAdvisor extends Advisor { /** * Get the Pointcut that drives this advisor. */ Pointcut getPointcut(); } 分别有NameMatchMethodPointcutAdvisor、RegexpMethodPointcutAdvisor、ControlFlowPointcut 1.NameMatchMethodPointcutAdvisor ,最基本的PointcutAdvisor,可以使用表达式指定Advice应用目标上的方法名称。 <!-- NameMatchMethodPointcutAdvisor,以llo结尾的方法,使用前面定义的before通知,mappedName可以使用集合注入,那就是nammedNames --> <bean id="helloAdvisor" class="org.springframework.aop. support.NameMatchMethodPointcutAdvisor"> <property name="mappedName" value="*llo"/> <property name="advice" ref="before"/> </bean>
<bean id="helloAdvisorProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces" value="com.bhsc.AOP.target.TargetInterface"/> <property name="target" ref="target"/> <property name="interceptorNames"> <list> <value>helloAdvisor</value> </list> </property> </bean>
在target中加入nihao方法: public void nihao(String name){ System.out.println("你好 :"+name); } TargetInterface ti=(TargetInterface)ctx.getBean("helloProxy"); ti=(TargetInterface)ctx.getBean("helloAdvisorProxy"); ti.hello("lisi"); ti.nihao("lisi"); 结果: before execute hello :lisi 你好 :lisi 2.RegexpMethodPointcutAdvisor,使用正则来编写pointcut表达式。 3.ControlFlowPointcut 用来判断某个方法中,是否要求目标对象执行某个方法,在执行时期动态决定是否介入Advice来提供服务,所以这是动态的PointCut功能。 public class Some implements ApplicationContextAware{ private TargetInterface target; public void setApplicationContext(ApplicationContext applicationContext)throws BeansException { target=(TargetInterface)applicationContext.getBean(""); } public void helloEverybody(){ target.hello("wangwu"); target.nihao("qw"); } public void hello(){ System.out.println("hello!"); } }
boolean flag=true; if(flag){ some.helloEverybody(); }else{ some.hello(); } PointCut Spring中的PointCut是实现PointCut接口来实现的:
public interface Pointcut { ClassFilter getClassFilter(); MethodMatcher getMethodMatcher(); Pointcut TRUE = TruePointcut.INSTANCE; } TruePointcut是PointCut的简单实现,传回的ClassFilter是ClassFilter.TRUE,传回的MethodMatcher是MethodMatcher.TRUE; ClassFilter决定一个类是否要应用Advice,matches决定传入的类是否符合PointCur的定义。 public interface ClassFilter { boolean matches(Class clazz); ClassFilter TRUE = TrueClassFilter.INSTANCE; } MethodMatcher决定某个方法是否要应用Advice: public interface MethodMatcher { boolean matches(Method method, Class targetClass); boolean isRuntime(); boolean matches(Method method, Class targetClass, Object[] args); MethodMatcher TRUE = TrueMethodMatcher.INSTANCE; }
如果是静态的PointCut,比如前面的NameMatchMethodPointcutAdvisor , RegexpMethodPointcutAdvisor,则执行第一个方法,isRuntime返回FALSE,否则执行第二个方法,isRuntime返回true。 如果有多个PointCut,还可以对它进行交集、并集的操作,通过ComposablePointcut类,可以操作它的intersection、union方法。
Introduction 是一种特殊的Advice,从行为上来看,它不像前面的Before,After等,在方法前后介入服务,而是直接介入整个对象的行为,就好像凭空多了一些可操作的行为,为对象动态加入(Mixin)原先所没有的职责。 <!-- Mixin --> <!-- 原来的 --> <bean id="someService" class="onlyfun.caterpillar.Some" /> <!-- 新加的 --> <bean id="otherIntroduction" class="onlyfun.caterpillar.OtherIntroduction" /> <bean id="introductionAdvisor" class= "org.springframework.aop.support.DefaultIntroductionAdvisor"> <constructor-arg ref="otherIntroduction" /> <constructor-arg value="onlyfun.caterpillar.IOther" />
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces" value="onlyfun.caterpillar.ISome" /> <property name="target" ref="someService" /> <property name="interceptorNames"> <list> <value>introductionAdvisor</value> </list> </property> </bean> ApplicationContext ctx = new ClassPathXmlApplicationContext("Aop.xml"); ISome some=(ISome)ctx.getBean("proxyFactoryBean"); some.doSome(); //看起来像是增加了职责 ((IOther)some).doOther();
结果:
原来对象的职责。。。 增加的职责...
Autoproxing 自动代理可以不用为每一个目标对象手动代理对象,使用自动代理,可以通过Bean名称或是Pointcut对比,自动为符合对比条件的目标对象建立代理对象。 1.BeanNameAutoProxyCreator 看一下前面的配置: <bean id="before" class="com.bhsc.AOP.BeforeAdvicesTest"></bean> <bean id="target" class="com.bhsc.AOP.target.TargetImple"></bean> <bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces" 、 value="com.bhsc.AOP.target.TargetInterface"/> <property name="target" ref="target"/> <property name="interceptorNames"> <list> <value>before</value> </list> </property> </bean> 不仅要定于Advice,target,还要定义一个代理对象,配置接口,target等,如果应用比较大,那么一个个配置是很麻烦的,PointcutAdvisor的定义只是对切入点更准确的定义,并不能解决配置繁琐的问题(NameMatchMethodPointcutAdvisor),不过Spring提供了自动代理,可以更加简单的配置。 比如,我们为Bean取好名称,xxxService,这样,就可以使用BeanNameAutoProxyCreator配置: <!-- 自动代理 --> <bean id="introductionProxyCreator" class="org.springframework. aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="interceptorNames" value="introductionAdvisor" /> <property name="beanNames"> <list> <value>*Service</value> </list> </property> </bean> ISome someService=(ISome)ctx.getBean("someService"); someService.doSome(); ((IOther)someService).doOther(); 以后只要新增
2.DefaultAdvisorAutoProxyCreator:自动搜寻 |
|