一、代理的认识
在信息不发达的年代总有人被其他人冒名顶替,上完大学,参加工作,而被顶替的人根本不知道,周边的人也不能发现出现冒名顶替了。因此为了做到替换别人且不被发现,因此冒名者需要熟悉被顶替人的一切方法,属性。当然冒名者也有自己的一些特有方法,属性。
我对代理模式额认识也是这样的。首先,为了混人耳目,与真实对象一致,因此代理对象内部一般需要封装真实对象,用于完成对真实对象方法的调用。这和装饰模式有点类似。
二、动态代理模式的实现思想
冒名顶替的实现:在现实生活中,冒名顶替者一般需要把自己的一切信息都改成被顶替者,自己的姓名,年龄,经历,以及家庭关系。
代理的实现:与冒名顶替的实现基本一致,首先需要有个冒名顶替这,也就是制造一个新的类。同时这个冒名顶替者把所有信息以及关系都改成和被顶替者一致,也就是新制造的这个类需要实现了真实对象所实现的接口,从而在外看看起来是同一对象。
三、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方法执行。