• Java基础:代理


    什么是代理

    代理模式是一种设计模式,简单说即是在不改变源码的情况下,实现对目标对象的功能扩展。


    比如有个歌手对象叫Singer,这个对象有一个唱歌方法叫sing()

    1 public class Singer{
    2     public void sing(){
    3         System.out.println("唱一首歌");
    4     }  
    5 }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    假如你希望对目标对象Singer的sing方法进行功能扩展,例如在唱歌前后向观众问好和答谢,类似这样:

    1 public void sing(){
    2     System.out.println("向观众问好");
    3     System.out.println("唱一首歌");
    4     System.out.println("谢谢大家");
    5 }  
    
    • 1
    • 2
    • 3
    • 4
    • 5

    但是又不能直接对源代码进行修改,甚至有可能你都不知道要对哪个目标对象进行扩展。这时就需要用到java的代理模式了。

    1.静态代理(委托类、代理类):

    静态代理要求原始类有实现某个接口。
    需要创建一个代理类,实现和原始类相同的接口,并在需要增强的方法里,调用原始类的该方法,调用前后加上我们需要添加的代码。
    使用的时候,直接创建一个代理类实例,调用目标方法即可。

    使用步骤:

    共同接口

    public interface Action {
        public void doSomething();
    }
    
    • 1
    • 2
    • 3

    原始类

    public class RealObject implements Action{
        public void doSomething() {
            System.out.println("do something");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    代理类

    public class Proxy implements Action {
        private Action realObject; 
        public Proxy(Action realObject) {
            this.realObject = realObject;
        }
        public void doSomething() {
            System.out.println("proxy do");
            realObject.doSomething();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    使用

    Proxy proxy = new Proxy(new RealObject());
    proxy.doSomething();
    
    • 1
    • 2
    示例
    public interface ISinger {
        void sing();
    }
    
    /**
     *  目标对象实现了某一接口
     */
    public class Singer implements ISinger{
        public void sing(){
            System.out.println("唱一首歌");
        }  
    }
    
    /**
     *  代理对象和目标对象实现相同的接口
     */
    public class SingerProxy implements ISinger{
        // 接收目标对象,以便调用sing方法
        private ISinger target;
        public UserDaoProxy(ISinger target){
            this.target=target;
        }
        // 对目标对象的sing方法进行功能扩展
        public void sing() {
            System.out.println("向观众问好");
            target.sing();
            System.out.println("谢谢大家");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    测试类:

    /**
     * 测试类
     */
    public class Test {
        public static void main(String[] args) {
            //目标对象
            ISinger target = new Singer();
            //代理对象
            ISinger proxy = new SingerProxy(target);
            //执行的是代理的方法
            proxy.sing();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    总结:这里做的事情无非就是,创建一个代理类SingerProxy,继承原始类的ISinger接口,实现其中的方法,并在实现中调用目标对象的方法。这里的关键是“调用目标对象方法”,如果直接重写就不叫代理了。

    优缺点

    优点:扩展原功能,不侵入原代码。

    缺点:
    ①冗余。由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。
    ②不易维护。代理对象必须提前写出,一旦接口发生了变化,代理对象的代码也要进行维护。

    2.动态代理(委托类、中介类)

    代理类在程序运行时运用反射机制创建的代理方式被成为动态代理。
    也就是说,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。
    相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。

    2.1 JDK动态代理

    同样要求原始类实现某个接口,但不用手动创建代理类,而是创建中介类。中介类实现InvocationHandler接口。

    使用:

    调用Proxy类中的newProxyInstance(ClassLoader loader,Class[]
    interfaces,InvocationHandler h)方法以创建一个动态代理对象,其中第三个参数为我们创建的实现InvocationHandler接口的类(中介类),前两个参数可通过目标类.getclass().getxxx获取。

    中介类:

    需实现InvocationHandler接口,包含一个Object类型的对象,并利用其编写中介类的有参构造函数。重写的方法:public Object invoke(Object proxy, Method method, Object[] args) throws Throwable里,proxy表示代理类对象, method标识了我们具体调用的代理类的方法,args为这个方法的参数。

    示例1:
    public interface ISinger {
        void sing();
    }
    
    /**
     *  目标对象实现了某一接口
     */
    public class Singer implements ISinger{
        public void sing(){
            System.out.println("唱一首歌");
        }  
    }
    
    -------------------------
    
    public class Test{
        public static void main(String[] args) {
            Singer target = new Singer();//这行要自己写
            ISinger proxy  = (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;
                        }
                    });
            proxy.sing();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    示例2:
    
    public static void main(String[] args) throws InterruptedException {
    	EnHello enHello=new EnHello();
    	Hello hello=(Hello)Proxy.newProxyInstance(enHello.getClass().getClassLoader(),enHello.getClass().getInterfaces(), new MyInvocationHandler(enHello));
    	hello.sayHello("Tom");
    
    }
    
    interface Hello{
    	String sayHello(String username);
    }
    
    static class EnHello implements Hello{
    	@Override
    	public String sayHello(String username) {
    		System.out.println("Hello, "+username);
    		return "finished";
    	}
    }
    
    static class MyInvocationHandler implements InvocationHandler{
    	private Object object;
    	public MyInvocationHandler(Object object){
    		this.object=object;
    	}
    
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		Object result=null;
    		System.out.println("before say hello");
    		if("sayHello".equals(method.getName())){
    			result=method.invoke(object,args);
    		}
    		System.out.println("before say hello");
    		return result;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    还可以只为指定方法动态代理,在invoke方法加上以下判断:

    String methodName = method.getName();
    if("eating".equals(methodName))
        method.invoke(obj,args);
    
    • 1
    • 2
    • 3

    优点一:可以隐藏委托类的实现;
    优点二:可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理

    2.2 CGLib动态代理

    JDK动态代理和cglib动态代理有什么区别?

    使用JDK动态代理的对象必须实现一个或多个接口
    使用cglib代理的对象则无需实现接口。

    cglib可以对任意类生成代理对象,它的原理是对目标对象进行继承代理,所以如果目标对象被final修饰,那么该类无法被cglib代理。

    使用方法:

    导包-创建MethodInterceptor实现类

    使用cglib需要引入cglib的jar包,如果你已经有spring-core的jar包,则无需引入,因为spring中包含了cglib。

    目标类(原始类)不能为final

    目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法

    示例1:
    /**
     * 目标对象,没有实现任何接口
     */
    public class Singer{
    
        public void sing() {
            System.out.println("唱一首歌");
        }
    }
    
    ----------------------
    
    /**
     * 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();
        }
    
    /*
    使用时只有intercept方法中,代码行 method.invoke前后的代码需要修改,其他的代码直接使用
    */
    
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("向观众问好");
            //执行目标对象的方法
            Object returnValue = method.invoke(target, args);
            System.out.println("谢谢大家");
            return returnValue;
        }
    }
    
    -----------------------
    
    /**
     * 测试类
     */
    public class Test{
        public static void main(String[] args){
            //目标对象
            Singer target = new Singer();
            //代理对象
            Singer proxy = (Singer)new ProxyFactory(target).getProxyInstance();
            //执行代理对象的方法
            proxy.sing();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    示例2:
    public class TestCglib implements MethodInterceptor {    
        Object target;   
        //动态生成一个新的类,使用父类的无参构造方法创建一个指定了特定回调的代理实例    
        public Object getProxyObject(Object object) {      
            this.target = object;       
            //增强器,动态代码生成器     
            Enhancer enhancer=new Enhancer();        
            //回调方法
            enhancer.setCallback(this); 
            //设置生成类的父类类型        
            enhancer.setSuperclass(target.getClass());   
            //动态生成字节码并返回代理对象      
            return enhancer.create();   
        }
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy 
    methodProxy) throws Throwable {
            System.out.println("----------before");    
            // 调用方法      
            Object result = methodProxy.invoke(target, objects);   
            System.out.println("----------after");       
            return null;  
        }
    }
    
    //使用
    public static void main(String[] args) {
          Boss boss=(Boss) new TestCglib().getProxyObject(new Boss());
          boss.eating();
          boss.sleeping();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
  • 相关阅读:
    JADE(自适应差分进化优化算法)在C++中的完整实现与深度解析
    一文搞懂模型量化算法
    关于git flow的一点思考
    CSP-J/S第二轮认证注意事项
    解读 --- System.Windows.Forms.Timer是前台线程吗?
    Java—Set
    Linux——权限
    小程序优点
    计算机毕业设计(附源码)python医院预约挂号系统
    ANet模块化通信管理机在北京冬奥会项目的应用
  • 原文地址:https://blog.csdn.net/comeonmao/article/details/132758552