博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java基础-静态代理以及动态代理
阅读量:4695 次
发布时间:2019-06-09

本文共 4721 字,大约阅读时间需要 15 分钟。

动态代理:

在了解动态代理之前,先对代理有一个认识.

代理模式是Java常见的设计模式之一。所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。

打个比方:你买火车票的时候,并不直接花钱购买, 而是将钱预先垫付到抢票软件上, 使抢票软件为你购买, 你要做的行为就是买票,抢票软件就是你的代理

代理对象控制对被代理对象的访问:

1388859-20180504153752219-1480287105.png

这是代理的通用模型图

  • Subject:定义了被代理角色和代理角色的共同接口或者抽象类,也就是subject中定义了共同接口opration();

  • Realsubject:实现或者继承抽象主题角色,定义实现具体业务逻辑的实现。

  • Proxy:也就是代理人,porxy持有Realsubject的引用控制和实现. 并且有自己的处理逻辑.

代理分为静态和动态两种,先了解静态代理,知道其缺点后,再了解动态代理,会豁然开朗.

静态代理的作用:

静态代理通常用于对原有业务逻辑的扩充。比如持有二方包的某个类,并调用了其中的某些方法。然后出于某种原因,比如记录日志、打印方法执行时间,但是又不好将这些逻辑写入二方包的方法里。所以可以创建一个代理类实现和二方方法相同的方法,通过让代理类持有真实对象,然后在原代码中调用代理类方法,来达到添加我们需要业务逻辑的目的。

静态代理的实现:

这里方便理解 引入的Proxy是自己定义的,在下面动态代理的时候会使用java.reflect中提供的Proxy

package proxy;  //将业务层抽象出来的接口.public interface Subject {      void doSomething();}

package proxy;//真实对象,也就是被代理者,理解为你自己就可以了,要进行买票行为.public class RealObject implements Subject{        @Override    public void doSomething() {        System.out.println("这里是真实对象");    }}

package proxy;//代理者,进行代理事务的人,理解成抢票软件,要代替你进行买票行为.public class Proxy implements  Subject{    private Subject realObject;     Proxy(Subject realObject){        this.realObject=realObject;        //从这里知道需要代理的人是谁    }        @Override    public void doSomething() {        System.out.println("这里是代理对象");        //可以在调用真实对象前增加操作        //例如在开抢前告诉你,抢票要开始了.        realObject.doSomething();//在这里调用真实的代理对象        //可以在调用真实对象后增加操作.        //在抢票结束后,告诉你成功或者失败.    }    }

执行:

