JDK动态代理学习笔记

一、代理的认识

    在信息不发达的年代总有人被其他人冒名顶替,上完大学,参加工作,而被顶替的人根本不知道,周边的人也不能发现出现冒名顶替了。因此为了做到替换别人且不被发现,因此冒名者需要熟悉被顶替人的一切方法,属性。当然冒名者也有自己的一些特有方法,属性。
    我对代理模式额认识也是这样的。首先,为了混人耳目,与真实对象一致,因此代理对象内部一般需要封装真实对象,用于完成对真实对象方法的调用。这和装饰模式有点类似。

二、动态代理模式的实现思想

    冒名顶替的实现:在现实生活中,冒名顶替者一般需要把自己的一切信息都改成被顶替者,自己的姓名,年龄,经历,以及家庭关系。
     代理的实现:与冒名顶替的实现基本一致,首先需要有个冒名顶替这,也就是制造一个新的类。同时这个冒名顶替者把所有信息以及关系都改成和被顶替者一致,也就是新制造的这个类需要实现了真实对象所实现的接口,从而在外看看起来是同一对象。

三、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方法执行。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页