前言
注解方式的aop,需要用到几个新的注解
1.@Aspect//表明这是一个切面类 2.@Pointcut("execution(* com.cong.service.AccountServiceImpl.*(..))")//切入点表达式 private void accountPointcut(){} 3.@Before(value = "accountPointcut()")//前置通知注解,accountPointcut()的括号不能去掉 类似的还有AfterReturning,AfterThrowing,Around 4.还有在配置文件中添加一句,告知spring开启注解aop的支持 <!-- 告知spring,开启注解aop的支持 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
1.创建maven工程,添加相关依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.cong</groupId> <artifactId>spring_aop_annotation</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <!-- aop --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.9</version> </dependency> </dependencies> </project>
2.在java目录下创建com.cong.pojo.Account类,通过注解创建对象,注入数据,并且添加到ioc容器中
package com.cong.pojo; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component//注入到ioc容器中 public class Account { @Value("1") private int id; @Value("cong") private String name; @Value("100") private float money; @Override public String toString() { return "Account{" + "id=" + id + ", name='" + name + '\'' + ", money=" + money + '}'; } }
3.在java目录下创建com.cong.service包,并且创建接口和类
package com.cong.service; import com.cong.pojo.Account; public interface AccountService { void saveAccount(Account account); } package com.cong.service; import com.cong.pojo.Account; import org.springframework.stereotype.Service; @Service("accountService") public class AccountServiceImpl implements AccountService { @Override public void saveAccount(Account account) { System.out.println("save account..." + account.toString()); // int i = 1/0; } }
4.在java目录下创建com.cong.utils.MyTransaction通知类
先注释掉环绕通知的注解
package com.cong.utils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; @Component// @Aspect//表明这是一个切面类 public class MyTransaction { @Pointcut("execution(* com.cong.service.AccountServiceImpl.*(..))")//切入点表达式 private void accountPointcut(){} //前置通知 @Before(value = "accountPointcut()")//前置通知注解 public void beforeTransaction(){ System.out.println("前置通知MyTransaction类中的beforeTransaction方法执行了..."); } //后置通知 @AfterReturning("execution(* com.cong.service.AccountServiceImpl.*(..))")//如果没有配置切入点表达式 public void afterReturningTransaction(){ System.out.println("后置通知MyTransaction类中的afterReturningTransaction方法执行了..."); } //异常通知 @AfterThrowing("accountPointcut()") public void afterThrowingTransaction(){ System.out.println("异常通知MyTransaction类中的afterThrowingTransaction方法执行了..."); } //最终通知 @After("accountPointcut()") public void afterTransaction(){ System.out.println("最终通知MyTransaction类中的afterTransaction方法执行了..."); } //环绕通知 //@Around("accountPointcut()") public Object aroundTransaction(ProceedingJoinPoint proceedingJoinPoint){ Object res = null; try { Object args [] = proceedingJoinPoint.getArgs(); beforeTransaction(); proceedingJoinPoint.proceed(args); afterReturningTransaction(); return res; }catch (Throwable throwable){ afterThrowingTransaction(); return new RuntimeException(throwable); }finally { afterTransaction(); } } }
5.在resources目录下创建bean.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"> <!-- 配置spring要扫描的包 --> <context:component-scan base-package="com.cong"></context:component-scan> <!-- 告知spring,开启注解aop的支持 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
6.在test,java目录下创建测试类
import com.cong.pojo.Account; import com.cong.service.AccountService; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class AopAnnotationTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); AccountService accountService = (AccountService)context.getBean("accountService"); Account account = (Account)context.getBean("account"); accountService.saveAccount(account); } }
7.运行结果,与我们想象的有些出入
后置通知和异常 通知的顺序明显不同
这是spring基于注解的aop的确有顺序调用的问题,所以实际开发中的选择应该有一个慎重的考虑
信息: Loading XML bean definitions from class path resource [bean.xml]
前置通知MyTransaction类中的beforeTransaction方法执行了...
save account...Account{id=1, name='cong', money=100.0}
最终通知MyTransaction类中的afterTransaction方法执行了...
后置通知MyTransaction类中的afterReturningTransaction方法执行了...
8.将前置通知,后置通知,异常通知,最终通知的注解注释掉,取消环绕通知的注解
//前置通知 //@Before(value = "accountPointcut()")//前置通知注解 public void beforeTransaction(){ System.out.println("前置通知MyTransaction类中的beforeTransaction方法执行了..."); } //后置通知 //@AfterReturning("execution(* com.cong.service.AccountServiceImpl.*(..))")//如果没有配置切入点表达式 public void afterReturningTransaction(){ System.out.println("后置通知MyTransaction类中的afterReturningTransaction方法执行了..."); } //异常通知 //@AfterThrowing("accountPointcut()") public void afterThrowingTransaction(){ System.out.println("异常通知MyTransaction类中的afterThrowingTransaction方法执行了..."); } //最终通知 //@After("accountPointcut()") public void afterTransaction(){ System.out.println("最终通知MyTransaction类中的afterTransaction方法执行了..."); } //环绕通知 @Around("accountPointcut()") public Object aroundTransaction(ProceedingJoinPoint proceedingJoinPoint){ Object res = null; try { Object args [] = proceedingJoinPoint.getArgs(); beforeTransaction(); proceedingJoinPoint.proceed(args); afterReturningTransaction(); return res; }catch (Throwable throwable){ afterThrowingTransaction(); return new RuntimeException(throwable); }finally { afterTransaction(); } }
9.再执行一遍,这样调用的顺序就没有问题了,毕竟这个全都是我们自己写的代码
信息: Loading XML bean definitions from class path resource [bean.xml] 前置通知MyTransaction类中的beforeTransaction方法执行了... save account...Account{id=1, name='cong', money=100.0} 后置通知MyTransaction类中的afterReturningTransaction方法执行了... 最终通知MyTransaction类中的afterTransaction方法执行了...