有两种实现AOP的方式:xml配置文件的方式和注解的形式
我们知道通知Advice是指对拦截到的方法做什么事,可以细分为
前置通知:方法执行之前执行的行为。
后置通知:方法执行之后执行的行为。
异常通知:方法抛出异常时的行为。
最终通知:方法执行成功失败都会执行的行为,finally。
环绕通知:前后都要做,如事务
相关依赖:
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.3.7.RELEASE</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2.2</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.8.10</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.10</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>
需要加功能的类
public class UserService { public void saveUser() { System.out.println("save a user..."); //throw new RuntimeException(); } }
要加的功能Advice
public class LogAdvice { //前置通知 public void before() { System.out.println("== before =="); } //最终通知 public void after() { System.out.println("== after =="); } //后置通知 public void afterReturning() { System.out.println("== afterReturning =="); } //异常通知 public void afterThrowing() { System.out.println("== afterThrowing =="); } //环绕通知֪ public void around(ProceedingJoinPoint joinPoint) { try { System.out.println("== around start =="); joinPoint.proceed(); // 执行原方法 System.out.println("== around end =="); } catch (Throwable e) {
//这里是处理还是抛出,情况不一样,涉及到会不会调用afterThrowing System.out.println("== around afterThrowing =="); } } }
配置文件实现AOP功能
<?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd "> <!-- 配置Service对象--> <bean id="userService" class="com.winner.test.UserService"/> <!-- 配置一个通知对象 --> <bean id="logAdvice" class="com.winner.test.LogAdvice"/> <!-- AOP有关的配置都 在aop:config中 --> <aop:config> <!-- 声明一个切面 ref对应的通知对象中有前置通知,后置通知等各种通知,各种通知的method属性的值 就是这个通知对中的方法名 --> <aop:aspect ref="logAdvice"> <!-- 声明切入点 --> <aop:pointcut id="myPointcut" expression="execution(public * *(..))"/> <!-- 指定在某切入点执行某操作 --> <!--指定before通知方法为,logAdvice.before(),引用切入点myPointcut --> <aop:before method="before" pointcut-ref="myPointcut"/> <!--指定before通知方法为,logAdvice.after(),引用切入点myPointcut --> <aop:after method="after" pointcut-ref="myPointcut"/> <!--指定before通知方法为,logAdvice.afterReturning(),引用切入点myPointcut --> <aop:after-returning method="afterReturning" pointcut-ref="myPointcut"/> <!--指定before通知方法为,logAdvice.afterThrowing(),引用切入点myPointcut --> <aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut"/> <!--指定before通知方法为,logAdvice.around(),引用切入点myPointcut --> <aop:around method="around" pointcut-ref="myPointcut" /> </aop:aspect> </aop:config> </beans>
测试
public class MainTest {
@Test
public void testUserService() throws Exception {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) ac.getBean("userService");
System.out.println(userService.getClass());
userService.saveUser();
System.out.println();
}
}
执行结果
没有异常的情况
== before ==
== around start ==
save a user...
== around end ==
== afterReturning ==
== after ==
有异常的情况
== before ==
== around start ==
save a user...
== around afterThrowing ==
== afterReturning ==
== after ==
切入点表达式
格式:
execution(修饰符? 返回值类型 类的全限定名? 方法名(参数))
这其中的问号后缀(?)表示可选的表达式元素。让我们来分析
execution(* com.winner.test.UserService.*(..))
表达式:星号*表示任何返回类型(ret- type-pattern,返回类型模式),后面跟着一个全限定类名(declaring-type-pattern,声明类型模式)。我们在这个类名后 又跟另一个星号*(..),这表示一个任意名称、任意数量(包括零)和任意参数类型的方法。
<!-- 声明切入点 -->
<!-- cn.itcast.oa.service包中所有类的所有public方法 -->
<aop:pointcut expression="execution(public * cn.winner.oa.service.*.*(..))" id="myPointcut" />
<!-- 所模块的service包中所有类的所有方法 -->
<aop:pointcut expression="execution(* cn.winner.oa.*.service.*.*(..))" id="myPointcut" />
<!-- cn.itcast.oa.service的所有子包中所有类的所有方法 -->
<aop:pointcut expression="execution(* cn.winner.oa.service.*.*.*(..))" id="myPointcut" />
<!-- 所有业务层的以save开头的方法 -->
<aop:pointcut expression="execution(* cn.winner.oa.service.*.save*(..))" id="myPointcut" />
<!-- 可以使用 && 与 || 与! 或是 and or not 进行多个表达式的组合 -->
<!-- 所有业务层的以save或delete开头的方法-->
<aop:pointcut expression="execution(* save*(..)) || execution(* delete*(..))" id="myPointcut" />
<!-- 所有业务层的以save或delete开头的方法-->
<aop:pointcut expression="execution(* save*(..)) or execution(* delete*(..))" id="myPointcut" />
<!-- 所有业务层的所有不是查询的方法-->
<aop:pointcut expression="execution(* *(..)) and !execution(* query*(..))" id="myPointcut" />
<!-- 所有业务层的所有不是查询的方法 -->
<aop:pointcut expression="execution(* *(..)) and not execution(* query*(..))" id="myPointcut" />
<aop:pointcut expression="!execution(* query*(..))" id="myPointcut" />
使用注解实现AOP
为了在Spring配置中使用@AspectJ切面,你首先必须启用Spring对@AspectJ切面配置的支持,并确保自动代理:
<!--启用Spring对@AspectJ的支持 --> <!-- 使用基于注解的方式配置切面,需要有下面这个配置 --> <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
beans.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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd "> <!-- 配置Service对象--> <bean id="userService" class="com.winner.test.UserService"/> <!-- 配置一个通知对象 --> <bean id="logAdvice" class="com.winner.test.LogAdvice"/> <!--启用Spring对@AspectJ的支持 --> <!-- 使用基于注解的方式配置切面,需要有下面这个配置 --> <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy> </beans>
切面:
@Aspect public class LogAdvice { // 声明一个切入点。本方法不能有返回值与参数 @Pointcut("execution(* *(..))") private void myPointcut() { } // 前置通知,在原方法执行之前 @Before("myPointcut()") public void before() { System.out.println("== before =="); } // 最终通知,在原方法执行之后 // 如果原方法有异常,也会执行 @After("myPointcut()") public void after() { System.out.println("== after =="); } // 后置通知,在原方法执行之后 // 如果原方法有异常,则不执行。 // 方法一:@AfterReturning("myPointcut()") // 方法二:@AfterReturning(pointcut = "myPointcut()") // 可以使用returning参数指定返回值的对应的参数名,Spring就会在调用本方法时把返回值传给指定名称的参数 @AfterReturning(pointcut = "myPointcut()", returning = "returnValue") public void afterReturning(Object returnValue) { System.out.println("== afterReturning =="); } // 异常通知,在出现异常之后 // @AfterThrowing("myPointcut()") @AfterThrowing(pointcut = "myPointcut()", throwing = "ex" ) public void afterThrowing(Exception ex) { System.out.println("== afterThrowing =="); } // 环绕通知,在原方法调用的前后执行。 // 在原方法执行出异常后,环绕通知的后面部分不会执行。 // 一定要在执行完原方法后,从本方法中返回结果。 @Around("myPointcut()") public void around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("== 环绕通知(前) =="); Object result = joinPoint.proceed(); // 执行原方法 System.out.println("== 环绕通知(后) =="); } }