首页 > 编程知识 正文

设计模式之代理模式的架构,设计模式 中介者模式

时间:2023-05-04 23:00:52 阅读:261206 作者:805

定义

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

静态代理:

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

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

换句话就是你按下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(); }} @Slf4jpublic 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 */@Slf4jpublic 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这是产生代理的入口)

 

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。