一、引言
Springboot的两大核心IOC和AOP:即控制翻转和面向切面编程,今天学习探讨AOP的作用、原理和使用。
二、什么是AOP
1.在不修改源码前提下,扩展功能。从而和业务代码剥离,做到解耦。
2.AOP在Spring框架中应用广泛,包括缓存,事务,定时任务,重试等这一些业务类的注解。
三、AOP的通知类型
1.Before前置通知:在方法执行前执行。
2.AfterReturning返回通知:方法正常结束后执行。
3.After后置通知:方法执行后执行,不管是(异常或者非异常都会执行)。
4.AfterThrowing异常通知:方法执行中有异常抛出。
5.Around环绕通知:在方法执行前和执行后都执行。
@Aspect 表明是一个切面类
@Component 将当前类注入到Spring容器内
@Pointcut 切入点,其中execution用于使用切面的连接点。使用方法:execution(方法修饰符(可选) 返回类型 方法名 参数 异常 模式(可选)) ,可以使用通配符匹配字符,*可以匹配任意字符。
@Before 在方法前执行
@After 在方法后执行
@AfterReturning 在方法执行后返回一个结果后执行
@AfterThrowing 在方法执行过程中抛出异常的时候执行
@Around 环绕通知,就是可以在执行前后都使用,这个方法参数必须为ProceedingJoinPoint,proceed()方法就是被切面的方法,上面四个方法可以使用JoinPoint,JoinPoint包含了类名,被切面的方法名,参数等信息。
package com.zte.zyl.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;
import org.aspectj.lang.annotation.*;
@Aspect
@Component
public class LogAspect {
@Pointcut("execution(public * com.zte.zyl.*.*(..))")
public void LogAspect(){}
@Before("LogAspect()")
public void doBefore(JoinPoint joinPoint){
System.out.println("doBefore");
}
@After("LogAspect()")
public void doAfter(JoinPoint joinPoint){
System.out.println("doAfter");
}
@AfterReturning("LogAspect()")
public void doAfterReturning(JoinPoint joinPoint){
System.out.println("doAfterReturning");
}
@AfterThrowing("LogAspect()")
public void deAfterThrowing(JoinPoint joinPoint){
System.out.println("deAfterThrowing");
}
@Around("LogAspect()")
public Object deAround(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("deAround");
return joinPoint.proceed();
}
}
四、AOP如何实现代理
1.基于接口和基于继承的方式;基于接口就是基于JDK动态代理的方式,使用的是反射机制;基于继承就是使用Cglib来创建目标类的子类,覆盖父类中的方法。
2.基于接口的动态代理,即JDK动态代理方式
1)声明一个共用的接口
//接口
public interface Play {
void play(String e);
}
2)真正的对象
package com.zte.zyl.proxy;
public class RealPlayer implements Play {
@Override
public void play(String e) {
System.out.println("play:"+e);
}
}
3)代理类,要实现同样的接口;以及调用范例。
package com.zte.zyl.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkProxyPlayerSubject implements InvocationHandler {
private RealPlayer realPlayer;
public JdkProxyPlayerSubject(RealPlayer realPlayer) {
this.realPlayer = realPlayer;
}
/*
*invoke方法方法参数解析
*Object proxy:指被代理的对象。
*Method method:要调用的方法
*Object[] args:方法调用时所需要的参数
*InvocationHandler接口的子类可以看成代理的最终操作类。
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Object result = null;
try{
//利用反射动态的来反射方法,这就是动态代理和静态代理的区别
result = method.invoke(realPlayer,args);
}catch (Exception e){
System.out.println("ex:"+e.getMessage());
throw e;
}finally {
System.out.println("after");
}
return result;
}
public static void main(String[] args) {
/*
*newProxyInstance方法参数解析
*ClassLoader loader:类加载器
*Class>[] interfaces:得到全部的接口
*InvocationHandler h:得到InvocationHandler接口的子类实例
*/
Play play = (Play) Proxy.newProxyInstance(JdkProxyPlayerSubject.class.getClassLoader(),new Class[]{Play.class},new JdkProxyPlayerSubject(new RealPlayer()));
play.play("hello");
}
}
2. CGLIB动态代理;主要是基于cglib底层的字节码技术来生成代理类。原理是生成目标类的子类作为代理类。
1)一个真实对象
public class Subject {
public void show(String x){
System.out.println(x);
}
}
2.代理类,要实现MethodInterceptor接口
public class SubjectCglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object o1 = methodProxy.invokeSuper(o, objects);
return o1;
}
}
3)使用
public class CglibProxy {
public static void main(String[] args) {
Subject subject = (Subject)Enhancer.create(Subject.class,new SubjectCglibProxy());
subject.show("hello");
}
}
三、JDK 和 CGLib动态代理区别
1、JDK动态代理具体实现原理:
通过实现InvocationHandlet接口创建自己的调用处理器;
通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理;
通过反射机制获取动态代理类的构造函数,其唯一参数类型就是调用处理器接口类型;
通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数参入;
JDK动态代理是面向接口的代理模式,如果被代理目标没有接口那么Spring也无能为力,Spring通过Java的反射机制生产被代理接口的新的匿名实现类,重写了其中AOP的增强方法。
2、CGLib动态代理:
CGLib是一个强大、高性能的Code生产类库,可以实现运行期动态扩展java类,Spring在运行期间通过 CGlib继承要被动态代理的类,重写父类的方法,实现AOP面向切面编程呢。
3、两者对比:
JDK动态代理是面向接口的。
CGLib动态代理是通过字节码底层继承要代理类来实现(如果被代理类被final关键字所修饰,那么抱歉会失败)。
4、使用注意:
如果要被代理的对象是个实现类,那么Spring会使用JDK动态代理来完成操作(Spirng默认采用JDK动态代理实现机制);
如果要被代理的对象不是个实现类那么,Spring会强制使用CGLib来实现动态代理。
————————————————
参考:https://blog.csdn.net/xlgen157387/article/details/82497594
有兴趣有时间的话可以参考下面链接,做源码AOP的详细阅读。
Spring AOP相关的源码解析:https://blog.csdn.net/woshilijiuyi/article/details/83448407
五、AOP应用场景
1.日志记录
2.性能统计
1)统计方法执行时间
@Around("LogAspect()")
public Object doAround(ProceedingJoinPoint pjp) {
long startTime = System.currentTimeMillis();
Object obj = null;
try {
obj = pjp.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
MethodSignature signature = (MethodSignature) pjp.getSignature();
String methodName = signature.getDeclaringTypeName() + "." + signature.getName();
System.out.println(methodName + "方法执行了" + (endTime - startTime) + "ms");
return obj;
}
3.安全控制
4.事务管理
5.异常处理
六、AOP未来使用趋势