Contents
  1. 1. 参考示例源代码:
  2. 2. JDK动态代理模式 VS CGLIB
  3. 3. Spring AOP会根据配置动态选择
  4. 4. 使用
  5. 5. JDK动态代理实现
  6. 6. CGLib实现
  7. 7. 自定义注解的使用
  8. 8. Aspect

参考示例源代码:

https://github.com/huangzhenshi/SpringFreamworkDemo/tree/master

https://zhuanlan.zhihu.com/p/29483023

  • JDK动态代理(运行时编译)
  • CGLib字节码加强(运行时编译)
  • AspectJ(编译时增强的AOP框架)
  • Spring代理模式的运用场景: 操作日志、事务控制、异常处理(AOP ThrowsAdvice)、权限认证、缓存

JDK动态代理模式 VS CGLIB

  • JDK动态代理 反射原理的类依赖其接口,代理接口的实现方法
  • CGLIB,字节码增强技术,根据代理类实现一个增强的子类采用方法拦截的技术拦截所有父类方法的调用,并顺势织入横切逻辑

Spring AOP会根据配置动态选择

proxy-target-class=true,全部都用CGLIB,false则接口类用反射,非接口类用CGLIB

使用

  • Pointcut(切点):指定在哪些类的哪些方法上织入横切逻辑
  • Advice(增强):描述横切逻辑和方法的具体织入点(方法前、方法后、方法的两端等)
  • Advisor(切面):将Pointcut和Advice两者组装起来。有了Advisor的信息,Spring就可以利用JDK或CGLib的动态代理技术采用统一的方式为目标Bean创建织入切面的代理对象了
<aop:config expose-proxy="true" proxy-target-class="true">
<aop:pointcut id="txPointcut" expression="execution(* com.test.service..*.*(..))"/>
<aop:advisor pointcut-ref="txPointcut" advice-ref="txAdvice"/>
</aop:config>

JDK动态代理实现

https://www.cnblogs.com/MOBIN/p/5597215.html
通过实现InvocationHandler接口的invoke方法,来实现代理编程

  1. 被代理类通过反射获取被代理类的接口和ClassLoader
  2. 获取接口代理类
  3. 根据接口代理类获取接口的构造函数
  4. 根据构造函数引入被代理类,最终获取代理类
    //获取动态代理类
    Class proxyClazz = Proxy.getProxyClass(Hello.class.getClassLoader(),Hello.class.getInterfaces());
    //获得代理类的构造函数,并传入参数类型InvocationHandler.class
    Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class);
    //通过构造函数来创建动态代理对象,将自定义的InvocationHandler实例传入
    IHello iHello = (IHello) constructor.newInstance(new HWInvocationHandler(new Hello()));
    //通过代理对象调用目标方法
    iHello.sayHello();

通过实现InvocationHandler接口的invoke方法,来实现代理编程

public class HWInvocationHandler implements InvocationHandler{
//目标对象
private Object target;
public HWInvocationHandler(Object target){
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("------插入前置通知代码-------------");
//执行相应的目标方法
Object rs = method.invoke(target,args);
System.out.println("------插入后置处理代码-------------");
return rs;
}
}

CGLib实现

  • 通过实现MethodInterceptor的intercept方法,来对所有的代理类进行代理操作。
  • 具体涉及到继承的增强类子类Enhancer类。并设置子类的方法调用触发器
    public class CglibProxy implements MethodInterceptor {
    // 被代理对象
    Object targetObject;
    //参数为被代理类,返回结果为代理类
    public Object getProxyObject(Object object) {
    this.targetObject = object;
    //增强器,动态代码生成器
    Enhancer enhancer=new Enhancer();
    //回调方法
    enhancer.setCallback(this);
    //设置生成类的父类类型
    enhancer.setSuperclass(targetObject.getClass());
    //动态生成字节码并返回代理对象
    return enhancer.create();
    }
    // 拦截方法
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    System.out.println("方法调用前");
    // 调用方法
    Object result = methodProxy.invoke(targetObject, args);
    System.out.println("方法调用后");
    return result;
    }
    }

