代理含义: 使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原方法。

一、静态代理

直接上代码如下

1、接口
  1. public interface ClothFactory {
  2. void produceCloth();
  3. }

2、静态代理类
  1. public class ProxyClothFactory implements ClothFactory{
  2. private ClothFactory clothFactory; // 就拿被代理对象进行实例化
  3. public ProxyClothFactory(ClothFactory clothFactory) {
  4. this.clothFactory = clothFactory;
  5. }
  6. @Override
  7. public void produceCloth() {
  8. System.out.println("代理工厂做一些准备工作");
  9. clothFactory.produceCloth();
  10. System.out.println("代理工厂做一些后续的收尾工作");
  11. }
  12. }

3、被代理对象
  1. public class SupClothFactory implements ClothFactory{
  2. @Override
  3. public void produceCloth() {
  4. System.out.println("sup生产衣服、。。。。。。。。");
  5. }
  6. }

4、静态代理的使用
  1. public class StaticProxyTest {
  2. public static void main(String[] args) {
  3. // 创建被代理对象
  4. SupClothFactory supClothFactory = new SupClothFactory();
  5. // 创建代理类对象
  6. ProxyClothFactory proxyClothFactory = new ProxyClothFactory(supClothFactory);
  7. proxyClothFactory.produceCloth();
  8. }
  9. }

总结

所所谓静态代理即
1、创建一个实现了接口的代理类。
2、通过这个代理类 去创建 被代理的类,并对代理类要执行的方法前后进行增强。

静态代理的特点:代理类和被代理类在编译期间就确定下来了。

二、动态代理

使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原方法。

  • 想要实现动态代理,需要解决的问题:
    • 如何根据加载到内存中的被代理类,动态创建一个代理类及其对象
    • 当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法。
  • 动态代理常用的两种方式

    • java自带的代理方式
    • Cglib

      1、javaProxy

      (1)接口
      1. public interface Human {
      2. String getBelief();
      3. void eat(String food);
      4. }

      (2)动态代理工厂

      ```java public class MyInvocationHandler implements InvocationHandler {

      private Object obj;// 需要使用被代理类的对象进行赋值

      public void bind(Object o) { this.obj = o; }

      // 当我们通过代理类的对象,调用方法A时,就会自动的调用如下方法:invoke(0 // 将被代理类要执行的方法a的功能,声明在invoke()中 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

      // 在原方法执行执行之前加入方法 System.out.println(“method.invoke(obj, args),执行之前”);

      // 代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法 // obj:被代理对象 Object invoke = method.invoke(obj, args);

      // 在原方法执行执行之后加入方法 System.out.println(“method.invoke(obj, args),执行之后”);

      // 上诉方法的返回值就作为当前类中的invoke()的返回值 return invoke; } }

public class ProxyFactory { // 调用此方法,放回一个被代理类对象,被代理类的对象 public static Object getProxyInstance(Object o) { MyInvocationHandler handler = new MyInvocationHandler(); handler.bind(o); // 被代理类的 类加载器、接口、 return Proxy.newProxyInstance(o.getClass().getClassLoader(), o.getClass().getInterfaces(), handler); } }

  1. <a name="Dsskl"></a>
  2. ##### (3)被代理对象
  3. ```java
  4. public class Superman implements Human {
  5. @Override
  6. public String getBelief() {
  7. return "I believe I can fly";
  8. }
  9. @Override
  10. public void eat(String food) {
  11. System.out.println("I like eating " + food);
  12. }
  13. }

(5)动态代理的使用
  1. /**
  2. * @description: 动态代理
  3. * 想要实现动态代理,需要解决的问题:
  4. * 1、如何根据加载到内存中的被代理类,动态创建一个代理类及其对象
  5. * 2、当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法。
  6. * @Author: wangchao
  7. * @Date: 2021/7/18
  8. */
  9. public class DynamicProxyTest {
  10. public static void main(String[] args) {
  11. // 被代理类对象
  12. Superman superman = new Superman();
  13. // 注意:这里的human不是superman,因为我们是使用superman在这里是被代理类,
  14. // 通过ProxyFactory.getProxyInstance(superman)生成了superman的代理对象
  15. Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superman);
  16. System.out.println(proxyInstance.getBelief());
  17. proxyInstance.eat("fish");
  18. System.out.println("\n----------------------\n");
  19. // 之前的静态代理,我们也可以通过动态代理来创建SupClothFactory的代理对象
  20. SupClothFactory supClothFactory = new SupClothFactory();
  21. ClothFactory clothFactory = (ClothFactory)ProxyFactory.getProxyInstance(supClothFactory);
  22. clothFactory.produceCloth();
  23. }
  24. }

2、Cglib

CGLIB是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。Hibernate作为一个比较受欢迎的ORM框架,同样使用CGLIB来代理单端(多对一和一对一)关联(延迟提取集合使用的另一种机制)。CGLIB作为一个开源项目,其代码托管在github,地址为:https://github.com/cglib/cglib CGLIB代理主要通过对字节码的操作,为对象引入间接级别,以控制对象的访问。我们知道Java中有一个动态代理也是做这个事情的,那我们为什么不直接使用Java动态代理,而要使用CGLIB呢?答案是CGLIB相比于JDK动态代理更加强大,JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了。 CGLIB底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类。除了CGLIB库外,脚本语言(如Groovy何BeanShell)也使用ASM生成字节码。ASM使用类似SAX的解析器来实现高性能。我们不鼓励直接使用ASM,因为它需要对Java字节码的格式足够的了解。

(1)创建Cglib代理工厂
  1. public class CreatureCglibFactory {
  2. public static <T>T getInstanceCglib(T t) {
  3. Enhancer enhancer = new Enhancer();
  4. enhancer.setSuperclass(t.getClass());
  5. enhancer.setCallback(new MethodInterceptor() {
  6. @Override
  7. public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
  8. System.out.println("Hi creature,come on!");
  9. Object invoke = methodProxy.invoke(t, objects);
  10. System.out.println("Oh,shit!");
  11. return invoke;
  12. }
  13. });
  14. return (T)enhancer.create();
  15. }
  16. }

(2)测试
  1. public class CreatureCglibFactoryTest {
  2. public static void main(String[] args) {
  3. Dog dog = CreatureCglibFactory.getInstanceCglib(new Dog());
  4. dog.eat();
  5. }
  6. }
  7. 输出:
  8. Hi creature,come on!
  9. 哈哈哈哈,真好吃
  10. Oh,shit!

三、总结

javaProxy

1、如何根据加载到内存中的被代理类,动态创建一个代理类及其对象

  1. Proxy.newProxyInstance(o.getClass().getClassLoader(), o.getClass().getInterfaces(), handler);

2、当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法
实现InvocationHandler方法,并使用method.invoke(obj,args);
如第二章:https://www.yuque.com/wangchao-volk4/fdw9ek/pc33e3#C31kr

Cglib

我们通过一个 Enhancer 和一个 MethodInterceptor 来实现对方法的拦截。
cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。

两者区别

1、java自带的proxy只能对接口进行代理,而cglib可以对所有类进行代理。
2、java动态代理使用java原生反射API(java.lang.reflect)进行操作,在生成类上比较高效;CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效。