zoukankan      html  css  js  c++  java
  • JAVA设计模式——代理(动态代理)

      传送门:JAVA设计模式——代理(静态代理)

      序言:

      在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的我们的功能,我们更需要学习的是其底层是怎么样的一个原理,而AOP的原理就是java的动态代理机制,所以本篇随笔就是对java的动态机制进行一个回顾。

    动态代理模式主要由四个元素共同构成:

      1. 接口,接口中的方法是要真正去实现的

      2. 被代理类,实现上述接口,这是真正去执行接口中方法的类

      3. InvocationHandler接口的实现,帮助被代理类去实现方法

      4. 代理类(Proxy)

    一、InvocationHandler

    在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。

    每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。

    我们来看看InvocationHandler这个接口的唯一一个方法 invoke方法:

    Object invoke(Object proxy, Method method, Object[] args) throws Throwable

    我们看到这个方法一共接受三个参数,那么这三个参数分别代表什么呢?

    • proxy: 指代我们所代理的那个真实对象
    • method:  指代的是我们所要调用真实对象的某个方法的Method对象
    • args:  指代的是调用真实对象某个方法时接受的参数

    如果不是很明白,等下通过一个实例会对这几个参数进行更深的讲解。

    二、Proxy类:

    Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:

    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException

    这个方法的作用就是得到一个动态的代理对象,其接收三个参数,我们来看看这三个参数所代表的含义:

    • loader:  一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
    • interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
    • h:  一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

    三、例子:

    动态代理:就是实现阶段不用关系代理是哪个,而在运行阶段指定具体哪个代理。

    抽象接口的类图如下:

     

          --图来自设计模式之禅

    所以动态代理模式要有一个InvocationHandler接口 和 InvocationHandler的实现类GamePlayerIH。其中 InvocationHandler是JD提供的动态代理接口,对被代理类的方法进行代理。

    代码实现如下

    抽象主题类或者接口:

    package com.yemaozi.proxy.dynamic;
    
    /*
     * 动态代理:就是实现阶段不用关系代理是哪个,而在运行阶段指定具体哪个代理。
     */
    public interface IGamePlayer {
        //登录游戏
        public void login(String username, String password);
        
        //击杀Boss
        public void killBoss();
        
        //升级
        public void upGrade();
    }

    被代理类:

    package com.yemaozi.proxy.dynamic;
    
    public class GamePlayer implements IGamePlayer {
        
        private String name = "";
        
        public GamePlayer(String name){
            this.name = name;
        }
        
        public void login(String username, String password) {
            System.out.println("登录名为 "+username+" 进入游戏," + name + " 登录        成功!");
        }
            
        public void killBoss() {
            System.out.println(this.name + " 击杀了Boss!");
        }
    
        public void upGrade() {
            System.out.println(this.name + "升级了!");
        }
    
    }    

    动态代理处理器类:

    package com.yemaozi.proxy.dynamic;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class GamePlayerIH implements InvocationHandler{
    
        //被代理的对象
        private Object obj;
        
        //将需要代理的实例通过处理器类的构造方法传递给代理。
        public GamePlayerIHr (Object obj){
            this.obj = obj;
        }
        
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            Object result = null;
            if("login".equalsIgnoreCase(method.getName())){
                //这个方法不受任何影响的情况下,在方法前后添加新的功能,或者增强方法,
                //从侧面切入从而达到扩展的效果的编程,就是面向切面编程(AOP Aspect Oriented Programming)。
                //AOP并不是新技术,而是相对于面向对象编程的一种新的编程思想。在日志,事务,权限等方面使用较多。
                System.out.println("代理登录游戏!");
                result = method.invoke(this.obj, args);
                return result;
            }
            result = method.invoke(this.obj, args);
            return result;
        }
    
    }

    由于代理是动态产生的,所以不需要再声明代理类。

    动态代理场景类:

    package com.yemaozi.proxy.dynamic;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    
    public class Client {
        public static void main(String[] args) {
            IGamePlayer gp = new GamePlayer("张三");
            InvocationHandler gpHandler = new GamePlayerIH(gp);
            //获取主题类的类加载器ClassLoader
            ClassLoader classLoader = gp.getClass().getClassLoader();
            //一个Interface对象的数组
            Class<?>[] cls = new Class[]{IGamePlayer.class};
            //动态产生一个代理者。
            IGamePlayer proxyGp =(IGamePlayer)Proxy.newProxyInstance(classLoader, cls, gpHandler);
            proxyGp.login("zhangsan", "123456");
            proxyGp.killBoss();
            proxyGp.upGrade();
        }
    }
    
    执行结果:
    代理登录游戏!
    登录名为 zhangsan 进入游戏,张三 登录成功!
    张三 击杀了Boss!
    张三升级了!
    
    //在此,我们没有创建代理类,但是确实有代理类帮我们完成事情。    

    四、动态代理的应用———AOP

    在此代理模式中,不仅代理是动态产生的(即在运行的时候生成),而且还在代理的时候,也增加了一些处理。在此处增加的处理,其实就是另一种编程思想-----面向切面编程思想(AOP Aspect Oriented Programming)。

    带有AOP的动态代理模式类图:

            --图来自设计模式之禅

     

    从上图中,可以看出有两个相对独立的模块(Subject和InvocationHandler)。动态代理实现代理的职责,业务逻辑Subject实现相关的逻辑功能,两者之间没有必然的相互耦合的关系。然而,通知Advice从另一个切面切入,最终在上层模块就是Client耦合,完成逻辑的封装。

    代码清单如下

    抽象主题或者接口:

    package com.yemaozi.proxy.dynamic_aop;
    
    public interface Subject {
        public void doSomething(String str);
        //...可以多个逻辑处理方法。。。
    }

    主题的实现类:

    package com.yemaozi.proxy.dynamic_aop;
    
    public class RealSubject implements Subject{
    
        public void doSomething(String str) {
            //do something...
            System.out.println("do something..." + str);
        }
    
    }

    AOP的通知接口:

    package com.yemaozi.proxy.dynamic_aop;
    
    //通知接口及定义、
    public interface IAdvice {
        public void exec();
    }

    前置通知

    package com.yemaozi.proxy.dynamic_aop;
    
    public class BeforeAdvice implements IAdvice {
        //在被代理的方法前来执行,从而达到扩展功能。
        public void exec() {
            System.out.println("前置通知被执行!");
        }
    }

    后置通知:

    package com.yemaozi.proxy.dynamic_aop;
    
    public class AfterAdvice implements IAdvice {
        
        //在被代理的方法后来执行,从而达到扩展功能。
        public void exec() {
            System.out.println("后置通知被执行!");
        }
    }

    动态代理的处理器类:

    所有的方法通过invoke方法类实现。

    package com.yemaozi.proxy.dynamic_aop;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class MyInvocationHandler implements InvocationHandler {
    
        //被代理的对象
        private Subject realSubject;
        //通过MyInvocationHandler的构造方法将被代理对象传递过来。
        public MyInvocationHandler(Subject realSubject){
            this.realSubject = realSubject;
        }
        //执行被代理类的方法。
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //在执行方法前,执行前置通知。
            IAdvice beforeAdvice = new BeforeAdvice();
            beforeAdvice.exec();
            Object result = method.invoke(this.realSubject, args);
            //在执行方法后,执行后置通知。
            IAdvice afterAdvice = new AfterAdvice();
            afterAdvice.exec();
            //前置通知,和后置通知,都是要看具体实际的业务需求来进行添加。
            return result;
        }
    
    }

    动态代理类(Proxy)

    package com.yemaozi.proxy.dynamic_aop;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    
    public class DynamicProxy {
        
        /**
         * public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler handler)
         * loader:
         *             一个ClassLoader对象,定义了由哪个ClassLoader对象,来对生产的代理进行加载。
         * interfaces:
         *             一个Interfaces数组,表示我将要给我所代理的对象提供一组什么样的接口,
         *             如果提供一组接口给它,那么该代理对象就宣称实现了该接口,从而可以调用接口中的方法。
         *             即,查找出真是主题类的所实现的所有的接口。
         * handler:
         *             一个InvocationHandler对象,表示当我这个动态代理对象在调用方法时,会关联到该InvocationHandler对象。
         *             该InvocationHandler与主题类有着关联。
         */
        public static <T> T newProxyInstance(ClassLoader classLoader, Class<?>[] interfaces, InvocationHandler handler){
            @SuppressWarnings("unchecked")
            T t = (T) Proxy.newProxyInstance(classLoader, interfaces, handler);
            return t;
        }
    }

    动态代理场景类:

    package com.yemaozi.proxy.dynamic_aop;
    
    import java.lang.reflect.InvocationHandler;
    
    public class AOPClient {
        
        public static void main(String[] args) {
            Subject realSubject = new RealSubject();
            InvocationHandler handler = new MyInvocationHandler(realSubject); 
            ClassLoader classLoader = realSubject.getClass().getClassLoader();
            Class<?>[] interfaces = realSubject.getClass().getInterfaces();
            Subject proxySubect = DynamicProxy.newProxyInstance(classLoader, interfaces, handler);
            proxySubect.doSomething("这是一个Dynamic AOP示例!!!");
        }
    }
    
    执行结果:
    前置通知被执行!
    do something...这是一个Dynamic AOP示例!!!
    后置通知被执行!

    动态代理中invoke的动态调用:

    动态代理类DynamicProxy是个纯粹的动态创建代理类通用类。

    所以在具体业务中,可以在进一步封装具体的具有业务逻辑意义的DynamicProxy类。

    代码如下

    具体业务的动态代理:

    package com.yemaozi.proxy.dynamic_aop;
    
    import java.lang.reflect.InvocationHandler;
    //具体业务的动态代理。
    public class SubjectDynamicProxy extends DynamicProxy {
        public static <T> T newProxyInstance(Subject subject){
            ClassLoader classLoader = subject.getClass().getClassLoader();
            Class<?>[] interfaces = subject.getClass().getInterfaces();
            InvocationHandler handler = new MyInvocationHandler(subject);
            T t = newProxyInstance(classLoader, interfaces, handler);
            return t;
        }
    }

    动态代理在现在用的是非常的多的,如像Spring AOP ,DBCP连接池,AspectJ等

  • 相关阅读:
    Mysql查看所有表的数据量
    Mysql存储过程查询数据插入别的表里。
    MYSQL查看最大连接数和修改最大连接数
    Centos7 安装字体库&中文字体
    docker-部署elk-6.1.3
    confluence输入数学公式之mathjax
    elasticsearch安装ansj分词器
    mongorestore 一次踩雷
    let‘s encrypt之nginx-https没有小锁
    微服务预想
  • 原文地址:https://www.cnblogs.com/TvvT-kevin/p/9867851.html
Copyright © 2011-2022 走看看