Spring AOP 及动态代理和静态代理区别

文章目录

  • 实现
    • 应用场景
    • 实现
    • 在运行期的代码中生成二进制字节码
  • 原理
    • 静态代理
  • JDK 动态代理
  • CGLib动态代理
    • jdk和cglib动态代理实现的区别
    • AOP各种实现机制比较

AOP是Aspect Oriented Programing的简称,面向切面编程。AOP适合于那些具有横切逻辑的应用:如性能监测,访问控制,事务管理、缓存、对象池管理以及日志记录。AOP将这些分散在各个业务逻辑中的代码通过横向切割的方式抽取到一个独立的模块中。AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP 代理则可分为静态代理和动态代理两大类,其中 静态代理是指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;而动态代理则在运行时借助于 JDK 动态代理、CGLIB 等在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强

代理对象的方法 = 增强处理 + 被代理对象的方法

实现

应用场景

Spring AOP 及动态代理和静态代理区别_第1张图片

实现

Spring AOP 及动态代理和静态代理区别_第2张图片

  • Joinpoint:拦截点,如某个业务方法
  • Pointcut:Joinpoint的表达式,表示拦截哪些方法。一个Pointcut对应多个Joinpoint
  • Advice:要切入的逻辑
  • Before Advice:在方法前切入
  • After Advice:在方法后切入,抛出异常则不会切入
  • After Returning Advice:在方法返回后切入,抛出异常则不会切入
  • After Throwing Advice:在方法抛出异常时切入
  • Around Advice:在方法执行前后切入,可以中断或忽略原有流程的执行
    Spring AOP 及动态代理和静态代理区别_第3张图片
    比如数据库事务,这个数据库事务代码贯穿了我们的整个代码,我们就可以这个叫做切面。 SpringAOP将切面定义的内容织入到我们的代码中,从而实现前后的控制逻辑。 比如我们常写的拦截器Interceptor,这就是一个切面类
    Spring AOP 及动态代理和静态代理区别_第4张图片

在运行期的代码中生成二进制字节码

由于JVM通过字节码的二进制信息加载类的,那么,如果我们在运行期系统中,遵循Java编译系统组织.class文件的格式和结构,生成相应的二进制数据,然后再把这个二进制数据加载转换成对应的类,这样,就完成了在代码中,动态创建一个类的能力了。
Spring AOP 及动态代理和静态代理区别_第5张图片

原理

静态代理

Spring AOP 及动态代理和静态代理区别_第6张图片
Subject:抽象主题角色,抽象主题类可以是抽象类,也可以是接口,是一个最普通的业务类型定义,无特殊要求。
RealSubject:具体主题角色,也叫被委托角色、被代理角色。是业务逻辑的具体执行者。
Proxy:代理主题角色,也叫委托类、代理类。它把所有抽象主题类定义的方法给具体主题角色实现,并且在具体主题角色处理完毕前后做预处理和善后工作。(最简单的比如打印日志):
Subject
抽象主题,定义主要功能

publicinterface Subject {
    publicvoid operate(); 
}
 
 
/**
 * 具体主题
 */ 
publicclass RealSubject implements Subject{ 
 
   @Override 
   publicvoid operate() { 
        System.out.println("realsubject operatestarted......"); 
   } 
}
 
/**
 * 代理类
 */ 
publicclass Proxy implements Subject{ 
 
   private Subject subject;   
    
   public Proxy(Subject subject) { 
        this.subject = subject; 
   } 
   
   @Override  
   publicvoid operate() { 
   
        System.out.println("before operate......"); 
   
        subject.operate(); 
   
        System.out.println("after operate......"); 
   
   } 
   
}

 
/**
 * 客户端
 */ 
publicclass Client { 
   /**
    * @param args
    */ 
   publicstaticvoid main(String[] args) { 
        Subject subject = new RealSubject(); 
        Proxy proxy = new Proxy(subject); 
        proxy.operate(); 
   } 
}

JDK 动态代理

Spring AOP 及动态代理和静态代理区别_第7张图片

public  interface Subject { 
  abstract  public  void request(); 
}
// 具体角色RealSubject: 
public  class RealSubject implements Subject { 
  public RealSubject() {} 
   
  public  void request() { 
    System.out.println( " From real subject. " ); 
 } 
   
}

代处理器(ProxyHandler)

import java.lang.reflect.Method; 
import java.lang.reflect.InvocationHandler; 
   
public  class DynamicSubject implements InvocationHandler { 
  private Object sub; 
  public DynamicSubject() {} 
   
  public DynamicSubject(Object obj) { 
    sub = obj; 
  } 
   
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
    System.out.println( " before calling "  + method); 
    method.invoke(sub,args); 
   
