引言

动态代理是 Java 技术中最重要的一点,如果不会动态代理,那么在学习 Spring、MyBatis 等框架的源码时是学不明白的!

代理的概念

动态代理简单来说就是动态的产生一个代理对象,代理对象会负责将所有的方法调用分派到委托对象上反射执行。

举一个生活中的例子:一个人还没有出名时,如果想找他唱歌跳舞,那就直接找他本人就完事了。但是当他出名后,成了明星,就不能直接找他唱歌跳舞了,得找他的经纪人来沟通。比如刘德华,他是一个非常出名的明星,会唱歌,会跳舞。如果你想找他唱歌跳舞,刘德华(被代理类)就会说,你先去找我的经纪人(代理类)吧。所以你这时只能去找经纪人了,因此这个经纪人就拦截了我们对刘德华的直接访问。

所以现实生活中的例子跟我们开发是一样的,我们在开发时,产生一个代理对象,拦截对被代理对象的访问,并做出相应的业务处理。

静态代理

说动态代理之前先说一下静态代理

我们就以刘德华为例子进行说明

创建被代理类接口

package com.test.proxy;

/**
 * <p>被代理类接口</p>
 * @author: miansen.wang
 * @date: 2019-06-07
 */
public interface Person {

	/**
	 * 唱歌
	 */
	void sing();
	
	/**
	 * 跳舞
	 */
	void dance();
}

创建被代理类

package com.test.proxy;

/**
 * <p>被代理类</p>
 * @author: miansen.wang
 * @date: 2019-06-07
 */
public class LiuDeHua implements Person {

	@Override
	public void sing() {
		System.out.println("刘德华唱歌");
	}

	@Override
	public void dance() {
		System.out.println("刘德华跳舞");
	}

}

静态创建代理类

package com.test.proxy;

/**
 * <p>代理类(刘德华的经纪人)</p>
 * @author: miansen.wang
 * @date: 2019-06-07
 */
public class LiuDeHuaProxy implements Person {

	// 被代理类对象
	private Person p;
	
	// 创建代理类对象时传入被代理类对象并完成初始化
	public LiuDeHuaProxy(Person p) {
		this.p = p;
	}
	
	@Override
	public void sing() {
		// 在刘德华唱歌之前拦截,做出相应的处理(收钱~)
		System.out.println("我是刘德华的经纪人,想要刘德华唱歌得先交 10 万块钱!");
		// 因为经纪人并不会唱歌,最后还是要让刘德华来唱歌,所以实际执行的是被代理类对象的 sing 方法
		p.sing();
	}

	@Override
	public void dance() {
		// 在刘德华跳舞之前拦截,做出相应的处理(收钱~)
		System.out.println("我是刘德华的经纪人,想要刘德华跳舞得先交 20 万块钱!");
		// 因为经纪人并不会跳舞,最后还是要让刘德华来唱歌,所以实际执行的是被代理类对象的 dance 方法
		p.dance();
	}
	
	public static void main(String[] args) {
		// 1.创建被代理类对象(刘德华)
		Person p = new LiuDeHua();
		// 2.创建代理类对象(经纪人)并将被代理类对象(刘德华)当作参数传进去
		LiuDeHuaProxy liuDeHuaProxy = new LiuDeHuaProxy(p);
		// 3.调用代理类的 sing 和 dance 方法
		liuDeHuaProxy.sing();
		liuDeHuaProxy.dance();
	}

}

测试代码

package com.test.proxy;

/**
 * <p></p>
 * @author: miansen.wang
 * @date: 2019-06-07
 */
public class LiuDeHuaProxyTest {

	public static void main(String[] args) {
		// 1.创建被代理类对象(刘德华)
		Person liuDeHua = new LiuDeHua();
		// 2.创建代理类对象(经纪人)并将被代理类对象(刘德华)当作参数传进去
		LiuDeHuaProxy liuDeHuaProxy = new LiuDeHuaProxy(liuDeHua);
		// 3.调用代理类的 sing 和 dance 方法
		liuDeHuaProxy.sing();
		liuDeHuaProxy.dance();
	}
}

运行结果如下

image

每当我们想要创建代理类时,都要单独创建一个类,也就是说有多少个被代理类就会有多少个代理类,如果被代理类的数量非常多,那么开发者的工作量将非常大,而且代码也不优雅!

所以动态代理技术出现了,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类。

动态代理

我们还是以刘德华的例子进行说明

动态创建代理类

第一步和第二步跟静态代理一样,这里就不重复写了,重点的代理类的创建我们是动态创建的

package com.test.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * <p>
 * 代理类(动态代理)
 * </p>
 * 
 * @author: miansen.wang
 * @date: 2019-06-07
 */
public class ProxyFactory {

	/**
	 * 实现了接口的被代理类对象
	 */
	private Object obj;

