代理模式
代理模式是常见设计模式的一种, 代理模式的定义是: 为其他对象提供一种代理 https://baike.baidu.com/item / 代理 以控制对这个对象的访问.
在某些情况下, 一个对象不适合或者不能直接引用另一个对象, 而代理对象可以在客户端和目标对象之间起到中介的作用.
静态代理
理解设计模式是比较枯燥的, 所以还是以举例子的方式来进行理解,
例如: 公司开年会想找个明星来表演, 那么并不会直接联系明星 (主要还是联系不上), 而是会联系明星的经纪人, 明星就是被代理的对象, 而经纪人就是代理对象. 明星只需要准备来参加年会时应该表演什么节目就可以, 其他的出场费之类的事情就交给经纪人来处理就好了. 代理对象可以理解为被代理对象的扩展, 能做被代理对象不能做的事情, 也可以调用代理对象做事情.
那么用代码实现这个场景是什么样子的呢?
执行合作方法的接口
- /**
- * @Description: 经纪公司接口, 代理对象和被代理对象都需要实现的接口
- */
- public interface Company {
- /** 合作 */
- void cooperation();
- }
被代理对象
- /**
- * @Description: 目标对象 - 明星 (被代理对象)
- */
- public class Start implements Company {
- @Override
- public void cooperation() {
- System.out.println("is show time");
- }
- }
代理对象
- /**
- * @Description: 经纪人 (代理对象)
- */
- public class Agent implements Company {
- private Company company;
- public Agent(Company company)
- {
- this.company = company;
- }
- @Override
- public void cooperation()
- {
- System.out.println("收出场费, 化妆等等");
- company.cooperation();
- System.out.println("收拾行李, 打道回府");
- }
- }
测试类
- import org.junit.Test;
- /**
- * @Description: 测试类
- */
- public class ProxyTest {
- @Test
- public void AnnualMeeting()
- {
- // 目标对象
- Start start = new Start();
- // 构建代理对象, 生成代理关系
- Agent agent = new Agent(start);
- // 用代理对象执行被代理对象的动作
- agent.cooperation();
- }
- }
输出结果:
收出场费, 化妆等等
is show time
收拾行李, 打道回府
静态代理的特点是: 可以在目标对象实现的基础上, 增强额外的功能操作, 即扩展目标对象的功能. 有时候不方便修改别人的代码或者是引入的一个功能, 需要进行功能扩展一下才能适用于自己的业务实现, 可以使用代理模式来进行设计.
但是静态代理的实现基础是一个目标对象对应一个代理对象, 并且在编译时就已经维护好了代理关系, 如果目标对象是多个那么就会需要多个代理对象, 这样在更新目标的对象的时候还需要更新代理对象, 当代理对象持续增加时维护成本就变得非常困难.
针对于这种情况, 动态代理应运而生.
动态代理
JDK 代理
动态代理的代理对象不需要和目标对象共同实现接口, 而是利用 JDK 的 API, 动态的在内存中构建代理对象.
动态生成代理对象需要调用 JDK 中的 java.lang.reflect.Proxy 类的 newProxyInstance 方法, 这个方法需要三个参数:
- @CallerSensitive
- public static Object newProxyInstance(ClassLoader loader,]Class<?>[] interfaces,InvocationHandler h)
ClassLoader loader: 类加载器, 用来加载目标对象类, 因为是在运行时获得目标对象, 所以肯定需要用到反射.
Class<?>[] interfaces: 目标对象类实现的接口集合, 这些接口中定义目标对象可以执行的方法.
InvocationHandler h: 这个参数代表的是动态代理对象在调用方法的时候, 会将方法转发到哪一个 invocationHandler 对象身上, InvocationHandler 是个接口,
需要自己实现它, 然后定义自己的动态代理执行方法.
创建包含动态代理对象具体执行方法的实现类.
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- /**
- * @Description: 包含动态代理对象具体执行方法的实现类
- */
- public class MyInvocationHandler implements InvocationHandler {
- private Company company;
- public MyInvocationHandler(Company company)
- {
- this.company = company;
- }
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
- {
- System.out.println("收出厂费, 化妆等");
- // 具体执行方法
- Object result = method.invoke(company,args);
- System.out.println("收拾现场, 卸妆, 打道回府");
- return result;
- }
- }
测试类
- import org.junit.Test;
- import java.lang.reflect.Proxy;
- /**
- * @Description: 测试类
- */
- public class DynamicProxyTest {
- @Test
- public void AnnualMeeting()
- {
- // 创建目标对象
- Company start = new Start();
- // 创建代理对象需要执行的方法处理对象
- MyInvocationHandler myInvocationHandler = new MyInvocationHandler(start);
- // 获得目标对象的类加载器
- ClassLoader classLoader = start.getClass().getClassLoader();
- // 创建动态代理对象
- Company proxy = (Company) Proxy.newProxyInstance(classLoader,start.getClass().getInterfaces(),myInvocationHandler);
- // 用动态代理对象执行目标对象的方法
- proxy.cooperation();
- }
- }
输出结果:
收出厂费, 化妆等
is show time
收拾现场, 卸妆, 打道回府
JDK 动态代理的特点: 代理对象不需要实现接口, 但是目标对象必须实现接口.
那么如果在实际的业务中目标对象确实没有实现接口, 怎么办呢?
遇到这种情况的时候就需要时 cglib 动态代理了.
Cglib 代理
Cglib 代理, 也叫作子类代理, 它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
JDK 的动态代理有一个限制, 就是使用动态代理的对象必须实现一个或多个接口, 如果想代理没有实现接口的类, 就可以使用 Cglib 实现.
Cglib 是一个强大的高性能的代码生成包, 它可以在运行期扩展 java 类与实现 java 接口. 它广泛的被许多 AOP 的框架使用, 例如 Spring AOP 和 synaop, 为他们提供方法的 interception(拦截).
Cglib 包的底层是通过使用一个小块的字节码处理框架 ASM 来转换字节码并生成新的类. 不鼓励直接使用 ASM, 因为它要求你必须对 JVM 内部结构包括 class 文件的格式和指令集都很熟悉.
在实现 cglib 代理时需要引入 cglib 的 jar 包, 但是 spring 核心功能已经包含了 cglib 的功能, 所以引入 spring-core 的 jar 包就可以了.
需要注意的是: 代理的类不能为 final, 否则报错, 目标对象的方法如果为 final/static, 那么就不会被拦截, 即不会执行目标对象额外的业务方法.
没有实现接口的目标对象类
- /**
- * @Description: 没有经纪公司的明星, 就行像最近以个人练习生出道的蔡徐坤
- */
- public class AloneStart {
- /** 合作 */
- public void cooperation() {
- System.out.println("is show time");
- }
- }
生成 Cglib 代理对象的类
- import org.mockito.cglib.proxy.Enhancer;
- import org.mockito.cglib.proxy.MethodInterceptor;
- import org.mockito.cglib.proxy.MethodProxy;
- import java.lang.reflect.Method;
- /**
- * @Description: 生成代理对象的类
- */
- public class CglibProxy implements MethodInterceptor {
- private AloneStart aloneStart;
- public CglibProxy(AloneStart aloneStart)
- {
- this.aloneStart = aloneStart;
- }
- /**
- * 创建代理对象
- * @return
- */
- public Object getProxyInstance()
- {
- // 动态代理工具类
- Enhancer enhancer = new Enhancer();
- // 设置父类
- enhancer.setSuperclass(aloneStart.getClass());
- // 设置回调函数调用对象
- enhancer.setCallback(this);
- // 返回代理对象
- return enhancer.create();
- }
- @Override
- public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable
- {
- System.out.println("收出厂费, 化妆等");
- // 执行代理方法
- methodProxy.invokeSuper(obj,objects);
- System.out.println("卸妆, 回家");
- return null;
- }
- }
测试类
- import org.junit.Test;
- /**
- * @Description: 测试类
- */
- public class CglibProxyTest {
- @Test
- public void cglibTest()
- {
- // 创建目标对象
- AloneStart aloneStart = new AloneStart();
- // 创建代理对象
- AloneStart startProxy = (AloneStart) new CglibProxy(aloneStart).getProxyInstance();
- // 用代理对象执行目标对象的方法
- startProxy.cooperation();
- }
- }
输出结果:
收出厂费, 化妆等
is show time
卸妆, 回家
jdk 采用反射机制调用委托类的方法, 而 cglib 采用类似索引的方式直接调用委托类方法;
还有需要注意的是:
在 Spring 的 AOP 中
如果加入容器的目标对象有实现接口, 用 JDK 代理
如果目标对象没有实现接口, 用 Cglib 代理
参考:
Java 的三种代理模式: https://www.cnblogs.com/cenyu/p/6289209.html
说说代理模式: http://www.importnew.com/26116.html
来源: https://www.cnblogs.com/jimoer/p/8899395.html