前言
最近准备整理一下框架学习的只是, 发现不管是 RPC 框架, 还是 Spring 的框架, Mybatis 的框架都有很多地方地方用到了动态代理的方式, 例如我们强大的 Spring AOP,Mybatis 中的接口方法查询都是用到了 JDK 动态代理, 为了后期巩固知识的方便, 我希望自己能从基础入手, 真正理解框架. 也要求自己以这样的方式来记录学习 / 复习过程.
一, 代理模式
1.1 什么是代理模式?
代理模式的定义: 代理模式是指给某一个对象提供一个代理对象, 并由代理对象来控制对原对象 (被代理) 的引用. 引战的说法就是, 婚介所来介绍对象
1.3 代理模式分类
如果按照代理创建的时期来分类的话, 可以分成静态代理和动态代理.
静态代理: 由程序员创建或者特定的工具自动生成的源码, 程序运行之前, 代理类的字节码文件已经创建好了.
动态代理: 通过反射机制动态创建, 在程序运行时才创建.
二, 静态代理
实现静态代理有四个步骤:
定义业务接口
被代理的类来实现业务接口
定义代理类并实现业务接口
客户端调用
下面我们将按照这个步骤来实现一个静态代理, 仿照 AOP, 我们来实现一个记录数据库插入数据的前后日志需求.
2.1 定义业务接口
IService.java
- package com.proxy.stat;
- /**
- * created with IntelliJ IDEA.
- * packageName : com.proxy.stat
- * author : wujw
- * date : 2018/9/12 21:47
- * version : 1.0.0
- * description : 业务接口
- */
- public interface IService {
- /**
- * 将数据库插入一条数据
- * @param data data
- */
- void insert(String data);
- }
2.2 被代理类实现业务接口
ServiceImpl.java
- package com.proxy.stat;
- /**
- * created with IntelliJ IDEA.
- * packageName : com.proxy.stat
- * author : wujw
- * date : 2018/9/12 21:50
- * version : 1.0.0
- * description : 被代理类实现业务接口
- */
- public class ServiceImpl implements IService{
- public void insert(String data) {
- System.out.println("insert into database :"+ data);
- }
- }
2.3 定义代理类并实现业务接口
ServiceProxy.java
- package com.proxy.stat;
- /**
- * created with IntelliJ IDEA.
- * packageName : com.proxy.stat
- * author : wujw
- * date : 2018/9/12 21:52
- * version : 1.0.0
- * description : 代理类并实现业务接口
- */
- public class ServiceProxy implements IService{
- // 被代理对象
- private IService iService;
- // 构造方式注入
- public ServiceProxy(final IService iService) {
- this.iService = iService;
- }
- public void insert(String data) {
- System.out.println("ready to insert into database");
- iService.insert(data);
- System.out.println("insert into database success");
- }
- }
2.4 客户端调用测试
- package com.proxy.stat;
- /**
- * created with IntelliJ IDEA.
- * packageName : com.proxy.stat
- * author : wujw
- * date : 2018/9/12 21:59
- * version : 1.0.0
- * description : 客户端调用
- */
- public class StaticProxyGo {
- public static void main(String[] args) {
- IService iService = new ServiceImpl();
- // 将被代理对象注入到代理类中
- ServiceProxy proxy = new ServiceProxy(iService);
- proxy.insert("Apple 凌晨会发布什么产品呢?");
- }
- }
结果::::::::::::::::::::::::
ready to insert into database
insert into database : Apple 凌晨会发布什么产品呢?
- insert into database success
- Process finished with exit code 0
这样我们就实现了一个静态代理. 但是静态代理中每出现一个服务都需要为其去创建一个代理类, 工作量太大了.
三, 动态代理(JDK)
使用 JDK 动态代理步骤
创建被代理的接口和类
创建 InvocationHandler 接口的实现类. 在 invoke 方法中实现代理逻辑
通过 Proxy 的静态方法
newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)
创建一个代理对象.
客户端调用
3.1 创建被代理的接口和类
我们依旧使用上面提到的静态代理中的接口和类.
Iservice.java 和 ServiceImpl
3.2 创建 InvcationHandler 接口的实现类
- package com.proxy.dynamic;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- /**
- * created with IntelliJ IDEA.
- * packageName : com.proxy.dynamic
- * author : wujw
- * date : 2018/9/12 22:19
- * version : 1.0.0
- * description : TODO
- */
- public class MyInvocationHandler implements InvocationHandler {
- // 被代理的对象, Object 类型
- private Object object;
- public MyInvocationHandler(Object object){
- this.object = object;
- }
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- System.out.println("ready to insert into database");
- Object returnValue = method.invoke(object,args);
- System.out.println("insert into database success");
- return returnValue;
- }
- }
3.4 客户端调用
- package com.proxy.dynamic;
- import com.proxy.IService;
- import com.proxy.ServiceImpl;
- import java.lang.reflect.Proxy;
- /**
- * created with IntelliJ IDEA.
- * packageName : com.proxy.dynamic
- * author : wujw
- * date : 2018/9/12 22:23
- * version : 1.0.0
- * description : TODO
- */
- public class DynamicProxyGo {
- public static void main(String[] args) {
- IService iService = new ServiceImpl();
- MyInvocationHandler handler = new MyInvocationHandler(iService);
- // 第一个参数是指定代理类的类加载器(我们传入当前测试类的类加载器)
- // 第二个参数是代理类需要实现的接口(我们传入被代理类实现的接口, 这样生成的代理类和被代理类就实现了相同的接口)
- // 第三个参数是 invocation handler, 用来处理方法的调用. 这里传入我们自己实现的 handler
- IService proxyObject = (IService) Proxy.newProxyInstance(DynamicProxyGo.class.getClassLoader(),iService.getClass().getInterfaces(),handler);
- proxyObject.insert("Apple 凌晨会发布什么产品呢?");
- }
- }
结果::::::::::::::::::::::::::::::::
ready to insert into database
insert into database : Apple 凌晨会发布什么产品呢?
- insert into database success
- Process finished with exit code 0
看到结果和静态代理一致, 说明我们的实现是成功的.
JDK 来实现动态代理需要实现类通过接口业务定义方法(可看需要的参数), 那对于没有接口的类, 那我们如果实现动态代理呢? 这时候就需要 CGLIB 了. CGLIB 采用了非常底层的字节码技术, 其原理是通过字节码技术为一个类创建子类, 并在子类中采用方法拦截的技术拦截所有父类方法的调用, 顺势织入横切逻辑. 但因为采用的是继承, 所以不能对 final 修饰的类进行代理. JDK 动态代理与 CGLIB 动态代理均是实现 Spring AOP 的基础.
四, 动态代理(CGLIB)
4.1 创建 CGLIB 代理类
- package com.proxy.dynamic;
- import net.sf.cglib.proxy.Enhancer;
- import net.sf.cglib.proxy.MethodInterceptor;
- import net.sf.cglib.proxy.MethodProxy;
- import java.lang.reflect.Method;
- /**
- * created with IntelliJ IDEA.
- * packageName : com.proxy.dynamic
- * author : wujw
- * date : 2018/9/12 22:35
- * version : 1.0.0
- * description : TODO
- */
- public class CGBILProxy implements MethodInterceptor {
- // 被代理对象
- private Object object;
- public Object getInstance(final Object object){
- this.object = object;
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(this.object.getClass());
- enhancer.setCallback(this);
- return enhancer.create();
- }
- public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
- System.out.println("ready to insert into database");
- Object returnValue = methodProxy.invoke(object,objects);
- System.out.println("insert into database success");
- return returnValue;
- }
- }
4.2 客户端调用
- package com.proxy.dynamic;
- import com.proxy.ServiceImpl;
- /**
- * created with IntelliJ IDEA.
- * packageName : com.proxy.dynamic
- * author : wujw
- * date : 2018/9/12 22:46
- * version : 1.0.0
- * description : TODO
- */
- public class CGlibProxyGo {
- public static void main(String[] args) {
- // 这次我们不使用接口
- ServiceImpl iService = new ServiceImpl();
- CGBILProxy cgbilProxy = new CGBILProxy();
- ServiceImpl service = (ServiceImpl) cgbilProxy.getInstance(iService);
- service.insert("Apple 凌晨会发布什么产品呢?");
- }
- }
结果::::::::::::::::::::::::::::::::
ready to insert into database
insert into database : Apple 凌晨会发布什么产品呢?
insert into database success
自此, 我们已经实现了三类代理方式, 接下来我们将继续深究代理模式的内部代码及其常用的框架中是实现案例.
我们将在下一篇幅中记录讨论, 这里感谢前辈们的案例, 给了我很多的参考案例
来源: https://www.cnblogs.com/wujiwen/p/9638008.html