Java代理模式详解

Java代理模式

参考文章:
https://www.jianshu.com/p/41f28d7ef6f1
https://www.jianshu.com/p/bacaafb5d02d

一、代理模式介绍

1. 代理模式的重要性:

spring底层就有用到动态代理模式,spring这个矿机中所周知是非常重要的,因此如果不学习动态代理这块内容,想学好spring是比较吃力的,当然spring还是用了动态字节码技术。

2. 代理模式的简介:

代理模式为其他对象提供了一种代理以控制对这个对象的访问,在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间启动中介的作用。客户端都是和代理打交道的,通过代理来实现与目标对象之间的交流。

3. 代理模式角色分析:

1 .抽象角色(接口):声明真实对象和代理对象的共同接口。
2 .代理角色:代理对象角色内部含有对真是对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口,以便在任何时刻都能代替真实对象,同事代理对象可以在执行真实对象操作时,附加其他操作,相当于对真实对象进行封装
3 . 真实角色:代理角色多代表的真实对象,是我们最终要引用的对象。

4. 代理种类:

静态代理,动态代理(JDK代理、CLIB代理);

二.代理模式详解

1. 静态代理:

  • 抽象角色代码:
//定义一个接口,并声明接口中的方法;
public interface ISinger {
     void sing();
}
  • 真实角色代码:
//定义一个class,并实现上述接口,重写sing()方法;
public class Singer implements ISinger{
    @Override
    public void sing() {
        System.out.println("sing a song");
    }
}
  • 代理角色代码:
//定义一个代理类,实现抽象角色接口,添加构造方法,并重写sing()方法
public class SingerProxy implements ISinger {
    private ISinger iSinger ;
    public SingerProxy(ISinger iSinger){
        this.iSinger = iSinger;
    }

    @Override
    public void sing() {
        System.out.println("向观众问好");
        iSinger.sing();
        System.out.println("谢谢大家");
    }
}

  • 测试代码及输出结果:
  public class MainClass {
    public static void main(String[] args) {
        //创建一个真实角色
        Singer singer = new Singer();
        //创建一个代理角色,构造方法中需要真实角色;
        ISinger singerProxy = new SingerProxy(singer);
        //代理角色执行方法(代理角色内部调用真实角色对应的方法)
        singerProxy.sing();
    }
}
//输出如下:
"C:\Program Files\Java\jdk1.8.0_211\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA .......
向观众问好
sing a song
谢谢大家

Process finished with exit code 0

2. 动态代理的jdk代理:

  • java 动态代理所使用到的类位于java.lang.reflect包下,一百年主要涉及到一下两个类;

    • Interface InvocationHandler:该接口中仅定义了一个方法public Object invoke(Object obj,Method method,Object[] args),在实际使用时,第一个参数obj一般是指的代理类,method是被代理的方法,如上述代码中的sing(),args为该方法的参数数组;
    • Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包含一下内容:
      1)、newProxyInstance可以这样理解,生成一个代理对象,可以代理真实对象,完成真实对象的操作以及自己的额外操作;
      2)、所谓Dynamic Proxy是这样一种calss:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后改class就宣称它实现了这些interface,你当然可以把改class实例当作这些interface中的任何一个来用,当然这个Dynamic proxy其实就是一个proxy,它不会替你做实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作;
  • 动态代理中的抽象角色代码:

//和静态代理一样,都需要有一个接口;
public interface ISinger {
    void sing();
}
  • 动态代理中的真实角色代码:
//定义一个class,实现抽象代码,并重写相关的方法;
public class Singer implements ISinger{

    @Override
    public void sing() {
        System.out.println("sing a song");
    }
}

  • 动态代理中的代理角色代码:
无!!!(此处代理角色在测试代码中自动生成)
  • 动态代理中的测试代码及结果:
public class MainClass {
    public static void main(String[] args) {
        //1、创建一个真实角色
        Singer target = new Singer();
        //2、调用Proxy.newProxyInstance方法,并构造一个InvocationHandler对象,
        //3、在对象内部重写invoke方法,同时调用method.invoke(target,args);
        //4、并在该方法的上下添加自己的代码逻辑
        //其中:target.getClass().getClassLoader():获取类加载器,用来生成代理对象;
        //			target.getClass().getInterfaces()获取接口元信息;
        ISinger iSinger = (ISinger) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("动态代理---向观众问好");//在真实对象的方法被调用“前”编写自己的业务逻辑
                Object returnValue = method.invoke(target,args);//此处通过反射调用真实对象对应的方法;
                System.out.println("动态代理---向观众问好");//在真实对象的方法被调用“后”编写自己的业务逻辑
                return returnValue;
            }
        });
        iSinger.sing();
    }
}
//测试结果
"C:\Program Files\Java\jdk1.8.0_211\bin\java.exe" "-javaagent:C:\Program.......
动态代理---向观众问好
sing a song
动态代理---向观众问好

