由于之前对代理的理解比较模糊,这次简单回顾下代理的知识:
1,生活中的代理:购买笔记本要去代理商,这样我们不需要去厂家直接拿货的费用和麻烦,而是借用别人的手完成目标。
程序中的代理:要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能。可以编写一个目标类具有相同接口的代理类,代理类的每个方法调用,目标类的相同方法,并在调用方法时加上系统功能的代码。
2,明确两个概念:
代理对象存在的价值:主要用于拦截对真实业务对象的访问。
代理对象有什么方法:与真实对象相同,只是无业务代码。
生成某一对象的代理对象,也就是需要编写一个类用于生成代理对象;
3,代理对象:
注意两个要素:
代理谁:设计一个类变量,以及一个构造函数,记住代理类代理哪个对象。
如何生成代理对象:invoke方法,设计一个方法生成代理对象;**
4,动态代理:
Java提供了一个Proxy类,调用它的newInstance方法可以生成某个对象的代理对象。使用该方法生成代理对象时,需要三个参数:
1)生成代理对象使用哪个类加载器;
2)生成哪个对象的代理对象,通过接口指定;
3)生成的代理对象的方法里干什么事,由开发人员编写handle接口的实现来指定。
5,初学者注意:
l Proxy类负责创建代理对象时,如果指定了handler(处理器),那么不管用户调用代理对象的什么方法,该方法都是调用处理器的invoke方法。
l 由于invoke方法被调用需要三个参数:代理对象、方法、方法的参数,因此不管代理对象哪个方法调用处理器的invoke方法,都必须把自己所在的对象、自己(调用invoke方法的方法)、方法的参数传递进来。
invoke指定是哪个对象,什么方法,该方法的参数
6,动态代理的应用:
l 在动态代理技术里,由于不管用户调用代理对象的什么方法,都是调用开发人员编写的处理器的invoke方法(这相当于invoke方法拦截到了代理对象的方法调用)。
l 并且,开发人员通过invoke方法的参数,还可以在拦截的同时,知道用户调用的是什么方法,因此利用这两个特性,就可以实现一些特殊需求,例如:拦截用户的访问请求,以检查用户是否有访问权限、动态为某个对象添加额外的功能。
7,企业级案例:
全站字符编码过滤(request代理);全站压缩流输出(response代理);自定义数据库连接池(connection代理);
8,AOP的切面编程的理解:个人:日志,事务,安全等操作是很多类具有的,所以将其抽取出来,提供出公共的接口让它们调用,其它代码放在这个切面的周围。这里采用代理,是因为类主体可能不一样,这个类就是代理类。
9,动态代理技术:因为要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是很麻烦的工作。所以JVM可以在运行时动态生成类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
10,JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collection; /** * 写一个ArrayList类的代理,实现和ArrayList中完全相同的功能,并可以计算每个方法运行的时间。*/ @SuppressWarnings("unchecked") public class Test4 { public static void main(String[] args) throws Exception{ //使用代理定义拦截加载器,接口,拦截器 Collection coll = (Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(),new Class[]{Collection.class}, new InvocationHandler() { //保存操作的对象 ArrayList target = new ArrayList(); @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Long beginTime = System.currentTimeMillis(); Object retVal = method.invoke(target, args); Long endTime = System.currentTimeMillis(); System.out.println(method.getName()+" Method running time is :" +(endTime - beginTime )); System.out.println("return "+retVal); return retVal; } }); //每次调用代理类的方法都会到invoke里面执行里面的代码 coll.add("asdsa"); coll.add("sfsdfds"); coll.add("sdfsdf"); System.out.println(coll.size()); } }