	/**
	 * 生成代理类对象 
	 * 这个方法有两个作用: 1.初始化被代理类对象 2.返回代理类对象
	 * 
	 * @return 代理类对象
	 */
	public Object getProxt(Object obj) {
		// 初始化被代理类对象
		this.obj = obj;

		/**
		 * 创建代理类的实例 有三个参数: 
		 * (1)loader:类加载器,代理类的类加载器跟被代理类的一样,所以通过 obj 获取被代理类的类加载器
		 * (2)interfaces:接口组,被代理类实现了哪些接口,那么代理类也要实现相同的接口,所以也通过 obj 获取被代理类实现的接口
		 * (3)h:实现了InvocationHandler 接口的调用处理器,可以单独创建一个类然后实现 InvocationHandler
		 * 接口,也可以通过内部类或者匿名内部类的形式实现
		 */
		return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),
				new ObjInterceptor());
	}

	/**
	 * 实现了InvocationHandler 接口的调用处理器,我这里通过内部类实现
	 * 
	 * @author: miansen.wang
	 * @date: 2019-06-07
	 */
	private class ObjInterceptor implements InvocationHandler {

		/**
		 * 当代理类对象调用 sing 或者 dance 方式时,都会转化为对 invoke 方法的调用。
		 * 在静态代理时,我们已经知道了代理类对象的具体方法,所以可以直接调用 但是动态代理,我们并不知道代理类对象的具体方法是什么,
		 * 所以转化为对 invoke 方法的调用。
		 * 这个方法有三个参数: 
		 * (1)proxy:把代理对象自己传递进来 
		 * (2)method:把代理对象当前调用的方法传递进来
		 * (3)args:把方法参数传递进来
		 */
		@Override
		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
			
			// 如果调用的是被代理类对象的 sing 方法
			if ("sing".equals(method.getName())) {
				System.out.println("我是经纪人,唱歌得先交 10 万块钱!");

				/**
				 * 返回值跟被代理类对象的方法的返回值一样
				 * 已经给钱了,但是经纪人不会唱歌,所以只能去找刘德华唱歌
				 */
				return method.invoke(obj, args);
			}

			// 如果调用的是被代理类对象的 dance 方法
			if ("dance".equals(method.getName())) {
				System.out.println("我是经纪人,跳舞得先交 20 万块钱!");
				
				/**
				 * 返回值跟被代理类对象的方法的返回值一样
				 * 已经给钱了,但是经纪人不会跳舞,所以只能去找刘德华跳舞
				 */
				return method.invoke(obj, args);
			}
			return null;
		}

	}
}

测试代码

package com.test.proxy;

/**
 * <p>测试动态代理</p>
 * @author: miansen.wang
 * @date: 2019-06-07
 */
public class ProxyFactoryTest {

	public static void main(String[] args) {
		// 1.创建被代理类对象
		Person liuDeHua = new LiuDeHua();
		
		ProxyFactory proxyFactory = new ProxyFactory();
		
		// 2.动态获取代理对象,这里返回的是 实现了 被代理类对象实现的接口 的代理类对象
		Object obj = proxyFactory.getProxt(liuDeHua);
		
		// 3.因为代理类对象实现了跟被代理类对象一样的接口,所以这里可以强转
		Person p = (Person)obj;
		
		// 4.当调用代理类对象的方法时,就会转到实现了 InvocationHandler 接口的实现类的 invoke 方法的调用
		p.sing();
		p.dance();
		
	}
}

运行结果如下

image

如何体现它是动态代理呢

假如我们不仅想找刘德华唱歌跳舞,我们还想找周杰伦来唱歌跳舞

package com.test.proxy;

/**
 * <p>被代理类(周杰伦)</p>
 * @author: miansen.wang
 * @date: 2019-06-07
 */
public class ZhouJieLun implements Person {

	@Override
	public void sing() {
		System.out.println("周杰伦唱歌");
	}

	@Override
	public void dance() {
		System.out.println("周杰伦跳舞");
	}

}

按照静态代理的逻辑,我们有得单独创建一个类来生成周杰伦的代理对象,但是我们有了动态代理就不用这样做了,可以直接通过 proxyFactory 对象的 getProxt 方法生成周杰伦的代理对象。

测试代码如下

package com.test.proxy;

/**
 * <p>测试动态代理</p>
 * @author: miansen.wang
 * @date: 2019-06-07
 */
public class ProxyFactoryTest {

	public static void main(String[] args) {
		// 1.创建被代理类对象
		Person liuDeHua = new LiuDeHua();
		
		ProxyFactory proxyFactory = new ProxyFactory();
		
		// 2.动态获取代理对象,这里返回的是 实现了 被代理类对象实现的接口 的代理类对象
		Object obj = proxyFactory.getProxt(liuDeHua);
		
		// 3.因为代理类对象实现了跟被代理类对象一样的接口,所以这里可以强转
		Person p = (Person)obj;
		
		// 4.当调用代理类对象的方法时,就会转到实现了 InvocationHandler 接口的实现类的 invoke 方法的调用
		p.sing();
		p.dance();
		
		// 创建周杰伦这个被代理对象
		Person zhouJieLun = new ZhouJieLun();
		// 生成周杰伦的代理对象
		Person p2 = (Person)proxyFactory.getProxt(zhouJieLun);
		// 调用代理对象的方法
		p2.sing();
		p2.dance();
	}
}

运行结果如下

image

可以看到只通过 proxyFactory 对象的 getProxt 方法就生成了两个代理对象!

参考资料

https://www.cnblogs.com/xdp-gacl/p/3971367.html

https://www.ibm.com/developerworks/cn/java/j-lo-proxy1/

原文链接:https://miansen.wang/2019/06/07/1/