设计模式之代理模式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这是产生代理的入口)

 

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

发表回复

您的电子邮箱地址不会被公开。

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