APO (面向切面编程) 是一种编程思想,它通过将通用的横向关注点(日志、事务、权限控制等)与业务逻辑分离,实现解耦,使得代码更易于维护。核心就是将非核心代码抽取出来,在实际运行时将代码切回业务代码。如何切回?使用cglib动态代理和jdk动态代理实现。
横切关注点
通知(也叫增强)
连接点 (joinpoint)
这是一个纯逻辑概念,指哪些可能被拦截到的点,也就被 通知增强 的各个点
切入点 (pointcut)
定位连接点的方式,可以理解为被选中的连接点。是一个表达式,比如execution(* com.spring.service.impl…(…)), 指定好了切入点,框架才知道在哪里进行增强
切面(aspect)
切入点和增强的结合,是一个类。在各个切入点进行增强,好比切西瓜,形成一个切面
目标 target
被代理的目标对象
代理 proxy
为了对目标对象应用通知而创建的对象,好比中介,目标是房东
织入
是指把通知应用到目标上,生成代理对象的过程,可以在编译时织入,也可以在运行时织入,spring 采用后者,也就是动态代理
org.springframework
spring-context
6.0.6
org.springframework
spring-aspects
6.0.6
org.springframework
spring-test
6.0.6
步骤
补充
public interface Calculator {
public double div(int i, int j);
}
实现类
@Component
public class CalculatorImpl implements Calculator {
@Override
public Double div(int i, int j){
try {
System.out.println("业务方法--正在计算除法");
double r = i / j;
return r;
}catch (Exception e){
System.out.println("业务方法--出现异常");
throw e; // 抛异常
}finally {
// 回收资源
System.out.println("业务方法----finally!");
}
}
}
@Component
@Aspect
public class LogAdvice {
@Before(value = "execution(* com.binbin.service.impl.*.*(..))")
public void atBefore(JoinPoint joinPoint){
// 获取方法名
String methodName = joinPoint.getSignature().getName();
System.out.println("Method Name: " + methodName);
System.out.println("Before-前置增强!");
}
@After(value = "execution(* com.binbin.service.impl.*.*(..))")
public void atAfter(JoinPoint joinPoint){
System.out.println("After-后置增强!");
}
@AfterReturning(value = "execution(* com.binbin.service.impl.*.*(..))",returning="res")
public void atAfterReturning(JoinPoint joinPoint,Object res){
System.out.println("AfterReturning -后置返回增强! 返回结果 res = " + res.toString());
}
@AfterThrowing(value = "execution(* com.binbin.service.impl.*.*(..))",throwing = "e")
public void error(Exception e){
System.out.println("AfterThrowing-异常处理增强!" + e);
}
}
@Configuration
@ComponentScan(basePackages = {"com.binbin.service","com.binbin.advice"})
@EnableAspectJAutoProxy // 开启aop 功能
public class JavaConfig {
}
@SpringJUnitConfig(value = JavaConfig.class) // 由spring 创建ioc 容器,相当于 new ClassPathXmlApplication("xxx.xml") 或者 new ClassPathXmlApplication(xxx.class)
public class SpringTest {
@Autowired
Calculator calculator;
@Test
public void test() {
try {
Double r = calculator.div(1, 1);
}catch (Exception e){
System.out.println();
}
}
}
调用calculator.div(1, 1):
依次触发 Before、AfterReturning、After,无异常
Method Name: div
Before-前置增强!
业务方法--正在计算除法
业务方法----finally!
AfterReturning -后置返回增强! 返回结果 res = 1.0
After-后置增强
调用calculator.div(1, 0):
依次触发 Before、AfterThrowing、After有异常
Method Name: div
Before-前置增强!
业务方法--正在计算除法
业务方法--出现异常
业务方法----finally!
AfterThrowing-异常处理增强!java.lang.ArithmeticException: / by zero
After-后置增强!
@Component
public class MyPointCut{
@Pointcut(value = "execution(* com.binbin.service.impl.*.*(..)) || execution(* com.binbin.pointcut.*.*())")
public void pc1(){}
@Pointcut(value = "execution(* com.binbin.service.impl.*.*(..)) || execution(* com.binbin.pointcut.*.*())")
public void pc2(){}
}
@Component
@Aspect
public class LogAdvice {
@Before("com.binbin.pointcut.MyPointCut.pc1() || com.binbin.pointcut.MyPointCut.pc1()")
public void atBefore(JoinPoint joinPoint){
// 获取方法名
String methodName = joinPoint.getSignature().getName();
int modifier = joinPoint.getSignature().getModifiers();
System.out.println("Modifier: " + Modifier.toString(modifier));
System.out.println("Method Name: " + methodName);
System.out.println("Before-前置增强!");
}
}
@Component
@Aspect
public class MyAroundAdvice {
@Around(value = "com.binbin.pointcut.MyPointCut.pc1() || com.binbin.pointcut.MyPointCut.pc2()")
public Object testAround(ProceedingJoinPoint proceedingJoinPoint){
Object[] args = proceedingJoinPoint.getArgs();// 获取目标方法参数
Object result = null;
try {
System.out.println("开启事务!");
result = proceedingJoinPoint.proceed(args);
System.out.println("提交事务");
}catch(Throwable t){
System.out.println("回滚事务");
throw new RuntimeException(); // 抛异常
}finally {
System.out.println("回收资源");
}
return result;
}
}
@Component
@Aspect
@Order(1)
public class TransitionAdvice {
@Before("com.binbin.pointcut.MyPointCut.pc1() || com.binbin.pointcut.MyPointCut.pc2()")
public void atBefore(JoinPoint joinPoint){
System.out.println("TransitionAdvice-Before-前置增强!");
}
@After(value = "execution(* com.binbin.service.impl.*.*(..))")
public void atAfter(JoinPoint joinPoint){
System.out.println("TransitionAdvice-After-后置增强!");
}
@AfterReturning(value = "execution(* com.binbin.service.impl.*.*(..))",returning="res")
public void atAfterReturning(JoinPoint joinPoint,Object res){
System.out.println("TransitionAdvice-AfterReturning -后置返回增强! 返回结果 res = " + res.toString());
}
@AfterThrowing(value = "execution(* com.binbin.service.impl.*.*(..))",throwing = "e")
public void error(Exception e){
System.out.println("TransitionAdvice-AfterThrowing-异常处理增强!" + e);
}
}
不抛异常时的运行结果:
TransitionAdvice 优先级高,before 最先执行,但是AfterReturning 和 After 最后执行,这好比嵌套的括号
TransitionAdvice-Before-前置增强!
Before-前置增强!
业务方法--正在计算除法
业务方法----finally!
AfterReturning -后置返回增强! 返回结果 res = 1.0
After-后置增强!
TransitionAdvice-AfterReturning -后置返回增强! 返回结果 res = 1.0
TransitionAdvice-After-后置增强!
抛异常时的运行结果:
TransitionAdvice 优先级高,before 最先执行,但是AfterThrowing 和 After 最后执行,这好比嵌套的括号
TransitionAdvice-Before-前置增强!
Before-前置增强!
业务方法--正在计算除法
业务方法--出现异常
业务方法----finally!
AfterThrowing-异常处理增强!java.lang.ArithmeticException: / by zero
After-后置增强!
TransitionAdvice-AfterThrowing-异常处理增强!java.lang.ArithmeticException: / by zero
TransitionAdvice-After-后置增强!