spring9——AOP之AspectJ对AOP的实现
从上述的实验中可以看出BeanNameAutoProxyCreator对于AOP的实现已经和完美了,但是还有两点不足之处:
1,对于切面的实现比较麻烦,既不同类型的通知切面要实现不同的接口,而且一个切面只有一个方法。
2,对于切入点的实现也不是很完美,既通知实现的切面对象的方法对于目标对象方法的精确织入要使用不同的顾问进行包装实现。
在以上的试验中,我们不难看出,其实对于切面的的实现就是切面方法向目标对象方法织入的过程。
那么提出两个问题:
1,可不可以让一个类去实现不同类型通知的功能,既一个类中有多个方法,可以让我们指定方法的的通知类型。
2,对于切入点的实现能不能规定一种切入点表达式,可以对目标对象的方法进行切入点的指定,免去使用多个顾问对不同通知包装的麻烦。
针对以上两点,AspectJ框架这个框架给出了很好的解决方案,而AspectJ框架又是属于spring框架的一部分。
我们这里依旧通过实验来说明问题,还是用之前实验的例子。
用于创建目标对象的接口和实现类。
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); } } }
切面的实现类,AspectJ框架允许我们的切面是一个POJO类,这样就避免了spring框架Api的侵染,另外AspectJ框架比之前多出了一个最终通知,就是不管目标方法执行与否,最终通知的方法都会执行,有点类似finally语句快。
public class MyAspect { //前置通知方法 public void myBefore() { System.out.println("执行前置通知方法"); } // 后置通知方法 public void myAfterReturning() { System.out.println("执行后置通知方法"); } // 环绕通知方法 public Object myAround(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("执行环绕通知方法"); Object[] args = pjp.getArgs(); int a = (int)args[0]; int b = (int)args[1]; if(b == 0){ System.err.println("除数不能为0"); return -1; } if(a == 0){ return 0; } return pjp.proceed(); } // 最终通知 public void myAfter() { System.out.println("执行最终通知"); } }
Xml文件
<?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="myAspect" class="com.opensource.service.MyAspect"/> <!-- AOP配置 --> <aop:config> <aop:pointcut expression="execution(* *..service.*.*(..))" id="pointcut1"/> <aop:pointcut expression="execution(* *..ICalculatorService.division(..))" id="pointcut2"/> <aop:aspect ref="myAspect"> <aop:before method="myBefore" pointcut-ref="pointcut1"/> <aop:after-returning method="myAfterReturning" pointcut-ref="pointcut1"/> <aop:around method="myAround" pointcut-ref="pointcut2"/> <aop:after method="myAfter" pointcut-ref="pointcut1"/> </aop:aspect> </aop:config> </beans>
AspectJ框架提供的标签可以让我们指定切面中不同的方法的通知类型,二切入点表达式则允许我们对该切面方法要织入spring容器中的那些目标对象,及对象的那些方法(切入点)。对于切入点表达式的使用这里介绍两种最常用的:
//指定工程中所有包下子包为service的接口和实现类为切入点
<aop:pointcut expression="execution(* *..service.*.*(..))" id="pointcut1"/>
//指定工程中所有包下接口名为ICalculatorService及其实现类的division方法为切入点 <aop:pointcut expression="execution(* *..ICalculatorService.division(..))" id="pointcut2"/>
你若是想对工程中某个具体的接口或者实现类的方法进行指定就要采用如下做法
这里注意*号后的空格不能少
<aop:pointcut expression="execution(* com.opensource.service.ICalculatorService.division(..))" id="pointcut2"/>
另外切入点表达式同样支持模糊匹配的方式。
测试类:
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, 2); System.out.println("两数相除商为:"+division); System.err.println("---------------------------------------------------------"); int add = bean.add(0, 2); System.out.println("两数想加和为:"+add); System.err.println("---------------------------------------------------------"); bean1.comparator(0, 1); } }
实验结果:
AspectJ框架对于Aop的实现配置比较简单,实现上也很灵活,还支持注解式开发,本文用到的式配置式开发,另外还有对于切面方法参数的配置,还有很多点。
最后说一点,我们作为程序员,研究问题还是要仔细深入一点的。当你对原理了解的有够透彻,开发起来也就得心应手了,很多开发中的问题和疑惑也就迎刃而解了,而且在面对其他问题的时候也可做到触类旁通。当然在开发中没有太多的时间让你去研究原理,开发中要以实现功能为前提,可等项目上线的后,你有大把的时间或者空余的时间,你大可去刨根问底,深入的去研究一项技术,为觉得这对一名程序员的成长是很重要的事情。