Process finished with exit code 0

3. 动态代理的CLIB代理:

  • 目标类不能为final,目标对象的方法如果为final / static,那么就不会被拦截,即不会执行目标对象额外的业务方法
  • 动态代理中的抽象角色代码:
无!!!
  • 动态代理中的真实角色代码:
public class Singer {
    public void sing(){
        System.out.println("sing a song");
    }
}

  • CGLIB动态代理中的工厂类代码:
public class ProxyFactory implements MethodInterceptor {
    private  Object target;
    public ProxyFactory(Object target){
        this.target = target;
    }
    public Object getProxyInstance(){
        //1.工具类
         Enhancer en = new Enhancer();
         //2.设置父类
         en.setSuperclass(target.getClass());
         //3.设置回调函数
         en.setCallback(this);
         //4.创建子类(代理对象)
         return en.create();
    }


    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //此处在真实对象方法执行前编写自己的业务逻辑
        System.out.println("CGLIB动态代理————向观众问好");
        Object returnValue = method.invoke(target,objects);//真实对象对应方法的调用;
        //此处在真实对象方法执行前编写自己的业务逻辑
        System.out.println("CGLIB动态代理————谢谢大家");
        return returnValue;
    }
}

  • CGLIB动态代理中的测试类代码:
public class MainClass {
    public static void main(String[] args) {
        Singer singer = new Singer();
        Singer proxySinger = (Singer) new ProxyFactory(singer).getProxyInstance();
        proxySinger.sing();
    }
}
//输出结果:
"C:\Program Files\Java\jdk1.8.0_211\bin\java.exe" "-javaagent:C:\Program 。。。。。。
CGLIB动态代理————向观众问好
sing a song
CGLIB动态代理————谢谢大家

Process finished with exit code 0

三. 代理模式的优缺点总结:

在代理模式之中:代理对象真实对象应该实现相同的功能(此功能可以表示为都需要有相同某个public方法),在面向对象的编程中,如果我们想要保证代理对象真实对象有相同的功能,有两种方式:

  1. 直观方式(JDK代理):定义一个功能接口(上述中的抽象角色),然后让真实对象代理对象都来实现这个接口;
  2. 隐讳方式(CGLIB)代理:通过继承来实现;因为如果代理对象继承自真实对象,那么代理对象真实对象肯定有相同的功能;
    其中,JDK提供的是创建动态代理的机制是以第1种思路设计的,而CGLIB代理模式测试以第2种思路设计的;

1.静态代理:

  • 优点:可以做到在不修改目标对象的功能前提下,对目标功能扩展;
  • 缺点:代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护;

2.jdk动态代理:

  • 优点:代理对象不需要实现接口, 利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
  • 缺点:目标对象一定要实现接口,否则不能用动态代理

3.CGLIB动态代理:

  • 优点:当要实现的类没有实现某一个接口的时候,且又像在不改原代码逻辑的情况下对这个类做代理,使用CGLIB动态代理最合适;
  • 缺点:相对来说更复杂一些,且不能被final修饰(被final修饰就不能被继承,就无法生成子类,就不能实现代理);

什么时候使用代理模式?

  • 当我们想要隐藏某个类时,可以为其提供代理;
  • 当一个类需要对不同的调用这提供不同的调用权限时,可以使用代理类来实现(代理类不一定只有一个,我们可以建立多个代理类来实现,页可以在一个代理类中继续宁权限半段来进行不同权限的功能调用);
  • 当我们要扩展某个功能时,可以使用代理模式,在代理模式中进行见到那扩展(只针对见到那扩展,可在引用委托类的语句之前与之后进行)

如果对JAVA反射不懂,请看这里:JAVA高级特性之——反射

具体jdk动态代理是如何实现的,且听下回分解

你可能感兴趣的:(java,设计模式)