Spring AOP 指导教程
什么是Spring AOP
spring aop可以在spring构建的系统中使用面向切面编程。当然Spring Boot也是基于Spring构建的。使用AOP
可以实现诸如事务,日志以及安全校验等通过切面统一完成的任务。他可以通过简单的注解方式实现在方法执行前后来执行你自己的逻辑。
什么是advice, joinpoint和pointcut
- Joinpoint:程序的执行点,如方法的执行或者异常的处理。
- Pointcut:一个用来匹配joinpoint的断言或者表达式。
- Advice:与一个pointcut关联,并在匹配点运行。
advices的类型
- Before advice:在join point之前执行的advice,不能阻止程序的继续运行。
- After returning advice:在join point完成之后执行的advice。
- After throwing advice:在执行的方法抛出异常之后执行。
- After advice:在执行的join point退出之后执行不论正常退出或者抛出了异常。
- Around advice:可以在方法调用之前或之后执行自定义行为,并且还可以选择继续执行join point或者执行另外的方法。
Spring AOP 示例
编写Aspectclass然后写相应的执行方法并在方法上写joint-point表达式
面向切面编程首先需要在类上加@Aspect注解表名这是一个AOP管理的类,然后在方法上加上point-cut表达式用来匹配joint-point方法。
@Aspectpublic class EmployeeCRUDAspect { @Before("execution(* EmployeeManager.getEmployeeById(..))") //point-cut expression public void logBeforeV1(JoinPoint joinPoint) { System.out.println("EmployeeCRUDAspect.logBeforeV1() : " + joinPoint.getSignature().getName()); }}
写由切面控制的方法(joint points)
首先将类注入到Spring中,之后写正常的方法即可。
@Componentpublic class EmployeeManager{ public EmployeeDTO getEmployeeById(Integer employeeId) { System.out.println("Method getEmployeeById() called"); return new EmployeeDTO(); }}
在上面的例子中,logBeforeV1方法会在getEmployeeById方法执行之前执行。将会打印如下日志:
EmployeeCRUDAspect.logBeforeV1() : getEmployeeByIdMethod getEmployeeById() called
AOP注解
@Before
在切面控制的方法之前执行。
@Aspectpublic class LoggingAspect { @Before("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))") public void logBeforeAllMethods(JoinPoint joinPoint) { ... } @Before("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.getEmployeeById(..))") public void logBeforeGetEmployee(JoinPoint joinPoint) { ... }}
logBeforeAllMethods方法会在EmployeeManagerImpl中所有方法执行前执行。
@After
在切面控制的方法之后执行。
@Aspectpublic class LoggingAspect { @After("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))") public void logAfterAllMethods(JoinPoint joinPoint) { ... } @After("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.getEmployeeById(..))") public void logAfterGetEmployee(JoinPoint joinPoint) { ... }}
@Around
可以在方法执行前后切入。
@Aspectpublic class LoggingAspect { @Around("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))") public void logAroundAllMethods(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("****LoggingAspect.logAroundAllMethods() : " + joinPoint.getSignature().getName() + ": Before Method Execution"); try { joinPoint.proceed(); } finally { //Do Something useful, If you have } System.out.println("****LoggingAspect.logAroundAllMethods() : " + joinPoint.getSignature().getName() + ": After Method Execution"); }}
@AfterReturning
在方法执行完成且没有抛出任何异常的情况下。
@Aspectpublic class LoggingAspect { @AfterReturning("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))") public void logAfterReturningAllMethods() throws Throwable { System.out.println("****LoggingAspect.logAfterReturningAllMethods() "); }}
以上示例中只用到了execution方式,还有一种使用@annotation可以对注解了指定接口的方法进行切面编程。这种用法在之前的mybatis多数据源中使用过。
另外还有很多AOP的注解,如果大家感兴趣的话,会继续把剩下的用法写完~