引言
动态代理是 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();
}
}
运行结果如下
每当我们想要创建代理类时,都要单独创建一个类,也就是说有多少个被代理类就会有多少个代理类,如果被代理类的数量非常多,那么开发者的工作量将非常大,而且代码也不优雅!
所以动态代理技术出现了,使得 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();
}
}
运行结果如下
如何体现它是动态代理呢
假如我们不仅想找刘德华唱歌跳舞,我们还想找周杰伦来唱歌跳舞
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();
}
}
运行结果如下
可以看到只通过 proxyFactory 对象的 getProxt 方法就生成了两个代理对象!
参考资料
https://www.cnblogs.com/xdp-gacl/p/3971367.html