1.怎样理解代理模式:
前提:
如果类A需要访问类C,但类C不让类A直接访问它,此时就需要一个类B(类C的代理类)让类A先访问类B,然后类B再访问类C(当然,在类B完成对类C的访问时,可以添加一些额外功能),最终以实现类A想要访问类C的目的。
2.代理类完成的功能:
目标类中方法的调用 |
功能增强 |
3.代理模式的作用
功能增强(更常用) | 在你原有的功能上,增加了额外的功能。有了新增加的功能,故叫做功能增强。 |
控制访问 | 在代理类中控制是否可以调用目标类的方法 |
补:对一个对象进行访问控制的一个原因是只有在我们确实需要这个对象时才对它进行创建和初始化。
4.实现代理的方式
4.1.静态代理:
代理类在程序运行前就已经定义好.java 源文件,其与目标类的关系在 程序运行前就已经确立。在程序运行前代理类已经编译为.class 文件
特点: | 代理类是自己手工实现的一个java类, 表示代理类 |
同时每一个代理类要代理的目标类是确定的 | |
优点: | 实现简单,容易理解 |
缺点: | 当目标类增加了,代理类可能也需要成倍的增加。代理类数量过多,管理和维护起来都很麻烦 |
当你的接口中功能增加了,或者修改了,会影响众多的实现类,如目标类和代理类都需要修改。影响面比较多。 |
4.2动态代理
在静态代理中目标类很多时候,可以使用动态代理,避免静态代理的缺点。
优点:
1) 动态代理中目标类即使很多,代理类数量可以很少,
2)当你修改了接口中的方法时,不会影响代理类。
什么是动态代理? | 使用jdk的反射机制,创建对象的能力,创建的是代理类的对象 |
动态代理能做什么? | 可以在不改变原来目标方法功能的前提下,可以在代理中增强自己功能的代码。 |
动态代理 指代理类对象在程序运行时由 JVM 根据反射机制动态生成的。动态代理不需要定义代理类的.java 源文件。 动态代理其实就是 jdk 运行期间,动态创建 class 字节码并加载到 JVM。 动态代理的实现方式常用的有两种:使用 JDK 代理代理,与通过 CGLIB 动态代理。
jdk 动态代理是基于 Java 的反射机制实现的。使用 jdk 中接口和类实现代理对象的动态创建。 Jdk 的动态要求目标对象必须实现接口,这是 java 设计上的要求。 从 jdk1.3 以来,java 语言通过 java.lang.reflect 包提供三个类支持代理模式 Proxy, Method 和 InovcationHandler。
4.2.1 InvocationHandler 接口
InvocationHandler 接口叫做调用处理器,该接口里只有一个方法,负责完调用目标方法,并增强功能。 通 过 代 理 对 象 执 行 目 标 接 口 中 的 方 法 , 会 把 方 法 的 调 用 分 派 给 调 用 处 理 器 (InvocationHandler)的实现类,执行实现类中的 invoke()方法,我们需要把功能代理写在 invoke ()方法中 。
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
proxy:代表生成的代理对象
method:代表目标方法
args:代表目标方法的参数
4.2.2 Method 类
invoke()方法的第二个参数为 Method 类对象,该类有一个方法也叫 invoke(),可以调用目标方法。这两个 invoke()方法,虽然同名,但无关。
public Object invoke ( Object obj, Object... args)
obj:表示目标对象
args:表示目标方法参数,就是其上一层 invoke 方法的第三个参数
4.2.3 Proxy 类
通 过 JDK 的 java.lang.reflect.Proxy 类 实 现 动 态 代 理 , 会 使 用 其 静 态 方 法 newProxyInstance(),依据目标对象、业务接口及调用处理器三者,自动生成一个动态代理对 象。
public static newProxyInstance ( ClassLoader loader, Class[] interfaces, InvocationHandler handler)
loader:目标类的类加载器,通过目标对象的反射可获取
interfaces:目标类实现的接口数组,通过目标对象的反射可获取
handler:调用处理器。
4.2.4动态代理的具体实现
jdk 动态代理是代理模式的一种实现方式,其只能代理接口。
实现步骤
1、新建一个接口,作为目标接口
2、为接口创建一个实现类,是目标类
3、创建类实现 java.lang.reflect.InvocationHandler 接口,调用目标方法并增加其他功能代码
4、创建动态代理对象,使用 Proxy.newProxyInstance()方法,并把返回值强制转为接口类型。