1.什么是AOP?
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发效率。
AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)
举例说明传统方法纵向继承:
2.AOP的专业术语
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。
Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。
Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知,通知分为前置通知,后置通知,异常通知,最终通知,环绕通知。
Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一些方法或Field。
Targer(目标对象):代理的目标对象。
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。
spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
Aspect(切面):是切入点和通知(引介)的结合
3.AOP的底层实现
Spring的AOP通过代理方式来实现切面的拦截,AOP源码中用到了两种动态代理来实现拦截切入功能:JDK动态代理和cglib代理
以下分别是两种代理方式的小demo,帮助理解,在实际应用中不需要这种复杂的代码,直接在文件中配置即可达到效果
接口:UserDao.java
package com.cj.aop.demo1; public interface UserDao { void save(); void update(); void delete(); void find(); }
实现类:UserDaoImpl.java
package com.cj.aop.demo1; public class UserDaoImpl implements UserDao { public void save() { System.out.println("保存。。。"); } public void update() { System.out.println("修改。。。"); } public void delete() { System.out.println("删除。。。"); } public void find() { System.out.println("查询。。。"); } }
如果想在sava()方法之前加上校验,这是就可以使用上面介绍的两种代理方式来实现
1.使用JDK动态代理
代理类:MyJDKProxy.java
package com.cj.aop.demo1; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class MyJDKProxy implements InvocationHandler{ private Object object; public MyJDKProxy(Object object){ this.object = object; } public Object createProxy(){ Object proxy = Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this); return proxy; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if("UserDaoImpl".equals(object.getClass().getSimpleName())&&"save".equals(method.getName())){ System.out.println("校验代码======="); return method.invoke(object,args); } return method.invoke(object,args); } }
测试:SpringTestDemo1.java
package com.cj.aop.demo1; import org.junit.Test; public class SpringTestDemo1 { @Test public void demo1(){ UserDao userDao = new UserDaoImpl(); UserDao proxy = (UserDao) new MyJDKProxy(userDao).createProxy(); proxy.save(); proxy.update(); proxy.delete(); proxy.find(); } }
输出结果:
2.使用cglib动态代理
代理类:MyCglibProxy.java
package com.cj.aop.demo2; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class MyCglibProxy implements MethodInterceptor{ private Object object; public MyCglibProxy(Object object){ this.object = object; } public Object createProxy(){ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(object.getClass()); enhancer.setCallback(this); Object proxy = enhancer.create(); return proxy; } public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { if("save".equals(method.getName())){ System.out.println(object.getClass().getName()); System.out.println(obj.getClass().getName()); System.out.println("校验代码======="); return methodProxy.invokeSuper(obj,args); } return methodProxy.invokeSuper(obj,args); } }
测试类:SpringTestDemo2.java
package com.cj.aop.demo2; import com.cj.aop.demo1.UserDao; import com.cj.aop.demo1.UserDaoImpl; import org.junit.Test; public class SpringTestDemo2 { @Test public void demo1(){ UserDao userDao = new UserDaoImpl(); UserDao proxy = (UserDao) new MyCglibProxy(userDao).createProxy(); proxy.save(); proxy.update(); proxy.delete(); proxy.find(); } }
输出结果:SpringTestDemo2.java
代理知识总结:
1.Spring在运行期,生成动态代理对象,不需要特殊的编译器
2.Spring AOP的底层就是通过JDK动态代理或CGLib动态代理技术为目标类执行横向织入
——若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。
——若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。
3.程序中应优先对接口创建代理,便于程序解耦维护
4.标记为final的方法,不能被代理,因为无法被覆盖
——JDK动态代理,是针对接口生成子类,接口中方法不能使用final修饰
——CGLib是针对目标类生成子类,因此类或方法不能使用final修饰
5.Spring只支持方法连接点,不提供属性连接(只支持方法拦截,不支持属性拦截)。
4.Spring Aop增强类型
Spring按照通知Advice在目标类方法的连接点位置,可以分为5类
——前置通知(org.springframework.aop.MethodBeforeAdvice)
在目标方法执行前实施增强
——后置通知(org.springframework.aop.AfterReturningAdvice)
在目标方法执行后实施增强
——环绕通知(org.aopalliance.intercept.MethodInterceptor)
在目标方法执行前后实施增强
——异常抛出通知(org.springframework.aop.ThrowsAdvice)
在方法抛出异常后实施增强
——引介通知(org.springframework.aop.IntroductionInterceptor)
在目标类中添加一些新的方法和属性
5.Spring Aop切面类型
Advisor:代表一般切面,Advice本身就是一个切面,对目标类所有方法进行拦截
PointcutAdvisor:代表具有切点的切面,可以指定目标拦截类哪些方法
IntroductionAdvisor:代表引介切面,针对引介通知而使用界面(不掌握)
下面列出切点、切面案例,先简单介绍ProxyFactoryBean,ProxyFactoryBean的作用是产生代理对象:
ProxyFactoryBean中常用属性介绍:
——target:代理的目标对象
——proxyInterfaces:代理要实现的接口
如果多个接口可以使用一下格式赋值:
<list>
<value></value>
...
</list>
——proxyTargerClass:是否对类代理而不是接口,设置为true时,使用CGLib代理
——interceptorNames:需要织入目标的Advice
——singleton:返回代理是否为单实例,默认为单例
——optimize:当设置为true时,强制使用CGLib
PointcutAdvisor 切点切面:
——使用普通的Advice作为切面,将对目标类中的所有方法进行拦截,不够灵活,在实际开发中常采用带有切点的切面
——常用PointcutAdvisor实现类
DefaultPointcutAdvisor最常用的切面类型,它可以通过任意Pointcut和Advice组合定义切面
JdkRegexpMethodPointcut构造正则表达式切点(下方采用这种方式举例)
案例1:一般切面拦截且有接口
接口:ProductDao.java
package com.cj.aop.demo3; public interface ProductDao { void save(); void delete(); void update(); void find(); }
实现类:ProductDaoImpl.java
package com.cj.aop.demo3; public class ProductDaoImpl implements ProductDao{ public void save() { System.out.println("保存..."); } public void delete() { System.out.println("删除..."); } public void update() { System.out.println("修改..."); } public void find() { System.out.println("查询..."); } }
前置通知类:MyBeforeAdvice.java
package com.cj.aop.demo3; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class MyBeforeAdvice implements MethodBeforeAdvice{ public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("校验代码========"); } }
配置文件:applicationContext.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--配置目标类--> <bean id="productDao" class="com.cj.aop.demo3.ProductDaoImpl"/> <!--前置通知类型--> <bean id="myBeforeAdvice" class="com.cj.aop.demo3.MyBeforeAdvice"/> <!--Spring的AOP产生代理对象--> <bean id="productDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!--配置目标类--> <property name="target" ref="productDao"/> <!--实现的接口--> <property name="proxyInterfaces" value="com.cj.aop.demo3.ProductDao"/> <!--采用拦截的名称--> <property name="interceptorNames" value="myBeforeAdvice"/> </bean> </beans>
测试:
package com.cj.aop.demo3; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import javax.annotation.Resource; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class SpringTestDemo3 {
//一定要注入代理对象 @Resource(name = "productDaoProxy") private ProductDao productDao; @Test public void demo1(){ productDao.save(); productDao.delete(); productDao.update(); productDao.find(); } }
输出结果:
案例2:有切点的切面拦截并且没有接口
目标类:CustomerDao.java
package com.cj.aop.demo4; public class CustomerDao { public void find(){ System.out.println("查询..."); } public void save(){ System.out.println("保存..."); } public void update(){ System.out.println("修改..."); } public void delete(){ System.out.println("删除"); } }
环绕通知类:MyAroundAdvice.java
package com.cj.aop.demo4; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class MyAroundAdvice implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("环绕前增强========"); Object obj = invocation.proceed(); System.out.println("环绕后增强========"); return obj; } }
配置文件:applicationContext2.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--配置目标类--> <bean id="customerDao" class="com.cj.aop.demo4.CustomerDao"/> <!--配置通知--> <bean id="myAroundAdvice" class="com.cj.aop.demo4.MyAroundAdvice"/> <!--要对目标类中的某个方法进行增强,需要配置一个带有切入点的切面--> <bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!--pattern中配置正则表达式, .任意字符 *任意次数 pattern单个,patterns多个用","隔开--> <!--<property name="pattern" value=".*save.*"/>--> <property name="patterns" value=".*save.*,.*delete.*"/> <property name="advice" ref="myAroundAdvice"/> </bean> <!--配置代理对象--> <bean id="customerDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="customerDao"/> <property name="proxyTargetClass" value="true"/> <property name="interceptorNames" value="myAdvisor"/> <!--<property name="interceptorNames" value="myAroundAdvice"/>--> </bean> </beans>
测试:
package com.cj.aop.demo4; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import javax.annotation.Resource; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext2.xml") public class SpringTestDemo4 { @Resource(name = "customerDaoProxy") private CustomerDao customerDao; @Test public void demo1(){ customerDao.find(); customerDao.save(); customerDao.update(); customerDao.delete(); } }
输出结果:
自动创建代理
前面案例中,每个代理对象都是通过ProxyFactoryBean织入切面代理,在实际开发中,非常多的Bean每个都配置ProxyFactoryBean开发维护量巨大
解决方案:自动创建代理
——BeanNameAutoProxyCreator根据Bean名称创建代理
——DefaultAdvisorAutoProxyCreator根据Advisor本身包含信息创建代理
——AnnotationAwareAspectJAutoProxyCreator基于Bean中的AspectJ注解进行自动代理
举例上面自动代理的前两种方式:
案例1:BeanNameAutoProxyCreator根据Bean名称创建代理
接口和实现类:ProductDao.java、ProductDaoImpl.java、CustomerDao.java
package com.cj.aop.demo5; public interface ProductDao { void save(); void delete(); void update(); void find(); }
package com.cj.aop.demo5; public class ProductDaoImpl implements ProductDao { public void save() { System.out.println("保存..."); } public void delete() { System.out.println("删除..."); } public void update() { System.out.println("修改..."); } public void find() { System.out.println("查询..."); } }
package com.cj.aop.demo5; public class CustomerDao { public void find(){ System.out.println("查询..."); } public void save(){ System.out.println("保存..."); } public void update(){ System.out.println("修改..."); } public void delete(){ System.out.println("删除"); } }
通知类:MyBeforeAdvice.java、MyAroundAdvice.java
package com.cj.aop.demo5; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class MyBeforeAdvice implements MethodBeforeAdvice{ public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("校验代码========"); } }
package com.cj.aop.demo5; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class MyAroundAdvice implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("环绕前增强========"); Object obj = invocation.proceed(); System.out.println("环绕后增强========"); return obj; } }
配置文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--配置目标类--> <bean id="productDao" class="com.cj.aop.demo5.ProductDaoImpl"/> <bean id="customerDao" class="com.cj.aop.demo5.CustomerDao"/> <!--配置通知--> <bean id="myBeforeAdvice" class="com.cj.aop.demo5.MyBeforeAdvice"/> <bean id="myAroundAdvice" class="com.cj.aop.demo5.MyAroundAdvice"/> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames" value="*Dao"/> <property name="interceptorNames" value="myBeforeAdvice"/> </bean> </beans>
测试类:
package com.cj.aop.demo5; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext3.xml") public class SpringTestDemo5 { @Autowired private ProductDao productDao; @Autowired private CustomerDao customerDao; @Test public void demo1(){ productDao.find(); productDao.save(); productDao.update(); productDao.delete(); System.out.println("============================="); customerDao.find(); customerDao.save(); customerDao.update(); customerDao.delete(); } }
输出结果:
案例2:DefaultAdvisorAutoProxyCreator根据Advisor本身包含信息创建代理
只有配置文件与上方案例1不同:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--配置目标类--> <bean id="productDao" class="com.cj.aop.demo6.ProductDaoImpl"/> <bean id="customerDao" class="com.cj.aop.demo6.CustomerDao"/> <!--配置通知--> <bean id="myBeforeAdvice" class="com.cj.aop.demo6.MyBeforeAdvice"/> <bean id="myAroundAdvice" class="com.cj.aop.demo6.MyAroundAdvice"/> <!--配置切面--> <bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="pattern" value="com.cj.aop.demo6.ProductDaoImpl.save"/> <property name="advice" ref="myAroundAdvice"/> </bean> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/> </beans>
输出结果:
6.Spring基于AspectJ的AOP开发
以上是两种传统的springaop方式,但是很少使用,在实际开发中我们使用AspectJ实现AOP,这种方式会使代码更加简单灵活,AspectJ实现AOP分为注解方式和XML方式,下面依次介绍
1.AspectJ简介
——AspectJ是一个基于Java语言的AOP框架
——Spring2.0以后新增了对AspectJ切点表达式的支持
——@AspectJ是AspectJ1.5新增功能,通过JDK注解技术,允许直接在Bean类中定义切面
——新版本Spring框架,建议使用AspectJ方式来开发AOP
2.基于AspectJ的注解AOP方式,@AspectJ提供了不同的通知类型
——@Before前置通知,相当于BeforeAdvice
——@AfterReturning后置通知,相当于AfterReturningAdvice
——@Around环绕通知,相当于MethodInterceptor
——@AfterThrowing,相当于ThrowAdvice
——@After最终final通知,不管是否异常,该通知都会执行
——@DeclareParents引介通知,相当于IntroductionInterceptor(不介绍)
3.在通知中通过value属性定义切面
——通过execution函数,可以定义切点的方法切入
——语法:
--execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)
——例如:
--匹配所有类public方法:execution(public * *(..))
--匹配指定包下所有类方法:execution(* com.cj.dao.*(..))不包含子包
--execution(* com.cj.dao..*(..)) ..*表示包、子孙包下所有类
--匹配指定类所有方法:execution(* com.cj.service.UserService.*(..))
--匹配实现特定接口所有类方法:execution(* com.cj.dao.GenericDao+.*(..))
--匹配所有save开头的方法:execution(* save*(..))
注解开发示例:
实现类:
package com.cj.aspect.demo1; public class ProductDao { public void save(){ System.out.println("保存商品..."); } public String update(){ System.out.println("修改商品..."); return "hello"; } public void delete(){ System.out.println("删除商品..."); } public void findone(){ System.out.println("发现一个商品..."); // int i=5/0; } public void findall(){ System.out.println("发现全部商品..."); // int i=5/0; } }
配置文件:
<?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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd "> <!--开启AspectJ的注解开发,自动代理--> <aop:aspectj-autoproxy/> <!--目标类--> <bean id="productDao" class="com.cj.aspect.demo1.ProductDao"/> <!--定义切面--> <bean class="com.cj.aspect.demo1.MyAspectAnno"/> </beans>
切面类:
package com.cj.aspect.demo1; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; @Aspect public class MyAspectAnno { @Before(value = "execution(* com.cj.aspect.demo1.ProductDao.save(..))") public void before(JoinPoint joinPoint){ System.out.println("前置通知=========="+joinPoint); } @AfterReturning(value = "execution(* com.cj.aspect.demo1.ProductDao.update(..))",returning = "result") public void afterReturning(Object result){ System.out.println("后置通知=========="+result); } @Around(value = "execution(* com.cj.aspect.demo1.ProductDao.delete(..))") public Object advice(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("环绕前通知=========="); Object obj = joinPoint.proceed(); System.out.println("环绕后通知=========="); return obj; } @AfterThrowing(value = "execution(* com.cj.aspect.demo1.ProductDao.findone(..))") public void afterThrowing(){ System.out.println("异常通知=========="); } @After(value = "execution(* com.cj.aspect.demo1.ProductDao.findall(..))") public void after(){ System.out.println("最后通知=========="); } }
测试类:
package com.cj.aspect.demo1; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class SpringDemo1 { @Autowired private ProductDao productDao; @Test public void demo1(){ productDao.save(); productDao.update(); productDao.delete(); productDao.findone(); productDao.findall(); } }
在测试异常通知时需要把实体类中的int i=5/0放开:
不同的通知类型可能会有不同的参数,@Before前置通知,可以在方法中传入JoinPoint对象,用来获取切点信息,@AfterReturning后置通知,通过returning属性可以定义方法返回值,作为参数,@Around环绕通知,around方法的返回值就是目标代理方法执行返回值,参数ProceedingJoinPoint可以调用拦截目标方法执行,@AfterThrowing异常抛出通知,通过设置throwing属性,可以设置发生异常对象参数,@After最终通知,无论是否出现异常,最终通知总是被执行的
上面就是AspectJ的注解模式AOP,但是如果我想把update、delete、findone都加变成前置,那么可能需要都进行修改所以我们使用@Pointcut来解决这个问题
@Pointcut为切点命名:
1.在每个通知内定义切点,会造成工作量大,不易维护,对于重复的切点,可以使用@Pointcut进行定义
2.切点方法 private void无参数方法,方法名为切点名
3.当通知多个切点时,可以使用||进行连接
切面类替换成下面代码:
package com.cj.aspect.demo1; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; @Aspect public class MyAspectAnno { @Before(value = "myPointcut1()") public void before(JoinPoint joinPoint){ System.out.println("前置通知=========="+joinPoint); } @AfterReturning(value = "myPointcut2()",returning = "result") public void afterReturning(Object result){ System.out.println("后置通知=========="+result); } @Around(value = "myPointcut3()") public Object advice(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("环绕前通知=========="); Object obj = joinPoint.proceed(); System.out.println("环绕后通知=========="); return obj; } @AfterThrowing(value = "myPointcut4()", throwing = "e") public void afterThrowing(Throwable e){ System.out.println("异常通知=========="+e.getMessage()); } @After(value = "myPointcut5()") public void after(){ System.out.println("最后通知=========="); } @Pointcut(value = "execution(* com.cj.aspect.demo1.ProductDao.save(..))") private void myPointcut1(){}; @Pointcut(value = "execution(* com.cj.aspect.demo1.ProductDao.update(..))") private void myPointcut2(){}; @Pointcut(value = "execution(* com.cj.aspect.demo1.ProductDao.delete(..))") private void myPointcut3(){}; @Pointcut(value = "execution(* com.cj.aspect.demo1.ProductDao.findone(..))") private void myPointcut4(){}; @Pointcut(value = "execution(* com.cj.aspect.demo1.ProductDao.findall(..))") private void myPointcut5(){}; }
输出结果和上面是一样的
以下是AspectJ的XML方式的AOP开发示例:
接口和实现类:
package com.cj.aspect.demo2; public interface CustomerDao { void save(); String update(); void delete(); void findone(); void findall(); }
package com.cj.aspect.demo2; public class CustomerDaoImpl implements CustomerDao { @Override public void save() { System.out.println("保存客户..."); } @Override public String update() { System.out.println("修改客户..."); return "Spring"; } @Override public void delete() { System.out.println("删除客户..."); } @Override public void findone() { System.out.println("查询一个客户..."); // int i = 5/0; } @Override public void findall() { System.out.println("查询所有客户..."); // int i = 5/0; } }
切面类:
package com.cj.aspect.demo2; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.runtime.internal.cflowstack.ThreadCounter; public class MyAspectXML { public void before(JoinPoint joinPoint){ System.out.println("XML方式前置通知=========="+joinPoint); } public void afterReturning(Object result){ System.out.println("XML方式后置通知=========="+result); } public Object around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("XML方式前环绕通知=========="); Object obj = joinPoint.proceed(); System.out.println("XML方式后环绕通知=========="); return obj; } public void afterThrowing(Throwable e){ System.out.println("XML方式异常通知=========="+e.getMessage()); } public void after(){ 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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd "> <!--XML的配置方式完成AOP的开发--> <!--配置目标类--> <bean id="customerDao" class="com.cj.aspect.demo2.CustomerDaoImpl"/> <!--配置切面类--> <bean id="myAspectXml" class="com.cj.aspect.demo2.MyAspectXML"/> <!--AOP的相关配置--> <aop:config> <!--配置切入点--> <aop:pointcut id="pointcut1" expression="execution(* com.cj.aspect.demo2.CustomerDao.save(..))"/> <aop:pointcut id="pointcut2" expression="execution(* com.cj.aspect.demo2.CustomerDao.update(..))"/> <aop:pointcut id="pointcut3" expression="execution(* com.cj.aspect.demo2.CustomerDao.delete(..))"/> <aop:pointcut id="pointcut4" expression="execution(* com.cj.aspect.demo2.CustomerDao.findone(..))"/> <aop:pointcut id="pointcut5" expression="execution(* com.cj.aspect.demo2.CustomerDao.findall(..))"/> <!--配置AOP的切面--> <aop:aspect ref="myAspectXml"> <!--前置通知--> <aop:before method="before" pointcut-ref="pointcut1"/> <!--后置通知--> <aop:after-returning method="afterReturning" pointcut-ref="pointcut2" returning="result"/> <!--环绕通知--> <aop:around method="around" pointcut-ref="pointcut3" /> <!--异常通知--> <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="e"/> <!--最终通知--> <aop:after method="after" pointcut-ref="pointcut5" /> </aop:aspect> </aop:config> </beans>
测试类:
package com.cj.aspect.demo2; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext2.xml") public class SpringDemo2 { @Autowired private CustomerDao customerDao; @Test public void demo1(){ customerDao.save(); customerDao.update(); customerDao.delete(); customerDao.findone(); customerDao.findall(); } }
输出结果:如果想测试异常通知需要把实现类总int i=5/0放开
以上就是Spring整合AspectJ实现AOP的代码,在实际开发中基本不会使用传统的方式配置AOP,都是使用AspectJ的这两种方式来实现,而这里两种方式各公司都有用到,所以需要都掌握,注解方式的优势在于开发方便,劣势在于需要修改代码来修改功能,而XML方式只需修改配置文件就可以啦。