—-慢慢来比较快,虚心学技术—-

同文链接:https://www.jianshu.com/p/ffaae0a37999

代理模式

代理是基本的设计模式之一,它是为了提供额外的或不同的操作,而插入的用来代替实际对象的对象,简单来说,代理通常充当着中间人的角色

代理的特征是代理类与委托类有同样的接口,我们通过访问实际对象时是通过代理对象来访问的,代理类实际上不实现任何服务功能,通过调用实际对象(委托类)的方法实现功能,这样设计可以为方法实现更多的额外操作,比如日志记录,时间判断等与实际功能无关,但是能够完善程序的操作。

java中,有两种代理方式

静态代理:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。

动态代理:代理类在程序运行时运用反射机制动态创建而成

静态代理

静态代理定义如上,下面是静态代理的简单实现,在调用委托类方法前后,记录相关日志

①首先,创建统一委托接口(Person)

  1. public interface Person {
  2. void sayHi();
  3. }

②创建基本委托类Student实现Person接口

public class Student implements Person {
    private String name;
    public  Student(){
        super();
    }

    public Student(String name){
        super();
        this.name = name;
    }
    @Override
    public void sayHi() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(this.name+"说:hi!!!!");
    }
}

③创建代理类StudentsProxy,与Student一样实现Person的接口,只不过代理类会将实际委托对象引入作为操作对象(个人理解StudentsProxy更像一个传声筒,只不过这个传声筒可以做的事情更多而已)

public class StudentsProxy implements Person {
    private static Logger logger= LoggerFactory.getLogger(StudentsProxy.class);
    Student stu;
    public  StudentsProxy(Person stu){
        //只代理Student类
        if(stu.getClass() ==Student.class){
            this.stu = (Student) stu;
        }
    }
    @Override
    public void sayHi() {
        logger.debug("执行方法sayHi");

        //调用委托类方法
        stu.sayHi();
        logger.debug("方法执行完成");
    }
}

④测试调用

public class ProxyTest_static {
    public static void main(String[] args) {
        //创建委托实际对象
        Person student = new Student("小明");

        //创建代理类对象
        Person sp = new StudentsProxy(student);

        //访问代理类方法,间接调用委托类方法
        sp.sayHi();
    }
}

⑤ 运行结果

[main] DEBUG com.java.proxy.StudentsProxy - 执行方法sayHi
小明说:hi!!!!
[main] DEBUG com.java.proxy.StudentsProxy - 方法执行完成

从上述代码可以得知,静态代理可以在委托类方法前后执行额外操作而无需修改委托类本身的实现。

静态代理的弊端:

1
.因为静态代理类与委托类接口是一对一的关系,任何一个委托类想要增加代理操作都需要创建一个代理类**,对于大面积代理很明显是不实际的。

2.再往小了说,静态代理类的方法与委托类的方法也是一对一的,很明显一个代理类里面也许需要很多重复性的代码,比如记录日志等,而且一旦委托类增加了一个方法,代理类也需要相应的增加一个方法,增加了维护难度。

为了解决这些弊端,是否可以做到一个代理类代理多个或任意个委托类呢?

动态代理

java的动态代理比代理思想更向前迈进了一步,因为它可以动态地创建代理并动态地处理对所代理方法的调用。在动态代理上所做的所有调用都会被重定向到单一的调用处理器上,它的工作是揭示调用的类型并确定相应的对策。(来自《java编程思想》)

简单来讲,动态代理类和静态代理类的一个很大的区别是,静态代理类在系统初始化的时候已经被程序员定义完成并编译,而动态代理类则是通过反射机制在运行时动态创建的,动态代理类使用泛型实现动态兼容多种接口的代理,恰好解决了静态代理的重复性问题

动态代理基本组成

java动态代理主要依靠java.lang.reflect包下的InvocationHandler接口以及Proxy类实现功能。

