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 示例
编写Aspect
class然后写相应的执行方法并在方法上写joint-point
表达式
面向切面编程首先需要在类上加@Aspect
注解表名这是一个AOP
管理的类,然后在方法上加上point-cut
表达式用来匹配joint-point
方法。
@Aspect
public 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
中,之后写正常的方法即可。
@Component
public class EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId) {
System.out.println("Method getEmployeeById() called");
return new EmployeeDTO();
}
}
在上面的例子中,logBeforeV1
方法会在getEmployeeById
方法执行之前执行。将会打印如下日志:
EmployeeCRUDAspect.logBeforeV1() : getEmployeeById
Method getEmployeeById() called
AOP注解
@Before
在切面控制的方法之前执行。
@Aspect
public 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
在切面控制的方法之后执行。
@Aspect
public 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
可以在方法执行前后切入。
@Aspect
public 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
在方法执行完成且没有抛出任何异常的情况下。
@Aspect
public class LoggingAspect {
@AfterReturning("execution(* com.howtodoinjava.app.service.impl.EmployeeManagerImpl.*(..))")
public void logAfterReturningAllMethods() throws Throwable
{
System.out.println("****LoggingAspect.logAfterReturningAllMethods() ");
}
}
以上示例中只用到了execution
方式,还有一种使用@annotation
可以对注解了指定接口的方法进行切面编程。这种用法在之前的mybatis
多数据源中使用过。
另外还有很多AOP的注解,如果大家感兴趣的话,会继续把剩下的用法写完~