package proxy;public class ProxyTest {    public static void main(String[] args){        RealObject realObject =new RealObject();        realObject.doSomething();//被代理对象做的事,或者说委托人想要做的事        System.out.println("-----------------");        Proxy proxy =new Proxy(new RealObject());//告知代理对象被代理对象是谁,或者说告诉执行者委托人要做什么.        proxy.doSomething();//通过代理对象调用被代理对象,实现代理.        /*        代理和被代理是不是有点晕,换一种说法来说.        以DOTA为例:        大哥本来要自己拉野,忽然发现一大波线袭来,于是就和酱油说,帮我拉两波野,一会带你喝汤! 大哥就是被代理者        酱油说: 是大哥! 酱油在去拉野的路上,吃了赏金符,拉完野之后配合队友杀了一波人. 酱油就是代理者        吃赏金符就是预处理.        杀人就是结果处理.         */    }}

大家有没有发现,这样虽然形成了代理行为,但是写法恨死,耦合度很高,现在是一个火车票,如果有飞机票,演唱会票等等一系列的需求和一系列的雇主,会导致代理类的代码量越来越大,并且会有重名的危险,这也就是静态代理的缺点.

为了解决这个问题,大佬们发明出了动态代理的方法.

JAVA为我们提供了动态代理的接口,使用起来非常方便,下边来了解一下.

需要将要扩展的功能写在一个InvocationHandler 实现类里:

这个Handler中的invoke方法中实现了代理类要扩展的公共功能。

静态代理的缺点:

有十个不同的RealObject,同时我们要去代理的方法是不同的,比要代理方法:doSomething、doAnotherThing、doTwoAnotherThing.

方法1:为每个代理创建代理类,这样会造成数个类

方法2: 在一个代理类中创建数个代理方法,当代理方法越来越多的时候容易造成代理类中的代码量越来越大 并且有重名的风险.

为此动态代理诞生了:

动态代理的使用:

先看一下动态代理的接口:

  1. 实现InvocationHandler接口

我们来看一下接口的结构:

public interface InvocationHandler {    public Object invoke(Object proxy, Method method, Object[] args)        throws Throwable; //这个接口只有一个方法 就是invoke方法,也就是说我们只要实现invoke方法就可以了}

虽然这个参数不用我们传入,但是还是需要了解一下,第一个就是代理类的实例,第二个是方法, 第三个是对象的参数数组.

2.通过newProxyInstance方法创建代理实例:

newProxyInstance(ClassLoader loader, Class
[] interfaces, InvocationHandler h) 通过这个方法返回我们代理类的实例, 也就是RealObject的Proxy返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序

从参数上看 第一个参数是类加载器, 第二个方法是个Class对象数组接口,第三个参数是我们上边实现的InvocationHandler

在这里不贴源码了,有兴趣的可以自己追踪或者查看下方我的参考资料.

动态代理实例:

沿用上边静态代理的类,下面只写测试类和处理类.

处理类:

import java.lang.reflect.Proxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class DynamicProxyHandler implements InvocationHandler {    private Object realObject;    Object bind(Object realObject){        this.realObject=realObject;//给真实对象赋值        return    Proxy.newProxyInstance(realObject.getClass().getClassLoader(),                                       realObject.getClass().getInterfaces(),                                        this);        //通过Class对象得到该对象的构造器,接口方法数组,传入自己构造后的实例.        //java.reflect.Proxy方法最后返回的是一个代理实例.    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        //在这里拓展代理逻辑        System.out.println("rua!");        Object result= method.invoke(realObject,args);//方法的调用.        //这里的invoke调用是java进行调用,proxy对象,方法,和参数数组已经是我们之前传入的被代理类的了.        return result;    }}

测试类:

public class DynamicProxy {    public static void main(String[] args){        DynamicProxyHandler pHandler=new DynamicProxyHandler();//创建我们动态代理处理类的对象        Subject proxy=(Subject)pHandler.bind(new RealObject());//对其进行动态代理的参数绑定然后返回一个代理实例        proxy.doSomething();//调用        }}

我第一次看的时候也是云里雾里,不明白为什么这样就动态了, 这里的方法调用,都与方法对象无关,不直接进行调用.

不管你在Subject中定义了什么接口,都与调用无关,依靠反射,实时获取你的方法,参数,类的数据,再通过反射进行调用.

有十个不同的RealObject,同时我们要去代理的方法是不同的,比要代理方法:doSomething、doAnotherThing、doTwoAnotherThing.

以这个例子来说, 我传入10个不同的对象,对象中自然有自己的实现,我通过反射拿到实现后构建新的对象,不会对原有对象造成影响,再对新的对象进行方法调用操作,来100个 1000个对象也同样可以.

上面一切的条件都是建立在反射的基础上,如果反射了解的不太清楚,请返回去看反射,我的博客也有写,也可以通过网络自行了解.

参考资料:

1.https://blog.csdn.net/wangqyoho/article/details/77584832

2.https://www.zhihu.com/question/20794107/answer/23330381
3.Thinking in java page 592-598

转载于:https://www.cnblogs.com/Curry-Rice/p/8990904.html

你可能感兴趣的文章
常用加密算法
查看>>
MYSQL培训准备(2):MYSQL自增长陷阱
查看>>
IDEA 创建普通的maven+java Project
查看>>
背包专题练习
查看>>
Python学习笔记(二)
查看>>
T-SQL: Create folders in remote server by sql statement
查看>>
linux SVN安装及配置教程
查看>>
poj1088 滑雪问题 dfs写法
查看>>
C# DataTable.Select()方法,条件中使用类型转换
查看>>
Windows7 Questions
查看>>
数据库迁移工具
查看>>
不使用中间变量交换两个变量的值
查看>>
Mysql导入sql文件
查看>>
大道至简:软件工程实践者的思想——第六章感想 从编程到工程
查看>>
SharePoint 2010版本表
查看>>
【BootStrap】初步教程
查看>>
[bbk4397] 第1集 - 第一章 AMS介绍
查看>>
Track Active Item in Solution Explorer
查看>>
maven内置属性
查看>>
spring Aop2
查看>>