JAVA的动态代理
代理模式
代理模式是经常使用的java设计模式,他的特征是代理类与托付类有相同的接口,代理类主要负责为托付类预处理消息、过滤消息、把消息转发给托付类,以及事后处理消息等。代理类与托付类之间一般会存在关联关系,一个代理类的对象与一个托付类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用托付类的对象的相关方法,来提供特定的服务。
依照代理的创建时期,代理类能够分为两种。
静态代理:由程序猿创建或特定工具自己主动生成源码,再对其编译。在程序执行前,代理类的.class文件就已经存在了。
动态代理:在程序执行时,运用反射机制动态创建而成。
首先看一下静态代理:
1、Count.java:
package net.battier.dao; /** * 定义一个账户接口 * * @author Administrator * */ public interface Count { // 查看账户方法 public void queryCount(); // 改动账户方法 public void updateCount(); }2.CountImpl.java
package net.battier.dao.impl; import net.battier.dao.Count; /** * 托付类(包括业务逻辑) * * @author Administrator * */ public class CountImpl implements Count { @Override public void queryCount() { System.out.println("查看账户方法..."); } @Override public void updateCount() { System.out.println("改动账户方法..."); } }3.CountProxy.java
package net.battier.dao.impl; import net.battier.dao.Count; /** * 这是一个代理类(增强CountImpl实现类) * * @author Administrator * */ public class CountProxy implements Count { private CountImpl countImpl; /** * 覆盖默认构造器 * * @param countImpl */ public CountProxy(CountImpl countImpl) { this.countImpl = countImpl; } @Override public void queryCount() { System.out.println("事务处理之前"); // 调用托付类的方法; countImpl.queryCount(); System.out.println("事务处理之后"); } @Override public void updateCount() { System.out.println("事务处理之前"); // 调用托付类的方法; countImpl.updateCount(); System.out.println("事务处理之后"); } }
4.TestCount.java
package net.battier.test; import net.battier.dao.impl.CountImpl; import net.battier.dao.impl.CountProxy; /** *測试Count类 * * @author Administrator * */ public class TestCount { public static void main(String[] args) { CountImpl countImpl = new CountImpl(); CountProxy countProxy = new CountProxy(countImpl); countProxy.updateCount(); countProxy.queryCount(); } }观察代码能够发现每个代理类仅仅能为一个接口服务,这样一来程序开发中必定会产生过多的代理,并且,所有的代理操作除了调用的方法不一样之外,其它的操作都一样,则此时肯定是反复代码。解决这一问题最好的做法是能够通过一个代理类完毕所有的代理功能,那么此时就必须使用动态代理完毕。
再来看一下动态代理:
与静态代理类的创建不同的是生成代理的方式,这里使用了java.lang.reflect.Proxy类的newProxyInstance()方法生成一个代理,而且在生成过程中须要传入一个代理处理器,java.lang.reflect.InvocationHandler的一个实现类。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
当中生成的代理类是由Java内部运行的。
public interface InvocationHandler
InvocationHandler 是代理实例的调用处理程序 实现的接口。
每一个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。
invoke
Object invoke(Object proxy,
Method method,
Object[] args)
throws Throwable在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。
參数:
proxy - 在其上调用方法的代理实例
method - 相应于在代理实例上调用的接口方法的 Method 实例。Method 对象的声明类将是在当中声明方法的接口,该接口能够是代理类赖以继承方法的代理接口的超接口。
args - 包括传入代理实例上方法调用的參数值的对象数组,假设接口方法不使用參数,则为 null。基本类型的參数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。
返回:
从代理实例的方法调用返回的值。假设接口方法的声明返回类型是基本类型,则此方法返回的值一定是相应基本包装对象类的实例;否则,它一定是可分配到声明返回类型的类型。假设此方法返回的值为 null 而且接口方法的返回类型是基本类型,则代理实例上的方法调用将抛出 NullPointerException。否则,假设此方法返回的值与上述接口方法的声明返回类型不兼容,则代理实例上的方法调用将抛出 ClassCastException。
抛出:
Throwable - 从代理实例上的方法调用抛出的异常。该异常的类型必须能够分配到在接口方法的 throws 子句中声明的任一异常类型或未经检查的异常类型 java.lang.RuntimeException 或 java.lang.Error。假设此方法抛出经过检查的异常,该异常不可分配到在接口方法的 throws 子句中声明的任一异常类型,代理实例的方法调用将抛出包括此方法曾抛出的异常的 UndeclaredThrowableException。
public class Proxy
extends Object
implements Serializable
Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的全部动态代理类的超类。
Proxy
protected Proxy(InvocationHandler h)使用其调用处理程序的指定值从子类(通常为动态代理类)构建新的 Proxy 实例。
參数:
h - 此代理实例的调用处理程序
getProxyClass
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)
throws IllegalArgumentException返回代理类的 java.lang.Class 对象,并向其提供类载入器和接口数组。该代理类将由指定的类载入器定义,并将实现提供的全部接口。假设类载入器已经定义了具有同样排列接口的代理类,那么现有的代理类将被返回;否则,类载入器将动态生成并定义这些接口的代理类。
对能够传递给 Proxy.getProxyClass 的參数有下面几个限制:
interfaces 数组中的全部 Class 对象必须表示接口,而不能表示类或基本类型。
interfaces 数组中的两个元素不能引用同一 Class 对象。
全部接口类型的名称通过特定的类载入器必须可见。换句话说,对于类载入器 cl 和全部接口 i,下面表达式必须为 true:
Class.forName(i.getName(), false, cl) == i
全部非公共接口必须位于同一包中;否则,该代理类将不可能实现全部的接口,不管它在哪一个包中定义。
对于有同样签名的指定接口中不论什么成员方法集:
假设不论什么方法的返回类型是基本类型或 void,那么全部的方法必须具有与此同样的返回类型。
否则,该方法之中的一个必须是返回类型,它能够指派给该方法其余的全部返回类型。
得到的代理类必须不超过虚拟机在类上施加的不论什么限制。比如,虚拟机能够限制某一类实现至多 65535 的接口数;在这样的情况下,interfaces 数组的大小必须不超过 65535。
假设违反了这些限制,Proxy.getProxyClass 将抛出 IllegalArgumentException。假设 interfaces 数组參数或其不论什么元素为 null,则将抛出 NullPointerException。
注意,指定的代理接口的顺序很重要:对接口组合同样但顺序不同的代理类的两个请求会导致两个不同的代理类。
參数:
loader - 定义代理类的类载入器
interfaces - 代理类要实现的接口列表
返回:
用指定的类载入器定义的代理类,它能够实现指定的接口
抛出:
IllegalArgumentException - 假设违反传递到 getProxyClass 的參数上的不论什么限制
NullPointerException - 假设 interfaces 数组參数或其不论什么元素为 null
newProxyInstance
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException返回一个指定接口的代理类实例,该接口能够将方法调用指派到指定的调用处理程序。此方法相当于:
Proxy.getProxyClass(loader, interfaces).
getConstructor(new Class[] { InvocationHandler.class }).
newInstance(new Object[] { handler });
Proxy.newProxyInstance 抛出 IllegalArgumentException,原因与 Proxy.getProxyClass 同样。
參数:
loader - 定义代理类的类载入器
interfaces - 代理类要实现的接口列表
h - 指派方法调用的调用处理程序
返回:
一个带有代理类的指定调用处理程序的代理实例,它由指定的类载入器定义,并实现指定的接口
抛出:
IllegalArgumentException - 假设违反传递到 getProxyClass 的參数上的不论什么限制
NullPointerException - 假设 interfaces 数组參数或其不论什么元素为 null,或假设调用处理程序 h 为 null
isProxyClass
public static boolean isProxyClass(Class<?> cl)当且仅当指定的类通过 getProxyClass 方法或 newProxyInstance 方法动态生成为代理类时,返回 true。
此方法的可靠性对于使用它做出安全决策而言很重要,所以此方法的实现不应仅測试相关的类能否够扩展 Proxy。
參数:
cl - 要測试的类
返回:
如该类为代理类,则为 true,否则为 false
抛出:
NullPointerException - 假设 cl 为 null
getInvocationHandler
public static InvocationHandler getInvocationHandler(Object proxy)
throws IllegalArgumentException返回指定代理实例的调用处理程序。
參数:
proxy - 返回调用处理程序的代理实例
返回:
代理实例的调用处理程序
抛出:
IllegalArgumentException - 假设參数不是一个代理实例
动态代理类:在执行时生成的class,在其生成过程中,你必须提供一组接口给它,然后该class就声称实现了这些接口。能够把该class的实例当做这些接口中的不论什么一个来用。事实上,这个Dynamic Proxy就是一个Proxy,他不会替你做不论什么实质性的工作。在生成它的实例时,必须提供一个Handler,由它接管实际的工作。
在使用动态代理类时,必须实现InvocationHandler接口。
通过DynamicProxy,RealSubject能够在执行时动态改变,须要控制的接口Subject也能够在执行时改变,控制的方式DynamicSubject类也能够动态改变,从而实现了很灵活的动态代理关系。
动态代理
与静态代理类对比的是动态代理类,动态代理类的字节码在程序执行时由Java反射机制动态生成,无需程序猿手工编写它的源码。动态代理类不仅简化了编程工作,并且提高了软件系统的可扩展性,由于Java 反射机制能够生成随意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。
1.BookFacade.java
package net.battier.dao; public interface BookFacade { public void addBook(); }2.BookFacadeImpl.java
package net.battier.dao.impl; import net.battier.dao.BookFacade; public class BookFacadeImpl implements BookFacade { @Override public void addBook() { System.out.println("添加图书方法。。。"); } }
package net.battier.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * JDK动态代理代理类 * * @author student * */ public class BookFacadeProxy implements InvocationHandler { private Object target; /** * 绑定托付对象并返回一个代理类 * @param target * @return */ public Object bind(Object target) { this.target = target; //取得代理对象 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); //要绑定接口 } @Override /** * 调用方法 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result=null; System.out.println("事物開始"); //运行方法 result=method.invoke(target, args); System.out.println("事物结束"); return result; } }
4.TestProxy.java
package net.battier.test; import net.battier.dao.BookFacade; import net.battier.dao.impl.BookFacadeImpl; import net.battier.proxy.BookFacadeProxy; public class TestProxy { public static void main(String[] args) { BookFacadeProxy proxy = new BookFacadeProxy(); BookFacade bookProxy = (BookFacade) proxy.bind(new BookFacadeImpl()); bookProxy.addBook(); } }看完上面的代码,大致明确动态代理的含义:
A接口有c方法,类B实现A接口,原本应该是运行B类中的c方法,可如今不这样做;
我声明产生B类的代理类B',由它来冒充B类的“兄弟”并“实现”A接口,
对外界来说B'应该也有c方法,可当真正调用它的时候,
它会去运行与它关联InvocationHandler的invoke()方法,
在这种方法里面你能够做非常多事情。这样,这个请求就被“代理”到其他地方去了。