一、代理的认识
在信息不发达的年代总有人被其他人冒名顶替,上完大学,参加工作,而被顶替的人根本不知道,周边的人也不能发现出现冒名顶替了。因此为了做到替换别人且不被发现,因此冒名者需要熟悉被顶替人的一切方法,属性。当然冒名者也有自己的一些特有方法,属性。
我对代理模式额认识也是这样的。首先,为了混人耳目,与真实对象一致,因此代理对象内部一般需要封装真实对象,用于完成对真实对象方法的调用。这和装饰模式有点类似。
二、动态代理模式的实现思想
冒名顶替的实现:在现实生活中,冒名顶替者一般需要把自己的一切信息都改成被顶替者,自己的姓名,年龄,经历,以及家庭关系。
代理的实现:与冒名顶替的实现基本一致,首先需要有个冒名顶替这,也就是制造一个新的类。同时这个冒名顶替者把所有信息以及关系都改成和被顶替者一致,也就是新制造的这个类需要实现了真实对象所实现的接口,从而在外看看起来是同一对象。
三、JDK动态代理工具
既然是偷偷冒名顶替别人,那么我们需要确定两件事:
1、我们要顶替谁,也就是我们需要对哪个真实对象生成代理对象
2、为什么我们要顶替他。当然是为了,在他的方法执行前,执行后我们可以做一些手脚。
有了这两个目的,那么需要一个接口 invocationhandler(interface)和一个类proxy(class)来帮助我们。
1、proxy类帮助我们冒名顶替,帮助我们生成代理对象。
Proxy类中有一个静态方法NewProxyInstance(loader, interfaces, h),他可以帮助我们生成代理对象,这里有三个参数,第一个参数classloader是指真实对象的类加载器,第二个参数interface是指真实对象的类所实现的口数组,第三个参数h是invocationhandler,是代理真实对象的调用处理器。
2、
invocationhandler(interface) 这接口中只有一个方法invok();这个方法就是我们要替换真实对象的方法,当代理对象调用方法时,都会进入invoke()方法中,因此我们可以在invoke()方法中加入自己需要的功能。
四、JDK动态代理的具体使用
由上我们知道真实对象所在的类必须实现某些接口,因此在这里我们给一个IUser接口,该接口中只有一个say()方法
public interface IUser { void say(); }
然后在写个User类实现该接口,我们就对User实例对象进行代理
public class User implements IUser { @Override public void say() { System.out.println("真实对象方法正在执行中"); } }
对象准备完成后,我们准备对User类的实例对象进行代理。代理分为两部分,1、生成代理对象。2、invoke()方法override
public class MyProxy implements InvocationHandler { private Object user =null; /* * 自定义一个方法将user对象传入 * 通过Proxy类生成代理对象 * 方法返回值为一个objcet的代理对象 * */ public Object getProxy(Object user) { this.user=user; return Proxy.newProxyInstance(user.getClass().getClassLoader(), user.getClass().getInterfaces(), this); } /* * Override invoke()方法 * */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("代理方法正在运行"); System.out.println("调用真实对象方法之前"); Object object = method.invoke(user, args); System.out.println("调用真实对象方法之后"); return object; } }
invoke()方法中三个参数详解,第一个参数Object proxy,是指代理对象。第二个参数是当前调用方法中的实例对象,在本例中指say()方法。第三个参数Object[] args,是method中的参数数组。
完成代理对象的生成,和逻辑方法的覆写后,测试一下
public void tset() { MyProxy myProxy = new MyProxy(); IUser proxy_1 = (IUser)myProxy.getProxy(new User()); proxy_1.say(); }
千万注意:代理对象是挂靠在真实对象所在类实现的某个接口上的,所以,在对代理对象强制类型转换时一定是接口。
IUser proxy_1 = (IUser)myProxy.getProxy(new User());
执行结果如下:
代理方法正在运行 调用真实对象方法之前 真实对象方法正在执行中 调用真实对象方法之后
可见,代理对象调用say()方法,实际上是调用了invoke()方法。完成对user对象的代理
五、CGLIB动态代理
1、CGLIB动态代理认识
有时候我们找不到接口,不能由JDK动态代理生成一个接口对象,这时,我们提供另外一种不需要接口的动态代理方法CGLIB动态代理。CGLIB动态代理的核心是生成一个真实对象子类的代理对象。
2、动态代理具体实例
(1)准备真实对象
public class User { public void say() { System.out.println("真实对象方法正在执行中"); } }
(2)生成子类代理对象
public class ProxyFactory implements MethodInterceptor { //目标对象 private Object target; public ProxyFactory(Object target) { this.target = target; } //生成代理对象 public Object getProxyInstance() { //工具对象 Enhancer enhancer = new Enhancer(); //设置父类 enhancer.setSuperclass(target.getClass()); //设置回调函数,执行target方法时会出发拦截方法interceptor() enhancer.setCallback(this); //创建代理对象 return enhancer.create(); } @Override public Object intercept(Object object, Method method, Object[] arg, MethodProxy methodProxy) throws Throwable { System.out.println("事物开启"); //真实对象方法调用 Object resultObjcet = method.invoke(target, arg); System.out.println("事物结束"); return resultObjcet; } }
(3)测试类
@Test public void proxyTest() { User user = new User(); User obj = (User)new ProxyFactory(user).getProxyInstance(); obj.say(); }
(4)测试结果
事物开启 真实对象方法正在执行中 事物结束使用CBLIB动态代理,需要使用enhancer工具对象,通过该对象setSuperClass方法将真实对象所在类设置为父类,通过setCallBanck方法,设置回调函数,也就是当需要执行真实对象方法时会触发interceptor方法执行。