AOP(Aspect Oriented Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面 向对象编程的一种补充和完善,它以通过预编译方式和运行期动态代理方式实现在不修改源代码的情况 下给程序动态统一添加额外功能的一种技术。
动态代理
- 代理:将非核心逻辑剥离出来以后,封装这些非核心逻辑的类、对象、方法。
- 目标:被代理“套用”了非核心逻辑代码的类、对象、方法。
public class CalcProxy {
//构造前注入被代理的对象
private Object target;
public CalcProxy(Object target) {
this.target = target;
}
public Object getProxy() {
//ClassLoader:加载动态生成的代理类的类加载器
ClassLoader classLoader = this.getClass().getClassLoader();
//Interfaces:目标对象实现的所有接口的class对象所组成的数组
Class<?>[] interfaces = target.getClass().getInterfaces();
//invocationHandler:设置代理对象实现目标对象方法的过程,即代理类中如何重写接口中的抽象方法
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
Object obj = null;
try {
System.out.println("[动态代理][before] " + method.getName() + ",参数:" + Arrays.toString(args));
obj = method.invoke(target, args);
System.out.println("[动态代理][after] " + method.getName() + ",结果:" + obj);
} catch (Exception e) {
System.out.println("[动态代理][throw] " + method.getName() + ",异常:" + e);
e.printStackTrace();
} finally {
System.out.println("[动态代理][finally] " + method.getName());
}
return obj;
}
};
return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
}
}
@Test
public void proxyTest() {
CalcProxy calcProxy = new CalcProxy(new CalcImpl());
Calc calc = (Calc) calcProxy.getProxy();
int i = calc.abs(1, 2);
}
//[动态代理][before] abs,参数:[1, 2]
//[动态代理][after] abs,结果:2
//[动态代理][finally] abs
AOP
- 说明:动态代理(InvocationHandler):JDK原生的实现方式,需要被代理的目标类必须实现接口。因 为这个技术要求代理对象和目标对象实现同样的接口(兄弟两个拜把子模式)。 cglib:通过继承被代理的目标类(认干爹模式)实现代理,所以不需要目标类实现接口。 AspectJ:本质上是静态代理,将代理逻辑“织入”被代理的目标类编译得到的字节码文件,所以最 终效果是动态的。weaver就是织入器。Spring只是借用了AspectJ中的注解。
- 引入依赖
<!-- spring-aspects会帮我们传递过来aspectjweaver -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.1</version>
</dependency>
- 编写切面类
@Aspect//标记为切面类
@Component//将此切面类交给spring管理
public class CalcAspect {
@Pointcut("execution(* com.liliu.spring.aop.Calculator.*(..))")
public void pointCut() {
}
//@Before("execution(public int com.liliu.spring.proxy.Calc.abs(int,int))")
//@Before("execution(* com.liliu.spring.proxy.Calc.*(..))")
@Before("pointCut()")//相当于try之前
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println("[AOP][Before] " + joinPoint.getSignature() + ",参数:" + Arrays.toString(joinPoint.getArgs()));
}
@AfterReturning(value = "pointCut()", returning = "result")//相当于try之后
public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
System.out.println("[AOP][AfterReturning] " + joinPoint.getSignature() + ",结果:" + result);
}
@After(value = "pointCut()")//相当于finally
public void afterAdvice(JoinPoint joinPoint) {
System.out.println("[AOP][After] " + joinPoint.getSignature());
}
@AfterThrowing(value = "pointCut()", throwing = "ex")//相当于catch
public void afterReturningAdvice(JoinPoint joinPoint, Throwable ex) {
System.out.println("[AOP][AfterThrowing] " + joinPoint.getSignature() + ",异常:" + ex);
}
@Around(value = "pointCut()")
public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {
Object obj = null;
try {
System.out.println("[AOP][Around][Before] " + proceedingJoinPoint.getSignature() + ",参数:" + Arrays.toString(proceedingJoinPoint.getArgs()));
obj = proceedingJoinPoint.proceed();
System.out.println("[AOP][Around][AfterReturning] " + proceedingJoinPoint.getSignature() + ",结果:" + obj);
} catch (Throwable throwable) {
System.out.println("[AOP][Around][AfterThrowing] " + proceedingJoinPoint.getSignature() + ",异常:" + throwable);
} finally {
System.out.println("[AOP][Around][After] " + proceedingJoinPoint.getSignature());
}
return obj;
}
}
前置通知: 使用@Before注解标识,在被代理的目标方法前执行
返回通知: 使用@AfterReturning注解标识,在被代理的目标方法成功结束后执行(寿终正寝)
异常通知: 使用@AfterThrowing注解标识,在被代理的目标方法异常结束后执行(死于非命)
后置通知: 使用@After注解标识,在被代理的目标方法最终结束后执行(盖棺定论)
环绕通知: 使用@Around注解标识,使用try…catch…finally结构围绕整个被代理的目标方法,包 括上面四种通知对应的所有位置
基于注解的AOP
- 编写切面类
切面类上的注解删掉 - spring配置文件
<context:component-scan base-package="com.liliu.spring.aopxml"></context:component-scan>
<!--AOP的注意事项:切面类和目标类都需要交给IOC容器管理切面类必须通过@Aspect注解标识为一个切面
在Spring的配置文件中设置<aop:aspectj-autoproxy />开启基于注解的AOP-->
<!--<aop:aspectj-autoproxy />-->
<aop:config>
<!--配置切面类-->
<aop:aspect ref="calcAspect">
<aop:pointcut id="point" expression="execution(* com.liliu.spring.aopxml.*(..))"/>
<aop:before method="beforeAdvice" pointcut-ref="point"/>
<aop:after-returning method="afterReturningAdvice" pointcut-ref="point" returning="result"/>
<aop:after method="afterAdvice" pointcut-ref="point"/>
<aop:after-throwing method="afterThrowingAdvice" pointcut-ref="point" throwing="ex"/>
</aop:aspect>
</aop:config>