自定义注解的使用

https://www.jianshu.com/p/7dcd59bdbb0a

  • 通过元注解给注解做修饰
  • 在对应的地方使用注解(可以是类上加注解、方法上加注解、属性上加注解)
  • 通过反射获取到目标(类、方法、属性),并调用 *.getAnnotation(),从而获取注解类
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyMessage {
String name() default "sam";
int num() default 0;
String desc();
}
public class AnnotationTest {
@MyMessage(num = 10, desc = "参数a")
private static int a;
@MyMessage(name = "Sam test", desc = "测试方法test")
public void test() {
System.out.println("test");
}
}
public static void main(String[] args) throws Throwable{
//加载annotationTest.class类
Class clazz = Class.forName("com.test.aop.自定义注解.AnnotationTest");
//获取属性
Field[] fields = clazz.getDeclaredFields();
//遍历属性
for (Field field : fields) {
MyMessage myMessage = field.getAnnotation(MyMessage.class);
System.out.println("name:" + myMessage.name() + " num:" + myMessage.num() + " desc:" + myMessage.desc());
}
//获取类中的方法
Method[] methods = clazz.getMethods();
//遍历方法
for (Method method : methods) {
//判断方法是否带有MyMessage注解
if (method.isAnnotationPresent(MyMessage.class)) {
// 获取所有注解 method.getDeclaredAnnotations();
// 获取MyMessage注解
MyMessage myMessage = method.getAnnotation(MyMessage.class);
System.out.println("name:" + myMessage.name() + " num:" + myMessage.num() + " desc:" + myMessage.desc());
}
}
}

Aspect

https://www.cnblogs.com/magicalSam/p/7161942.html
原理:通过自定义注解+Aspect技术实现AOP代理

  • 通过@Aspect注解使该类成为切面类
  • 通过@Pointcut 指定切入点 ,这里指定的切入点为MyLog注解类型,也就是被@MyLog注解修饰的方法,进入该切入点。
  • @Before 前置通知:在某连接点之前执行的通知,但这个通知不能阻止连接点之前的执行流程(除非它抛出一个异常)。
  • @Around 环绕通知:可以实现方法执行前后操作,需要在方法内执行point.proceed(); 并返回结果。
  • @AfterReturning 后置通知:在某连接点正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
  • @AfterThrowing 异常通知:在方法抛出异常退出时执行的通知。
  • @After 后置通知:在某连接点正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回
@Aspect
@Component
public class MyLogAspect {
//切入点
@Pointcut(value = "@annotation(com.test.aop.aspectj.MyLog)")
private void pointcut() {
}
//pointcut() 设置切点,也就是aspect绑定Mylog注解的一个纽带
//@annotation(myLog) 注入当前的mylog,这样才可以正常的mylog.requestUrl了
@Around(value = "pointcut()&& @annotation(myLog)")
public Object around(ProceedingJoinPoint point, MyLog myLog) {
System.out.println("++++执行了around方法++++");
//拦截的类名
Class clazz = point.getTarget().getClass();
//拦截的方法
Method method = ((MethodSignature) point.getSignature()).getMethod();
System.out.println("执行了 类:" + clazz + " 方法:" + method + " 自定义请求地址:"+myLog.requestUrl());
try {
Object result=point.proceed();
System.out.println("++++执行了around方法结束++++");
return result;//执行程序
} catch (Throwable throwable) {
throwable.printStackTrace();
return throwable.getMessage();
}
}
}
Contents
  1. 1. 参考示例源代码:
  2. 2. JDK动态代理模式 VS CGLIB
  3. 3. Spring AOP会根据配置动态选择
  4. 4. 使用
  5. 5. JDK动态代理实现
  6. 6. CGLib实现
  7. 7. 自定义注解的使用
  8. 8. Aspect