    System.out.println( " after calling "  + method); 
    return  null ; 
  } 
} 

该代理类的内部属性为Object类,实际使用时通过该类的构造函数DynamicSubject(Object obj)对其赋值;此外,在该类还实现了invoke方法,该方法中的
method.invoke(sub,args);
其实就是调用被代理对象的将要被执行的方法,方法参数sub是实际的被代理对象,args为执 行被代理对象相应操作所需的参数。通过动态代理类,我们可以在调用之前或之后执行一些 相关操作。
// 客户端:

import java.lang.reflect.InvocationHandler; 
import java.lang.reflect.Proxy; 
import java.lang.reflect.Constructor; 
import java.lang.reflect.Method; 
   
public class Client { 
   
  static public void main(String[] args) throws Throwable { 
   RealSubject rs = new RealSubject(); // 在这里指定被代理类 
   InvocationHandler ds = new DynamicSubject(rs); 
   Class cls = rs.getClass(); 
   
   // 以下是一次性生成代理 
   Subject subject = (Subject) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(),ds ); 
   subject.request(); 
  } 
}

通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口 (Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实现了非常灵活的动态代理关系。

CGLib动态代理

Spring AOP 及动态代理和静态代理区别_第8张图片

public class SayHello { 
 public void say(){ 
  System.out.println("hello everyone"); 
 } 
}

该类实现了创建子类的方法与代理的方法。getProxy(SuperClass.class)方法通过入参即父类的字节码,通过扩展父类的class来创建代理对象。intercept()方法拦截所有目标类方法的调用,obj表示目标类的实例,method为目标类方法的反射对象,args为方法的动态入参,proxy为代理类实例。proxy.invokeSuper(obj, args)通过代理类调用父类中的方法。

public class CglibProxy implements MethodInterceptor{ 
 private Enhancer enhancer = new Enhancer(); 
 public Object getProxy(Class clazz){ 
  //设置需要创建子类的类 
  enhancer.setSuperclass(clazz); 
  enhancer.setCallback(this); 
  //通过字节码技术动态创建子类实例 
  return enhancer.create(); 
 } 
 //实现MethodInterceptor接口方法 
 public Object intercept(Object obj, Method method, Object[] args, 
   MethodProxy proxy) throws Throwable { 
  System.out.println("前置代理"); 
  //通过代理类调用父类中的方法 
  Object result = proxy.invokeSuper(obj, args); 
  System.out.println("后置代理"); 
  return result; 
 } 
} 
public class DoCGLib { 
 public static void main(String[] args) { 
  CglibProxy proxy = new CglibProxy(); 
  //通过生成子类的方式创建代理类 
  SayHello proxyImp = (SayHello)proxy.getProxy(SayHello.class); 
  proxyImp.say(); 
 } 
}

jdk和cglib动态代理实现的区别

区别 JDK动态代理 CGLIB动态代理
性能
创建代理时间 长(单例很合适)
final方法 可以代理 无法代理
机制 反射(生成类的过程比较高效) ASM(生成类之后的相关操作比较高效)

AOP各种实现机制比较

类别 机制 原理 优点 缺点
静态AOP 静态织入 在编译期,切面直接以字节码的形式编译到目标字节码文件中 对系统无性能影响 灵活性不够
动态AOP JDK动态代理 在运行期,目标类加载后,为接口动态生成代理类,将切面织入到代理类中 相对于静态AOP更加灵活 切入的关注点需要实现接口。对系统有一点性能影响
动态字节码生成 CGLIB动态代理 在运行期,目标类加载后,动态构建字节码文件生成目标类的子类,将切面逻辑加入到子类中 没有接口也可以织入 扩展类的实例方法为final时,则无法进行织入
自定义类加载器 静态织入 在运行期,目标加载前,将切面逻辑加到目标字节码里 可以对绝大部分类进行织入 代码中如果使用了其他类加载器,则这些类将不会被织入
字节码转换 在运行期,所有类加载器加载字节码前进行拦截 可以对所有类进行织入 灵活性不够

参考:
http://blog.csdn.net/luanlouis/article/details/24589193( Java动态代理机制详解(JDK 和CGLIB,Javassist,ASM))
http://www.cnblogs.com/xiaoxiao7/p/6057724.html(AOP和动态代理)
http://blog.csdn.net/heyutao007/article/details/49738887( Java动态代理的两种实现方法)
http://blog.csdn.net/hintcnuie/article/details/10954631(动态代理proxy与CGLib的区别)
https://my.oschina.net/flashsword/blog/194481(1000行代码读懂Spring(二)- 在Spring中实现AOP)

你可能感兴趣的:(Spring,面试,spring,AOP)