设计模式之代理模式XXOO

设计模式之代理模式XXOO定义代理模式可以分为两种,一种是静态代理,一种是动态代理。静态代理:代理类一般会持有一个被代理的对象引用,且对于不关心的方法全部委托给被代理的对象处理。自己处理关心的方法。这种代理方式是死板的,它不是在运行时动态创建,它就是硬编码,你代码编译前写的是什么,编译后就是什么。换句话就是你按下CTRL+S的那一刻,就会被代理对象生成一个不可动态改变的代理类。静态代理一般对于代理的对象是单个或者多个固定的类(数量不会太多)使用。效果会比动态代理要好。动态代理:动态代理又分为JDK动

大家好,又见面了,我是你们的朋友全栈君。

定义

代理模式可以分为两种,一种是静态代理,一种是动态代理。

静态代理:

代理类一般会持有一个被代理的对象引用,且对于不关心的方法全部委托给被代理的对象处理。自己处理关心的方法。

这种代理方式是死板的,它不是在运行时动态创建,它就是硬编码,你代码编译前写的是什么,编译后就是什么。

换句话就是你按下CTRL+S的那一刻,就会被代理对象生成一个不可动态改变的代理类。

静态代理一般对于代理的对象是单个或者多个固定的类(数量不会太多)使用。效果会比动态代理要好。

动态代理:

动态代理又分为JDK动态代理以及CGLIB动态代理。

JDK动态代理是实现一个InvocationHandler接口,并且调用Proxy的静态方法去产生代理类。主要是运行时动态生成代理类,类似CURD操作添加日志、拦截等等时使用。例如:Spring AOP。

举个栗子

以数据库连接为例,静态代理。

public interface Connection extends Wrapper {

    /**
     * 创建连接
     * @return
     * @throws SQLException
     */
    Statement createStatement() throws SQLException;

    /**
     * 关闭连接
     * @throws SQLException
     */
    void close() throws SQLException;
}

 

public class DBUtil {

    private static LinkedList<Connection> connectionList = new LinkedList<Connection>();

    static{
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    private static Connection createNewConnection() throws SQLException {
        return (Connection) DriverManager.getConnection("url","username", "password");
    }

    private DBUtil(){
        if (connectionList == null || connectionList.size() == 0) {
            for (int i = 0; i < 10; i++) {
                try {
                    connectionList.add(createNewConnection());
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public Connection getConnection() throws Exception{
        if (connectionList.size() > 0) {
            //这是原有的方式,直接返回连接,这样可能会被程序员把连接给关闭掉
            //return connectionList.remove();  
            //下面是使用代理的方式,程序员再调用close时,就会归还到连接池
            return new ConnectionStaticProxy(connectionList.remove());
        }
        return null;
    }

    public void recoveryConnection(Connection connection){
        connectionList.add(connection);
    }

    public static DBUtil getInstance(){
        return DataSourceInstance.dataSource;
    }

    private static class DataSourceInstance{

        private static DBUtil dataSource = new DBUtil();

    }
}
@Slf4j
public class ConnectionStaticProxy implements Connection {

    private Connection connection;

    public ConnectionStaticProxy(Connection connection) {
        super();
        this.connection = connection;
    }

    @Override
    public Statement createStatement() throws SQLException {
        return connection.createStatement();
    }

    @Override
    public void close() throws SQLException {
        log.info("不是真正关闭连接,只是归还给连接池");
        DBUtil.getInstance().recoveryConnection(connection);
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }
}

动态代理

这个动态代理的演示只能代理Connection 这一个接口,如果出现这种情况,用静态代理会更好。

在你发现你使用静态代理的时候,需要写一大堆重复代码的时候,就请改用动态代理。

/**
 * 描述: 动态代理
 * 这里只是带来了一个类Connection
 * 注:在你发现你使用静态代理的时候,需要写一大堆重复代码的时候,就请改用动态代理
 *
 */
public class ConnectionProxy implements InvocationHandler {

    private Connection connection;

    public ConnectionProxy(Connection connection) {
        super();
        this.connection = connection;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 这里判断是Connection接口的close方法的话
        if (Connection.class.isAssignableFrom(proxy.getClass()) && method.getName().equals("close")) {
            // 我们不执行真正的close方法
            //method.invoke(connection, args);
            // 将连接归还连接池
            DBUtil.getInstance().recoveryConnection(connection);
            return null;
        }else {
            return method.invoke(connection, args);
        }
    }

    public Connection getConnectionProxy(){
        return (Connection) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Connection.class}, this);
    }
}

当我们需要代理一系列类的某一些方法,最典型的应用就是springAOP,我们需要创造出一批代理类,切入到一系列类当中的某一些方法中。下面给出一个经常使用的动态代理方式。

/**
 * 描述: 动态代理
 * 这个代理类的作用是可以代理任何类,因为它被传入的对象是Object,而不再是具体的类
 *
 * @author: yanglin
 * @Date: 2020-07-07-9:46
 * @Version: 1.0
 */
@Slf4j
public class ConnectionProxyOne implements InvocationHandler {

    private Object source;

    public ConnectionProxyOne(Object source){
        super();
        this.source = source;
    }

    public void before(){
        log.info("before 在方法前做一些事,比如打开事务");
    }

    public void after(){
        log.info("after 在方法返回前做一些事,比如提交事务");
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 假设我们切入toString方法,其他其实也是类似的,一般我们这里大部分是针对特定的方法做事情的,通常不会对类的全部方法切入
        // 比如我们常用的事务管理器,我们通常配置的就是对save,update,delete等方法才打开事务
        if (method.getName().equals("toString")) {
            before();
        }
        Object result = method.invoke(source, args);
        if (method.getName().equals("toString")) {
            after();
        }
        return result;
    }

    public Object getConnectionProxy(){
        return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Connection.class}, this);
    }
}
之前有个疑问?如果被代理的类未实现接口是否可以使用动态代理。答案是可以的。
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info("接口的方法全部变成这样了");
        // 这里source是TestClass,但是我们不能使用反射调用它的方法,像下面这样,放开这一行会抛异常
        // return method.invoke(source, args);

