一、Spring Aop的基本概念
1.Aop的概念
面向切面编程(AOP,Aspect Oriented Programming),通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。主要功能:日志记录,性能统计,安全控制,事务处理,异常处理等。
AOP实现原理是java动态代理,但是jdk的动态代理必须实现接口,所以spring的aop是用cglib这个库实现的,cglis使用里asm这个直接操纵字节码的框架,所以可以做到不使用接口的情况下实现动态代理。
AOP与OOP的区别:
OOP面向对象编程,针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。而AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程的某个步骤或阶段,以获得逻辑过程的中各部分之间低耦合的隔离效果。这两种设计思想在目标上有着本质的差异。
OOP面向名次领域,AOP面向动词领域。
2.Aop的术语
1)连接点(Joinpoint)
程序执行的某个特定位置:如类开始初始化前、类初始化后、类某个方法调用前、调用后、方法抛出异常后。一个类或一段程序代码拥有一些具有边界性质的特定点,这些点中的特定点就称为“连接点”。Spring仅支持方法的连接点,即仅能在方法调用前、方法调用后、方法抛出异常时以及方法调用前后这些程序执行点织入增强。连接点由两个信息确定:第一是用方法表示的程序执行点;第二是用相对点表示的方位。
2)切点(Pointcut)
每个程序类都拥有多个连接点,如一个拥有两个方法的类,这两个方法都是连接点,即连接点是程序类中客观存在的事物。AOP通过“切点”定位特定的连接点。连接点相当于数据库中的记录,而切点相当于查询条件。切点和连接点不是一对一的关系,一个切点可以匹配多个连接点。在Spring中,切点通过org.springframework.aop.Pointcut接口进行描述,它使用类和方法作为连接点的查询条件,Spring AOP的规则解析引擎负责切点所设定的查询条件,找到对应的连接点。其实确切地说,不能称之为查询连接点,因为连接点是方法执行前、执行后等包括方位信息的具体程序执行点,而切点只定位到某个方法上,所以如果希望定位到具体连接点上,还需要提供方位信息。
3)增强(Advice)
增强是织入到目标类连接点上的一段程序代码,在Spring中,增强除用于描述一段程序代码外,还拥有另一个和连接点相关的信息,这便是执行点的方位。结合执行点方位信息和切点信息,我们就可以找到特定的连接点。
4)目标对象(Target)
增强逻辑的织入目标类。如果没有AOP,目标业务类需要自己实现所有逻辑,而在AOP的帮助下,目标业务类只实现那些非横切逻辑的程序逻辑,而性能监视和事务管理等这些横切逻辑则可以使用AOP动态织入到特定的连接点上。
5)引介(Introduction)
引介是一种特殊的增强,它为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过AOP的引介功能,我们可以动态地为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。
6)织入(Weaving)
织入是将增强添加对目标类具体连接点上的过程。AOP像一台织布机,将目标类、增强或引介通过AOP这台织布机天衣无缝地编织到一起。根据不同的实现技术,AOP有三种织入的方式:
a、编译期织入,这要求使用特殊的Java编译器。
b、类装载期织入,这要求使用特殊的类装载器。
c、动态代理织入,在运行期为目标类添加增强生成子类的方式。
Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
7)代理(Proxy)
一个类被AOP织入增强后,就产出了一个结果类,它是融合了原类和增强逻辑的代理类。根据不同的代理方式,代理类既可能是和原类具有相同接口的类,也可能就是原类的子类,所以我们可以采用调用原类相同的方式调用代理类。
8)切面(Aspect)
切面由切点和增强(引介)组成,它既包括了横切逻辑的定义,也包括了连接点的定义,Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。
二、动态代理
目前,Spring Aop中常用JDK和CGLIB两种动态代理技术
1.JDK动态代理
JDK动态代理是java.lang.reflect.* 包提供的方式,他必须借助一个接口才能产生代理对象。因此,对于使用业务接口的类,Spring默认使用JDK动态代理实现Aop。
下面通过一个实例演示使用JDK动态代理实现Spring Aop
目录结构
1.1创建应用
创建一个名为ch4的Web应用,并导入所需JAR包
1.2创建接口及实现类
在ch4的src目录下,创建一个dynamic.jdk 包,在该包中创建接口TestDao和接口实现类,在代理类中对其方法进行增强处理。
TestDao.java
package dynamic.jdk; public interface TestDao { public void save(); public void modify(); public void delete(); }
TestDaoImpl.java
package dynamic.jdk; import org.springframework.stereotype.Repository; @Repository("testDao") public class TestDaoImpl implements TestDao{ public void save() { System.out.println("保存"); } public void modify() { System.out.println("修改"); } public void delete() { System.out.println("删除"); } }
1.3创建切面类
在ch4的src目录下,创建一个aspect包,在该包中创建切面类MyAspect,在该类中可以定义多个通知(增强处理功能方法)
MyAspect.java
package aspect; //切面类,可以定义多个通知,即增强处理方法 public class MyAspect { public void check() { System.out.println("模拟权限控制"); } public void except() { System.out.println("模拟异常处理"); } public void log() { System.out.println("模拟日志目录"); } public void monitor() { System.out.println("性能监测"); } }
1.4创建代理类
在dynamic.jdk包中,创建代理类JDKDynamicProxy。在JDK动态代理中,代理类必须实现java.lang.reflect.InvocationHandler接口,并编写代理方法。在代理方法中,需要通过Proxy实现动态代理。
JDKDynamicProxy.java
package dynamic.jdk; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import aspect.MyAspect; public class JDKDynamicProxy implements InvocationHandler { //声明目标类接口对象(真实对象) private TestDao testDao; /*创建代理方法,建立代理对象和真实对象的代理关系,并返回代理对象*/ public Object createProxy(TestDao testDao) { this.testDao = testDao; //1.类加载器 ClassLoader cld = JDKDynamicProxy.class.getClassLoader(); //2.被代理对象实现的所有接口 Class[] clazz = testDao.getClass().getInterfaces(); //使用代理类进行增强,返回代理后的对象 return Proxy.newProxyInstance(cld, clazz, this); } /** * 代理的逻辑方法,所有动态代理类方法调用,都交给该方法处理 * proxy被代理对象 * method将要被执行的方法信息 * args执行方法时需要的参数 * return代理结果 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //创建一个切面 MyAspect myAspect = new MyAspect(); //前增强 myAspect.check(); myAspect.except(); //在目标类上调用方法,并传入参数,相当于调用testDao里的方法 Object obj = method.invoke(testDao,args); //后增强 myAspect.log(); myAspect.monitor(); return obj; } }
1.5创建测试类
在dynamic.jdk包中,创建测试类JDKDynamicTest。在主方法中创建代理对象和目标对象,然后从代理对象中获取对目标对象增强后的对象,最后调用该方法的添加、修改和删除方法。
JDKDynamicTest.java
package dynamic.jdk; public class JDKDynamicTest { public static void main(String[] args) { //创建代理对象 JDKDynamicProxy jdkProxy = new JDKDynamicProxy(); //创建目标对象 TestDao testDao = new TestDaoImpl(); //从代理对象中获取增强后的目标对象,该对象是一个被代理的对象,他会进入代理的逻辑方法invoke里 TestDao testDaoAdvice = (TestDao)jdkProxy.createProxy(testDao); //执行方法 testDaoAdvice.save(); System.out.println("=============="); testDaoAdvice.modify(); System.out.println("=============="); testDaoAdvice.delete(); } }运行结果
2.CGLIB动态代理
CGLIB(Code Generation Library)是一个高性能开源的代码生成包,采用非常底层的字节码技术,对指定的目标类生成一个子类,并对子类进行增强。在Spring Core包中已经集成了CGLIB所需要的的JAR包,不需要另外导入JAR包。
目录结构
2.1创建目标类
在ch4的src目录下,创建一个dynamic.cglib 包,在该包中创建目标类TestDao该类不需要实现任何接口。
TestDao.java
package dynamic.cglib; public class TestDao { public void save() { System.out.println("保存"); } public void modify() { System.out.println("修改"); } public void delete() { System.out.println("删除"); } }
2.2创建代理类
在dynamic.cglib包中,创建代理类CglibDynamicProxy,该类实现MethodInterceptor接口。
CglibDynamicProxy.java
package dynamic.cglib; import java.lang.reflect.Method; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import aspect.MyAspect; public class CglibDynamicProxy implements MethodInterceptor { /** * 创建代理的方法,生成CGLIB代理对象 * target目标对象,需要增强的对象 * 返回目标对象的CGLIB代理对象 */ public Object createProxy(Object target) { //创建一个动态类对象,即增强对象 Enhancer enhancer = new Enhancer(); //确实需要增强的类,设置其父类 enhancer.setSuperclass(target.getClass()); //确定代理逻辑对象为当前对象,要求当前对象实现MethodInterfaceptor方法 enhancer.setCallback(this); //返回创建的代理对象 return enhancer.create(); } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { // TODO Auto-generated method stub //创建一个切面 MyAspect myAspect = new MyAspect(); //前增强 myAspect.check(); myAspect.except(); //在目标类上调用方法,并传入参数,相当于调用testDao里的方法 Object obj = methodProxy.invokeSuper(proxy,args); //后增强 myAspect.log(); myAspect.monitor(); return obj; } }
2.3创建测试类
在dynamic.cglib 包中,创建测试类CglibDynamicTest。在主方法中创建代理对象和目标对象增强后的对象,最后调用该对象的添加、修改和删除方法。
CglibDynamicTest.java
package dynamic.cglib; public class CglibDynamicTest { public static void main(String[] args) { //创建代理对象 CglibDynamicProxy cdp = new CglibDynamicProxy(); //创建目标对象 TestDao testDao = new TestDao(); //从代理对象中获取增强后的目标对象,该对象是一个被代理的对象,他会进入代理的逻辑方法invoke里 TestDao testDaoAdvice = (TestDao)cdp.createProxy(testDao); //执行方法 testDaoAdvice.save(); System.out.println("=============="); testDaoAdvice.modify(); System.out.println("=============="); testDaoAdvice.delete(); } }
运行结果
三、基于代理类的Aop实现
从上一小节可知,在Spring中默认使用JDK动态代理实现Aop编程。使用org.springframework.aop.framework.ProxyFactoryBean创建代理是Spring Aop实现的基本方式。
1.通知类型
在讲解ProxyFactoryBean之前,先了解一下Spring的通知类型。根据Spring中通知在目标类方法的连接点位置,可以分为6中如下类型:
1.环绕通知(org.aopaliance.intercept.MethodInterceptor)是在目标方法执行前和执行后实施增强,可以用于日志记录、事务处理等功能。
2.前置通知(org.apringframework.aop.MethodBeforeAdvice)
是在目标方法执行前实施增强,可以用于权限管理等功能。
3.后置返回通知(org.springframework.aop.AfterReturningAdvice)是在目标方法成功执行后实施增强,可应用于关闭流、删除临时文件等功能。
4.后置(最终)通知(org.springframework.aop.AfterAdvice)是在目标方法执行后实施增强,与后置返回通知不同的是,不管是否发生异常都要执行该通知,可应用于释放资源。
5.异常通知(org.springframework.aop.ThrowsAdvice)是在方法抛出异常后实施增强,可应用于处理异常、记录日志等功能。
6.引入通知(org.springframework.aop.IntroductionInterceptor)是在目标类中添加一些新的方法和属性,可应用于修改目标类(增强类)。
2.ProxyFactoryBean
ProxyFactoryBean是org.springframework.aop.framework.ProxyFactoryBean接口实现类,FactoryBean负责实例化一个Bean实例,ProxyFactoryBean负责为其他Bean实例创建代理实例。
下面通过一个实现环绕通知的实例演示Spring使用ProxyFactoryBean创建AoP代理的过程。
目录结构
2.1导入相关jar包
在核心jar包基础上,需要在向ch4应用的/WEB-INF/lib目标下导入jar包spring-aop-5.0.2.RELEASE.jar和aopalliance-1.0.jar(aopallication-1.0.jar是AoP联盟提供的规范包)。
2.2创建切面类
由于该类实例实现环绕通知,所以切面类需要实现org.aopaliance.intercept.MethodInterceptor接口。在src目录下,创建一个spring.proxyfactorybean包,并在该包中创建切面类MyAspect
MyAspect.java
package spring.proxyfactorybean; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; /** *切面类 */ public class MyAspect implements MethodInterceptor { @Override public Object invoke(MethodInvocation arg0) throws Throwable { // TODO Auto-generated method stub //增强方法 check(); except(); //执行目标方法 Object obj = arg0.proceed(); //增强方法 log(); monitor(); return obj; } public void check() { System.out.println("模拟权限控制"); } public void except() { System.out.println("模拟异常处理"); } public void log() { System.out.println("模拟日志记录"); } public void monitor() { System.out.println("性能检测"); } }
2.3配置切面并指定助理
切面类需要配置Bean实例,Spring容器才能识别为切面对象。在spring.proxyfactorybean包中,创建配置文件applicationContext.xml,
并在文件中配置切面和指定代理对象。
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="testDao" class="dynamic.jdk.TestDaoImpl" /> <!-- 创建一个切面 --> <bean id="myAspect" class="spring.proxyfactorybean.MyAspect" /> <!-- 使用Spring代理工厂定义一个名为testDaoProxy的代理对象 --> <bean id="testDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 指定代理对象接口 --> <property name="proxyInterfaces" value="dynamic.jdk.TestDao" /> <!-- 指定目标对象 --> <property name="target" ref="testDao" /> <!-- 指定切面,植入环绕通知 --> <property name="interceptorNames" value="myAspect" /> <!-- 指定代理方法,true为指定CGLIB动态代理,默认为false,指定JDK动态代理 --> <property name="proxyTargetClass" value="true" /> </bean> </beans>
2.4创建测试类
在spring.proxyfactorybean包中,创建测试类ProxyFactoryBeanTest,在主方法中使用Spring容器获取代理对象,并执行目标方法。
ProxyFactoryBeanTest.java
package spring.proxyfactorybean; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import dynamic.jdk.TestDao; public class ProxyfactoryBeanTest { public static void main(String[] args) { // TODO Auto-generated method stub ApplicationContext appCon = new ClassPathXmlApplicationContext("/spring/proxyfactorybean/applicationContext.xml"); //从容器中,获取增强后的目标对象 TestDao testDaoAdvice = (TestDao)appCon.getBean("testDaoProxy"); //执行方法 testDaoAdvice.save(); System.out.println("=============="); testDaoAdvice.modify(); System.out.println("=============="); testDaoAdvice.delete(); } }
运行结果
四、基于XML配置开发AspectJ
AspectJ是一个基于Java语言的Aop框架。从Spring2.0 以后引入了AspectJ的支持。目前的Spring框架,建议开发者使用AspectJ实现Spring Aop 。使用AspectJ实现Spring Aop的方式有两种:一种是基于XML配置开发AspectJ,另一种是基于注解开发AspectJ。
基于XML配置开发AspectJ是指通过XML配置文件定义切面、切入点及通知,所有这些定义都必须在<aop:config>元素内。
下面通过一个实例演示基于XML配置开发AspectJ的过程
目录结构
1.导入AspectJ框架相关的jar包
需要再像ch4应用的/WEB-INF/lib目录下导入jar包spring-aspect-5.0.2.RELEASE.jar和aspectjweaver-1.8.13.jar 。spring-aspect-5.0.2.RELEASE.jar是Spring为Aspect提供的实现,Spring的包中已提供。
aspectjweaver-1.8.13.jar是Aspect框架所提供的规范包
2.创建切面类
在ch4应用的src目录下,创建aspectj.xml包,在该包中创建切面类MyAspect,并在类中编写各种类型通知。
MyAspect.java
package aspectj.xml; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; /* * 切面类,在次类中编写各种类型通知 */ public class MyAspect { /** * 前置通知,使用Joinpoint接口作为参数获得目标对象信息 */ public void before(JoinPoint jp) { System.out.println("前置通知:模拟权限控制"); System.out.println(".目标对象:模拟权限控制" + jp.getTarget() + ".被增强处理的方法:" + jp.getSignature().getName()); } /** * 后置返回通知 */ public void afterReturning(JoinPoint jp) { System.out.println("后置返回通知:" + "模拟删除临时文件"); System.out.println(".被增强的处理方法" + jp.getSignature().getName()); } /** * 环绕通知 * ProceedingJoinPoint子接口,代表可以执行的目标方法 * 返回类型必须是Object * 必须一个参数是ProceedingJoinPoint类型 * 必须是throws Throwable */ public Object around(ProceedingJoinPoint pjp) throws Throwable{ //开始 System.out.println("环绕开始:执行目标方法前,模拟开始事务"); //执行当前目标方法 Object obj = pjp.proceed(); //结束 System.out.println("环绕结束:执行目标方法后,模拟关闭事务"); return obj; } /** * 异常通知 */ public void except(Throwable e) { System.out.println("异常通知:" + "程序执行异常" + e.getMessage()); } /** * 后置(最终)通知 */ public void after() { System.out.println("最终通知:模拟释放资源"); } }
3.创建配置文件,并编写相关配置
在aspect.xml包中,创建配置文件applicationContext.xml,并在<aop:config>元素以及其子元素编写相关配置
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" 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"> <!-- 定义目标对象(使用4.2.1节中的实现类) --> <bean id="testDao" class="dynamic.jdk.TestDaoImpl"/> <!-- 定义切面 --> <bean id="myAspect" class="aspectj.xml.MyAspect"/> <!-- AOP配置 --> <aop:config> <!-- 配置切面 --> <aop:aspect ref="myAspect"> <!-- 配置切入点,通知增强哪些方法 --> <aop:pointcut expression="execution(* dynamic.jdk.*.*(..))" id="myPointCut"/> <!-- 将通知与切入点关联 --> <!-- 关联前置通知 --> <aop:before method="before" pointcut-ref="myPointCut"/> <!-- 关联后置返回通知,在目标方法成功执行后执行 --> <aop:after-returning method="afterReturning" pointcut-ref="myPointCut"/> <!-- 关联环绕通知 --> <aop:around method="around" pointcut-ref="myPointCut"/> <!-- 关联异常通知,没有异常发生时将不会执行增强,throwing属性设置通知的第二个参数名称 --> <aop:after-throwing method="except" pointcut-ref="myPointCut" throwing="e"/> <!-- 关联后置(最终),不管目标方法是否成功都要执行 --> <aop:after method="after" pointcut-ref="myPointCut"/> </aop:aspect> </aop:config> </beans>
4.创建测试类
在aspectj.xml包中,创建测试类XMLAspectTest,在主方法中使用Spring容器获取代理对象,并执行目标方法。
XMLAspectTest.java
package aspectj.xml; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import dynamic.jdk.TestDao; public class XMLAspectJTest { public static void main(String[] args) { // TODO Auto-generated method stub ApplicationContext appCon = new ClassPathXmlApplicationContext("/aspectj/xml/applicationContext.xml"); //从容器中获取增强后的目标对象 TestDao testDaoAdvice = (TestDao)appCon.getBean("testDao"); //执行方法 testDaoAdvice.save(); } }
运行结果
五、基于注解开发AspectJ
基于注解的开发AspectJ要比基于XML配置开发AspectJ便捷许多,所以在实际开发中推荐使用注解方式。
@Aspect:切面。表示一个横切进业务的一个对象。它里面包含切入点(Pointcut)和Advice(通知)。
@Pointcut:切入点。表示需要切入的位置,比如某些类或者某些方法,也就是先定一个范围。
@Before:前置通知,Advice(通知)的一种,切入点的方法体执行之前执行。
@Around:环绕通知,Advice(通知)的一种,环绕切入点执行也就是把切入点包裹起来执行。
@After:后置(最终)通知,Advice(通知)的一种,在切入点正常运行结束后执行。
@AfterReturning:后置返回通知,Advice(通知)的一种,在切入点正常运行结束后执行,异常则不执行
@AfterThrowing:异常通知,Advice(通知)的一种,在切入点运行异常时执行。
下面通过一个实例讲解基于注解开发AspectJ的过程
目录结构
1.创建切面类,并进行注解
在ch4应用的src目录下,创建aspectj.annotation包,在该包中创建切面类MyAspect。在该类中,首先使用@Aspect注解定义一个切面类,由于该类在Spring中是作为组件使用的,所以还需要使用@Component注解。然后,使用@Pointcut注解切入点表达式,并通过定义方法来表示切入点名称。最后在每个通知方法上添加相应的注解,并将切入点名称作为参数传递给需要执行增强的通知方法。
MyAspect.java
package aspectj.annotation; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; /** *切面类,在此类中编写各种类型通知 */ @Aspect//对应<aop:aspect ref="MyAspect"> @Component//对应<bean id="myAspect" class="aspectj.xml.MyAspect" /> public class MyAspect { /** * 定义切入点 */ @Pointcut("execution(* dynamic.jdk.*.*(..))") private void myPointCut() { //对应<aop:pointcut expression="execution(* dynamic.jdk.*.*(..))" id="myPointCut" /> } /** * 前置通知,使用Joinpoint接口作为参数获取目标对象信息 */ @Before("myPointCut()")//对应<aop:before method="before" pointcut-ref="myPointCut" /> public void before(JoinPoint jp) { System.out.println("前置通知,模拟权限控制"); System.out.println(",目标类对象:" + jp.getTarget() + ",被增强的处理方法:" + jp.getSignature().getName()); } /* * 后置返回通知 */ @AfterReturning("myPointCut()") public void afterReturning(JoinPoint jp) { System.out.println("后置返回通知,模拟删除临时文件"); System.out.println(",目标类对象:" + jp.getTarget() + ",被增强的处理方法:" + jp.getSignature().getName()); } /** * 环绕通知 * ProceedingJoinPoint子接口,代表可以执行的目标方法 * 返回类型必须是Object * 必须一个参数是ProceedingJoinPoint类型 * 必须是throws Throwable */ @Around("myPointCut()") public Object around(ProceedingJoinPoint pjp) throws Throwable{ //开始 System.out.println("环绕开始:执行目标方法前,模拟开始事务"); //执行当前目标方法 Object obj = pjp.proceed(); //结束 System.out.println("环绕结束:执行目标方法后,模拟关闭事务"); return obj; } /** * 异常通知 */ @AfterThrowing(value="myPointCut()",throwing="e") public void except(Throwable e) { System.out.println("异常通知:" + "程序执行异常" + e.getMessage()); } /** * 后置(最终)通知 */ @After("myPointCut()") public void after() { System.out.println("最终通知:模拟释放资源"); } }
2.注解目标类
使用注解@Reposition将目标类dynamic.jdk.TestDaoImpl注解为目标对象,注解代码如下:
@Repository("testDao")
3.创建配置文件
在aspect.annotation包中,创建配置文件applicationContext.xml,并在配置文件中指定需要扫描的包,使注解生效。同时需要启动基于注解的AspectJ支持。
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" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 指定需要扫描的包,使注解生效 --> <context:component-scan base-package="aspectj.annotation" /> <context:component-scan base-package="dynamic.jdk" /> <!-- 启动基于注解的AspectJ支持 --> <aop:aspectj-autoproxy /> </beans>
4.创建测试类
在aspectj.annotation包中,创建测试类AnnotationAspectTest,在主方法中使用Spring容器获取代理对象,并执行目标方法。
AnnotationAspectTest.java
package aspectj.annotation; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import dynamic.jdk.TestDao; public class AnnotationAspectJTest { public static void main(String[] args) { // TODO Auto-generated method stub ApplicationContext appCon = new ClassPathXmlApplicationContext("/aspectj/annotation/applicationContext.xml"); //从容器中获取增强后的目标对象 TestDao testDaoAdvice = (TestDao)appCon.getBean("testDao"); //执行方法 testDaoAdvice.save(); } }
测试结果