InvocationHandler:提供调用委托类目标方法的统一出口方法—Object invoke(Object proxy, Method method, Object[] args),该方法通过反射执行委托类目标方法,同时执行额外操作

Proxy:该类负责调用JDK动态创建目标代理类,并将目标代理类的方法调用重定向到InvocationHandler的实现类的invoke方法中

动态代理的简单实现-方法日志记录与耗时计算

①在Person接口中增加一个sayHello方法

public interface Person {
    void sayHi();
    void sayHello();
}

image.gif
②Student类实现Person接口

public class Student implements Person {
    private String name;
    public  Student(){
        super();
    }

    public Student(String name){
        super();
        this.name = name;
    }
    @Override
    public void sayHi() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(this.name+"说:hi!!!!");
    }

    @Override
    public void sayHello() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(this.name+"说:hello!!!");
    }
}

image.gif
③定义动态代理对象 ,实现InvocationHandler接口,强制实现invoke方法

public class StuInvocationHandler<T> implements InvocationHandler {
    //动态代理目标类
    T target;
    public StuInvocationHandler(T target){
        this.target = target;
    }

    /**
     * 实现动态代理方法,必须实现,作为代理方法的必经之路
     * 
     * @param proxy 动态代理接口对象
     * @param method 目标方法
     * @param args 目标方法参数
     *       
     * @return java.lang.Object 方法执行结果
     *       
     * @author *** 2019/2/18
     * @version 1.0
     **/
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        logger.debug("执行"+method.getName()+"方法");
        long startTime = System.currentTimeMillis();
        // 通过反射执行目标方法
        Object result = method.invoke(target, args);
        long finishTime = System.currentTimeMillis();
        logger.debug("方法执行完成,耗时"+(finishTime - startTime)+"ms");
        return result;
    }
}

image.gif
⑤测试代码

public class ProxyTest_dynamic {
    public static void main(String[] args) {

        //创建目标委托对象
        Student student = new Student("小明");

        //创建一个与代理对象相关联的InvocationHandler
        StuInvocationHandler stuInvocationHandler= new StuInvocationHandler<Person>(student);

        //创建一个代理对象proxyInstance 来代理stuInvocationHandler,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
        Person proxyInstance = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuInvocationHandler);

        //代理执行方法
        proxyInstance.sayHi();
        proxyInstance.sayHello();
    }
}

image.gif
⑥运行结果

[main] DEBUG com.java.handler.StuInvocationHandler - 执行sayHi方法
小明说:hi!!!!
[main] DEBUG com.java.handler.StuInvocationHandler - 方法执行完成,耗时1015ms
[main] DEBUG com.java.handler.StuInvocationHandler - 执行sayHello方法
小明说:hello!!!
[main] DEBUG com.java.handler.StuInvocationHandler - 方法执行完成,耗时1001ms

image.gif
那么问题来了,动态代理是怎么做到将委托类的方法重定向到InvocationHandler的实现类的invoke方法中的呢?

其实,Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuInvocationHandler)方法InvocationHandler的实现类作为参数传入,在创建目标代理类的时候,已经将代理类里面的对应方法改成了InvocationHandler的实现类方法调用实现的形式

总结

1.代理是基本的设计模式之一,它是为了提供额外的或不同的操作,而插入的用来代替实际对象的对象,简单来说,代理通常充当着中间人的角色。代理分为静态代理和动态代理两种方式

2.静态代理是由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了

3.动态代理是代理类在程序运行时运用反射机制动态创建而成的代理方式,主要依靠java.lang.reflect包下的InvocationHandler接口以及Proxy类实现功能

4.动态代理统一通过invoke作为方法调用出口,实现方式是在动态创建代理类的时候将方法调用指向InvocationHandler的invoke方法,底层反射调用方法

参考文档

【1】https://www.cnblogs.com/gonjan-blog/p/6685611.html
【2】《java编程思想》

如有贻误,还请评论指正