        /**
         * 只要你确认你传入的类包括了所有你传入的接口的方法,只是没实现这些接口而已,那么你可以在invoke中这样使用
         */

        log.info("before");
        Method sourceMethod = source.getClass().getDeclaredMethod(method.getName(), method.getParameterTypes());
        sourceMethod.setAccessible(true);
        Object result = sourceMethod.invoke(source, args);
        log.info("after");
        return result;
    }

以上,有兴趣的话可以跟下源码,理解下原理。(Proxy.newProxyInstance这是产生代理的入口)

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/160923.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Node.js中常见的异步/等待设计模式

    Node.js中常见的异步/等待设计模式

    2021年6月7日
    83
  • 史上最全设计模式导学目录(完整版)

    史上最全设计模式导学目录(完整版)圣诞献礼!2012年-2013年,Sunny在CSDN技术博客中陆续发表了100多篇与设计模式相关的文章,涵盖了七个面向对象设计原则和24个设计模式(23个GoF设计模式+简单工厂模式),为了方便大家学习,现将所有与设计模式学习相关文章的链接进行了整理,希望能给各位带来帮助!

    2022年6月14日
    26
  • JAVA设计模式之单例模式

    本文继续介绍23种设计模式系列之单例模式。概念:  java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。  单例模式有以下特点:  1、单例类只能有一个实例。  2、单例类必须自己创建自己的唯一实例。  3、单例类必须给所有其他对象提供这一实例。  单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例…

    2022年3月11日
    38
  • 十二、适配器模式——解决充电的烦恼 #和设计模式一起旅行#

    轻轻的我走了, 正如我轻轻的来; 我轻轻的招手, 作别西天的云彩。 ——徐志摩 《再别康桥》故事背景把奶茶店盘了出去,我和设计模式MM,继续上路,坐着冒着烟的飞机来到了剑桥,这里真是个美丽的地方,我用手机拍了很多的照片手机没电了,也玩的累了。找了个酒店 ,准备休息一下,然后给我的手机充充电。 才知道英国的插座都是下面这个样子:而我的…

    2022年2月27日
    43
  • 23种设计模式对应应用场景

    23种设计模式对应应用场景

    2021年10月2日
    41
  • JAVA设计模式之原型模式

    定义:用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。类型:创建类模式类图:原型模式主要用于对象的复制,它的核心是就是类图中的原型类Prototype。Prototype类需要具备以下两个条件:实现Cloneable接口。在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在ja

    2022年3月11日
    41

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号