对于上篇博客http://www.cnblogs.com/cdf-opensource-007/p/6464237.html结尾处提到的两个问题,可以使用spring提供的自动代理生成器解决。自动代理生成器可以让我们把切面织入目标对象方法时不用再使用ProxyFactoryBean这个类来生成代理对象了,同时可以把切面织入多个代理代理对象,并且在获取代理对象时使用的是目标对象的id来获取,这符合正常的使用习惯。
spring提供了两个自动代理生成器,一个是DefaultAdvisorAutoProxyCreator,这一款代理生成器比较暴力,会把容器内所有的切面向所有的目标对象织入,而且切面只能是顾问的形式。另一款自动代理生成器是BeanNameAutoProxyCreator,这一款代码生成器就比较友好了,可以让我们有选择性的对目标对象和切面进行织入的操作,而且切面可以是顾问或者通知的形式。
下面我们还是以实验的形式说明问题。分别提供两个接口,和两个实现类,用于目标对象的创建,在上篇博客实验的基础上我们增加一个比较器的接口和实现类。
计算器接口:
public interface ICalculatorService { int add(int a,int b); int division(int a ,int b); }
比较器接口:
public interface IComparatorService { void comparator(int a,int b); }
计算器实现类:
public class CalculatorServiceImpl implements ICalculatorService { @Override public int add(int a, int b) { return a+b; } @Override public int division(int a, int b) { return a/b; } }
比较器实现类:
public class ComparatorServiceImpl implements IComparatorService { @Override public void comparator(int a, int b) { if(a > b){ System.out.println(a+"比"+b+"大"); }else if(a < b){ System.out.println(a+"比"+b+"小"); }else{ System.out.println(a+"等于"+b); } } }
依旧提供三个切面的实现类,分别是前置通知,环绕通知和后置通知。
public class TestMethodBeforeAdive implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("执行前置通知--->"+"正在执行的方法名为"+method.getName()); } }
public class TestMethodInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("执行环绕通知--->"+"正在执行的方法名为"+invocation.getMethod().getName()); Object[] arguments = invocation.getArguments(); int a = (int)arguments[0]; int b = (int)arguments[1]; if(b == 0){ System.err.println("除数不能为0"); return -1; } if(a == 0){ return 0; } return invocation.proceed(); } }
public class TesAfterRunningAdive implements AfterReturningAdvice { @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("执行后置通知--->"+"正在执行的方法名为"+method.getName()); } }
配置文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 目标对象 --> <bean id="comparatorServiceTarget" class="com.opensource.service.impl.ComparatorServiceImpl"/> <bean id="calculatorServiceTarget" class="com.opensource.service.impl.CalculatorServiceImpl"/> <!-- 通知 --> <bean id="methodBeforeAdive" class="com.opensource.service.impl.TestMethodBeforeAdive"/> <bean id="afterRunningAdive" class="com.opensource.service.impl.TesAfterRunningAdive"/> <bean id="methodInterceptor" class="com.opensource.service.impl.TestMethodInterceptor"/> <!-- 顾问 --> <bean id="advisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <property name="advice" ref="methodInterceptor"/> <property name="mappedNames" value="division"/> </bean> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"> <list> <value>calculatorServiceTarget</value> <value>comparatorServiceTarget</value> </list> </property> <property name="interceptorNames"> <list> <value>methodBeforeAdive</value> <value>afterRunningAdive</value> <value>advisor</value> </list> </property> </bean> </beans>
测试类:
public class MyTest { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("spring-bean.xml"); ICalculatorService bean = (ICalculatorService)ac.getBean("calculatorServiceTarget"); IComparatorService bean1 = (IComparatorService)ac.getBean("comparatorServiceTarget"); int division = bean.division(10, 0); System.out.println("两数相除商为:"+division); System.err.println("---------------------------------------------------------"); int add = bean.add(0, 2); System.out.println("两数想加和为:"+add); System.err.println("---------------------------------------------------------"); bean1.comparator(0, 1); } }
实验结果:
这里提一句:对于BeanNameAutoProxyCreator中beanNames和interceptorNames这两个属性可以采用模糊匹配的方式,例如以下这种形式:
<property name="beanNames" value="*Target"/>
还有对顾问包装通知时,一个顾问只能包装一个通知,这也体现了顾问对于通知的精细化管理。
最后说一点,我们作为程序员,研究问题还是要仔细深入一点的。当你对原理了解的有够透彻,开发起来也就得心应手了,很多开发中的问题和疑惑也就迎刃而解了,而且在面对其他问题的时候也可做到触类旁通。当然在开发中没有太多的时间让你去研究原理,开发中要以实现功能为前提,可等项目上线的后,你有大把的时间或者空余的时间,你大可去刨根问底,深入的去研究一项技术,为觉得这对一名程序员的成长是很重要的事情。