Java 代理模式:静态代理、JDK 动态代理和 Cglib 动态代理的区别

代理模式是Java常用的设计模式,代理类通过调用被代理类的相关方法,并对相关方法进行增强。加入一些非业务性代码,比如事务、日志等操作。Spring 的 AOP 就是基于代理模式的思想实现的。

在接触代理模式之前觉得使用代理很麻烦,一个类的方法为什么不直接调用,还要通过增加的代理类,其实,当你只有一个类要实现一些增加的功能时,直接在这个类里增加方法是很简单,当你有几十个,几百个类要增加相同功能时,一个一个的添加就非常繁琐。使用代理:

  • 一是增加功能很方便,直接写在代理类里就可以了;

  • 二是降低了代码的耦合度,更加便于调整和维护,要调整时,只要调整代理类就可以了,其他类的代码都不用动。

静态代理

  • 接口

    public interface Animal {
       public void eat();
    }
  • 实现类(被代理的类)

    public class Dog implements Animal{
       @Override
       public void eat() {
           System.out.println("狗吃肉骨头");
      }
    }
  • 静态代理类

    public class StaticProxy implements Animal{
     //被代理对象
       private Animal dog = new Dog();
       @Override
       public void eat() {
           System.out.println("StaticProxy执行前");
           dog.eat();//被代理对象的方法
           System.out.println("StaticProxy执行后");
      }
    }
  • 测试

    public class Test {
       public static void main(String[] args) {
           Animal dog = new StaticProxy();
           dog.eat();
      }
    }  
    // 运行结果
    StaticProxy执行前
    狗吃肉骨头
    StaticProxy执行后

JDK 动态代理

接口和实现类同上

// JDK 动态代理,实现 InvocationHandler 接口
public class DynamicProxy implements InvocationHandler{
   private Object obj;//被代理对象

   //传入被代理对象
   public Object getProxy(Object objA){
       this.obj = objA;
       //创建代理对象,并关联被代理对象
       Object objProxy = Proxy.newProxyInstance(objA.getClass().getClassLoader(),
                         objA.getClass().getInterfaces(), this);
       return objProxy;
  }
 //使用反射技术,协助调用被代理对象的方法
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       System.out.println("DynamicProxy方法执行前");
       Object objResult = method.invoke(obj, args);
       System.out.println("DynamicProxy方法执行后");
       return objResult;
  }
}
  • 测试

    public class Test {
       public static void main(String[] args) {
           //JDK 动态代理
           Animal dog = new Dog();
           DynamicProxy dp = new DynamicProxy();
           Animal dogProxy = (Animal) dp.getProxy(dog);
           dogProxy.eat();
      }
    }
    // 运行结果
    DynamicProxy方法执行前
    狗吃肉骨头
    DynamicProxy方法执行后

Cglib 动态代理

接口和实现类同上

//cglib 动态代理,实现 MethodInterceptor 接口
public class CglibProxy implements MethodInterceptor{
   //被代理的对象
   private Object obj;
   //传入目标对象
   public CglibProxy(Object objA){
       this.obj = objA;
  }
   public Object getProxy(){
       //创建目标对象的子对象,通过反射对父对象的内容拦截和处理
       Enhancer e = new Enhancer();
       e.setSuperclass(obj.getClass());
       e.setCallback(this);
       //返回代理对象
       return e.create();
  }
   
   @Override
   public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
       System.out.println("CglibProxy方法执行前");
       Object result = methodProxy.invoke(obj, args);
       System.out.println("CglibProxy方法执行后");
       return result;
  }
}
  • 测试

    public class Test {
       public static void main(String[] args) {
           //cglib代理
           Animal dog = new Dog();
           CglibProxy proxy = new CglibProxy(dog);
           Animal dogProxy = (Animal) proxy.getProxy();
           dogProxy.eat();
      }
    }

AOP代理

AOP代理可替代目标对象,AOP代理对象包含了目标对象的全部方法,但AOP代理中的方法与目标对象的方法存在差异:AOP代理里的方法可以在执行目标方法之前、之后插入一些通用处理。

img

