【java_设计模式】代理模式 jdk动态代理 三种业务实现

学习路径:https://coding.imooc.com/learn/list/270.html

前言

Spring 的AOP实质就是动态代理模式

相关设计模式

静态代理
静态代理模式下的被代理类没有使用多态。动态代理借助JDK的InvocationHandler接口实现多态,从而完成动态绑定实例化的类型

应用场景

1. 三层架构中代理ServiceImpl —— 新增代理类实现
/**
 * 只展示应用层代码, 下面会具体分析
 */
public static void main(String[] args) {
        IOrderService orderServiceDynamicProxy = (IOrderService) new OrderServiceDynamicProxy(
                new OrderServiceImpl() ).bind();

        // 数据准备
        Order order = new Order();order.setUserId(2);

        // 使用动态代理存数据
        orderServiceDynamicProxy.saveOrder(order);
    }
  • 实现
    实现InvacationHandler改写invoke方法,引入Proxy类的newProxyInstance方法,下面面会分析这个案例
2. Filter拦截敏感词 —— 匿名内部类实现
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        // todo 需要增强 request 的 getParameter()方法
        // 代理的是request对象
        HttpServletRequest proxyInstance = (HttpServletRequest) Proxy.newProxyInstance(
                req.getClass().getClassLoader(),
                new Class[]{HttpServletRequest.class},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if (method.getName().equals("getParameter")) {
                            String value = (String) method.invoke(req, args); // request是这个接口的实现类对象

                            // 去除所有空格, 注意返回值
                            value = value.replace(" ", "");
                            // 将脏话按照长度替换成*号, list是存储脏话的集合
                            for (String s : list) {
                                if (value.contains(s)) {
                                    StringBuilder temp = new StringBuilder();
                                    for (int i = 0; i < s.length(); i++) {
                                        temp.append("*");
                                    }
                                    value = value.replace(s, temp.toString());
                                }
                            }
                            return value;
                        }
                        return method.invoke(req, args);
                    }
                });

        // todo 一定要注意将代理类传进来
        chain.doFilter(proxyInstance, resp);
    }
3. 增强自定义数据库连接池Connection的close() —— 普通内部类实现
  /**
   * 只保留动态代理相关的代码
   */
  public class ConnectionPool {
    // 创建Connection的方法
    public Connection createConnection() {
        Connection proxyConnection = null;
        try {
            // todo 与JDBCUtil的唯一耦合,分离了JDBCUtil的职责
            Connection connection = JDBCUtil.getConnection();
            proxyConnection = (Connection) Proxy.newProxyInstance(
                    ConnectionPool.class.getClassLoader(),
                    new Class[]{Connection.class}, // 具体要代理的是哪个接口的实现类
                    new ConnectionHandler(connection) // 传入代理类
            );
        } catch (SQLException e) {
            e.printStackTrace();
        }
        curCount++;
        return proxyConnection;
    }

    /**
     * 内置被代理对象的封装类
     */
    class ConnectionHandler implements InvocationHandler {
        // 在内部维护一个需要被增强的对象(被代理对象)
        private Connection connection;

        public ConnectionHandler(Connection connection) {
            this.connection = connection;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 先得到当前调用的方法的方法名
            String methodName = method.getName();
            // 需要增强的方法,进行增强
            if ("close".equalsIgnoreCase(methodName)) {
                // todo 可以强转的内在原因
                // todo proxy 通过 newInstance() 传入的 interfaces[] 拼接处 Proxy implements Connection
                pool.addLast((Connection) proxy);
            } else { // 其他方法正常调用
                return method.invoke(connection, args);
            }
            return null;
        }
    }
}

三层架构中代理ServiceImpl 原理解析

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
新增代理类
/**
 *  两个关键方法
 * @see InvocationHandler#invoke(Object, Method, Object[]) 
 * @see Proxy#newProxyInstance(ClassLoader, Class[], InvocationHandler) 
 */
public class OrderServiceDynamicProxy implements InvocationHandler {

    // 被代理的对象,抽象成Object类
    private Object target;

    /**
     * 构造方法初始化成员变量
     * @param target 需要代理的类(接口的实现类)
     */
    public OrderServiceDynamicProxy(Object target) {
        this.target = target;
    }

    /**
     * 核心1:
     *      动态代理使用ClassLoader和反射生成一个class文件
     *      该class文件是Proxy类型,存在JVM中,可以在实例化过程中动态绑定类型
     *      也就是动态实现的核心
     *
     *      【动态的实现】cls.getInterfaces() 获取被代理类的接口而不是直接获取该代理类
     *      接口 ->  能约束返回值的取值范围,但是这里不能写死,要交给上层强转
     *
     * @return Object 类型 -- 让上层决定
     */
    public Object bind(){
        // 获取代理类的class文件
        Class cls = target.getClass();
        return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(),this);
    }

    /**
     * 核心2:
     *      当Proxy类型被实例化动态绑定后,调用绑定完成的类的方法时,就触发invoke()
     *      通过反射最终调用的是被代理类的方法
     * @param proxy   代理
     * @param method  代理的方法
     * @param args    代理的方法所要传入的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object argObject = args[0];
        beforeMethod(argObject);
        // 使用反射得到的方法和参数调用目标方法
        Object object = method.invoke(target, args);
        afterMethod();
        return object;
    }

    /**
     * 以下跟静态不同的是,可以设置不同类的实例进该方法的行为
     * @param obj
     */
    private void beforeMethod(Object obj){
        int userId = 0;
        System.out.println("动态代理 before core");

        // 这边还可以设置很多if判断,或者switch语句对入参类进行判断,使该前置方法不再单一处理一个类
        if(obj instanceof Order){
            Order order =(Order)obj;
            userId = order.getUserId();
        }
        int dbRouter = userId % 2;
        System.out.println("静态代理分配到【db" + dbRouter +"】处理数据");
        /**
         *  负载均衡的逻辑,与代理模式的设计模式无关
         *  todo 设置dataSource;
         */
        DataSourceContextHolder.setDBType("db" + String.valueOf(dbRouter));
    }

    private void afterMethod() {
        System.out.println("动态代理 after code");
    }

}
方法细节: InvocationHandler#invoke 与 Proxy#newProxyInstance
// debug 看看Proxy#newProxyInstance的返回值,是个很诡异的$Proxy0,源码上来看这个$Proxy0是动态生成的,这个类的作用看下文
return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(),this);
// 代理类声明
public class OrderServiceDynamicProxy implements InvocationHandler

// 代理类中的成员变量 -- 代理类的声明
private Object target;

// 有声明可知,InvocationHandler用作生成$Proxy0的参数,其invoke方法作用见下文
public Object bind(){
        // 获取代理类的class文件
        Class cls = target.getClass();
        return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(),this);
}

在这里插入图片描述
关于invoke为什么在saveOrder()后自动触发,与$Proxy0有关。 相关的内容参考:

https://www.jianshu.com/p/89546eff3cd0

以上参考内容在$Proxy0这个类中找到了答案,必须实现invoke方法是为了 $Proxy0顺利解析被代理方法的参数和方法
在这里插入图片描述

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object argObject = args[0];
        beforeMethod(argObject);
        // 使用反射得到的方法和参数调用目标方法
        Object object = method.invoke(target, args);
        afterMethod();
        return object;
}

debug获取JVM生成的$Proxy

详细分析$Proxy0的生成源码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值