思路:创建Dog对象和他的GunDog实现类,创建InvocationHandler的实现类MyInvocationHandler, 在invoke方法中调用目标对象的方法,并且在方法前后加入自定义的Utils类的方法, 创建代理对象工厂类MyProxyFactory,用于生产代理对象。

  • 接口

public interface Dog {
   void info();
   void run();
}
  • 实现接口(被代理类)

public class GunDog implements Dog {
   @Override
   public void info() {
       System.out.println("我是一只猎狗");
  }

   @Override
   public void run() {
       System.out.println("我奔跑迅捷");
  }
}
  • 类aop的前置后置执行方法

public class Util {
   public void method1(){
       System.out.println("----模拟第一个通用方法----");
  }
   public void method2(){
       System.out.println("----模拟第二个通用方法----");
  }
}
  • 代理类

    public class MyInvocationHandler implements InvocationHandler {
       //被代理的对象;
       private Object target;

       public void setTarget(Object object) {
           this.target = object;
      }
       @Override
       public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
           Util u = new Util();
           u.method1();
           //注意这里第一个参数不要放proxy进去,因为我们要为指定的target生成动态代理对象
           //放proxy是新生成的动态代理对象。
           Object result = method.invoke(target, args);
           u.method2();
           return result;
      }
    }
  • 创建代理及利用jdk代理执行类

public class MyProxyFactory {
   public static Object getProxy(Object target){
       //创建一个MyInvocationHandler对象
       MyInvocationHandler handler=new MyInvocationHandler();
       //设置指定的要生成动态代理的target对象
       handler.setTarget(target);
       //创建并返回一个动态代理对象。
       return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),handler);
  }
}
  • 测试

    public class MainTest {
       public static void main(String args[]) throws Exception {
           GunDog dog=new GunDog();
           Dog proxyDog= (Dog) MyProxyFactory.getProxy(dog);
           proxyDog.info();
           proxyDog.run();
      }
    }
    // 结果
    ----模拟第一个通用方法----
    我是一只猎狗
    ----模拟第二个通用方法----
    ----模拟第一个通用方法----
    我奔跑迅捷
    ----模拟第二个通用方法----

四者的特点和区别

  1. 静态代理,要把被代理的对象写在类里面,只能处理一个类,执行效率高,代码的耦合度很高,复用性很差。

  2. JDK 动态代理,是代理类要实现 InvocationHandler 接口,接口里面有个(method.invoke(对象,参数) 方法,它是利用反射执行被代理对象的方法;Java 动态代理通过 Proxy.newProxyInstance() 方法动态的获得代理对象,这个方法有三个参数:(类加载器、接口,InvocationHandler 接口的子类实例);其中有个参数是接口,也就是说,Java 动态代理只能代理实现了接口的类,被代理的类如果没有实现任何接口,则不能实现 JDK 动态代理。

  3. Cglib 动态代理,和 JDK 动态代理通过接口实现不同, Cglib 动态代理通过继承实现,通过生成子类字节码,重写被代理类的方法,在重写的方法中增强功能;因为 Cglib 动态代理要继承被代理的类,所以,被 final 修饰的类或方法不能实现 Cglib 动态代理。

  4. AOP动态代理,AOP代理里的方法可以在执行目标方法之前、之后插入一些通用处理。类似JDK的动态代理,但是它代理的是IOC容器内的方法,不能执行代理容器外的方法

Spring AOP不起作用原因

一、直接在切面类定义切点;

  • AOP切面类里面的方法全部不支持触发切面,否则一个切面函数把自己当做切点就会导致递归层层调用。

  • AOP切面类发出函数调用一律不触发切面,避免两个切面类相互调用迭代请求的情况。

二、被代理对象两个切点方法内部调用;

被Spring的AOP增强的类,在同一个类的内部方法调用时,其被调用方法上的增强通知将不起作用,即Spring的事务传播策略在内部方法调用时将不起作用,不管你希望某个方法需要单独事务,是RequiresNew,还是要嵌套事务,要Nested,等等,统统不起作用。

不仅仅是事务通知,所有用AOP实现的通知,都会受到同样限制。

posted @ 2020-12-16 08:04  guojunpeng-hub  阅读(212)  评论(0)    收藏  举报