`

7、代理模式

阅读更多

一、代理的概念与作用简介

 

  • 生活中的代理
  • 武汉人从武汉的代理商手中买联想电脑和直接跑到北京联想总部买
  • 这两种方式都解决了买电脑的问题,但是从代理商手上买肯定好处要多点
  • 程序中的代理
  • 要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能
  • 如:异常处理,日志,计算方法的运行时间,事物管理等等,该怎么做呢?
  • 编写一个与目标类具有相同接口的代理类
  • 代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能代码
  • 如果采用工厂模式和配置文件的方式进行管理
  • 则不需要修改客户端程序,在配置文件中配置是使用目标类还是代理类
  • 这样以后很容易切换
  • 譬如:想要日志功能时就配置代理类,否则配置目标类
  • 这样增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易
  • 代理模式的作用是:为其他对象提供一种代理以控制对目标对象的访问。
  • 在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

二、代理模式一般涉及到的角色

 

  • 抽象角色:声明真实对象和代理对象的共同接口;
  • 真实角色(目标对象):代理角色所代表的真实对象,是我们最终要引用的对象。
  • 代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,
  •   同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。
  •   同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装


  •  

三、静态代理示例

//抽象角色UserManagr
public interface UserManager {

	public void save(String name,String pwd);
	public void update(String name,String pwd,String repwd);
	public void delete(int id);
	public String select(int id);
}

//真实角色:UserManagerImpl
public class UserManagerImpl implements UserManager {

	public void delete(int id) {
	System.out.println("------------UserManagerImpl.delete(id)------------");}

	public void save(String name, String pwd) {
	System.out.println("------------UserManagerImpl.save(name, pwd)------------");}

	public String select(int id) {
	System.out.println("------------UserManagerImpl.select(id)------------");
	return null;}

	public void update(String name, String pwd, String repwd) {
	System.out.println("-------UserManagerImpl.update(name, pwd,repwd)-----");}
}

//代理角色: UserManagerSPorxy
public class UserManagerSPorxy implements UserManager {

	UserManager userManager;
	public UserManagerSPorxy(UserManager userManager) {
		this.userManager = userManager;
	}
	public void delete(int id) {
		log();
		userManager.delete(id);
	}
	public void save(String name, String pwd) {
		log();
		userManager.save(name, pwd);
	}
	public String select(int id) {
		log();
		userManager.select(id);
		return null;
	}
	public void update(String name, String pwd, String repwd) {
		log();
		userManager.update(name, pwd, repwd);
	}
	public void log(){  
		System.out.println("------------write log into logfile--------------");}
	}
}

//测试程序Test
public class Test {
	public static void main(String[] args) {
		//UserManager userManager=new UserManagerImpl();

		UserManager userManager=new UserManagerSPorxy(new UserManagerImpl());
		userManager.delete(5);
		userManager.save("lkl", "123");
	}
}

 

四、静态代理的优缺点

 

优点:

  • 不需要修改目标对象就实现了功能的增加。

缺点:

  • 真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。
  • 如果事先并不知道真实角色则无法使用。
  • 一个真实角色必须对应一个 代理角色,如果大量使用会导致类的急剧膨胀;
  • 如果事先并不知道真实角色,该如何使用代理呢?这个问题可以通过Java的动态代理类来解决

五、动态代理

 

  • JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理
  • JVM生成的动态类必须实现一个或多个接口
  • 所以,JVM生成的动态类只能用作具有相同接口的目标类的代理
  • CGLIB库可以动态生成一个类的子类,
  • 一个类的子类也可以用作该类的代理,
  • 所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库
  • 代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外
  • 还可以在代理方法中的如下四个位置加上系统功能代码:
  • 调用目标方法前,后,前后,处理目标方法异常的catch块中

六、Java动态代理类位于java.lang.reflect包下

 

一般主要涉及到以下两个类:

/**
 * InvocationHandler
 * 该接口中仅定义了一个方法
 * InvocationHandler 是代理实例的调用处理程序 实现的接口。
 * 每个代理实例都具有一个关联的调用处理程序
 * 对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。 
 */
public interface InvocationHandler
{
	/**
	 * 这个抽象方法在代理类中动态实现。 
	 * @param proxy 一般是指代理类
	 * @param method 是被代理的方法
	 * @param args 该方法的参数数组
	 */
	public Object invoke(Object proxy, Method method, Object[] args)
							throws Throwable;
}

/**
 * Proxy
 * Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类
 * 作用类似于上例中的UserManagerSPorxy
 */
public class Proxy implements Serializable {

    private static final long serialVersionUID = -2222568056686623797L;

    /** prefix for all proxy class names */
    private final static String proxyClassNamePrefix = "$Proxy";

    /** parameter types of a proxy class constructor */
    private final static Class[] constructorParams =
	{ InvocationHandler.class };

    /** maps a class loader to the proxy class cache for that loader */
    private static Map loaderToCache = new WeakHashMap();

    /** marks that a particular proxy class is currently being generated */
    private static Object pendingGenerationMarker = new Object();

    /** next number to use for generation of unique proxy class names */
    private static long nextUniqueNumber = 0;
    private static Object nextUniqueNumberLock = new Object();

    /** set of all generated proxy classes, for isProxyClass implementation */
    private static Map proxyClasses =
	Collections.synchronizedMap(new WeakHashMap());

    /**
     * the invocation handler for this proxy instance.此代理实例的调用处理程序。
     * @serial
     */
    protected InvocationHandler h;

	/**
     * Prohibits instantiation.(禁止实例化)
     */
    private Proxy() {
    }

	/**
     * Constructs a new <code>Proxy</code> instance from a subclass
     * (typically, a dynamic proxy class) with the specified value
     * for its invocation handler.
	 * 使用其调用处理程序的指定值从子类(通常为动态代理类)构建新的 Proxy 实例
     *
     * @param   h the invocation handler for this proxy instance
	 * 此代理实例的调用处理程序
     */
    protected Proxy(InvocationHandler h) {
		this.h = h;
    }

	/**
	 * 返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。
	 * @param loader - 定义代理类的类加载器
	 * @param interfaces - 代理类要实现的接口列表
	 * 虚拟机可以限制某一类实现至多 65535 的接口数
	 * interfaces 数组的大小必须不超过 65535
	 * 注意,指定的代理接口的顺序非常重要:对接口组合相同但顺序不同的代理类的两个请求会导致两个不同的代理类
	 */
	public static Class<?> getProxyClass(ClassLoader loader, 
                                         Class<?>... interfaces)
				throws IllegalArgumentException{}

	/**
	 * 返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序
	 */
	public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
                               throws IllegalArgumentException{}

	//当且仅当指定的类通过 getProxyClass 方法或 newProxyInstance 方法动态生成为代理类时,返回 true
	public static boolean isProxyClass(Class<?> cl){}

	//返回指定代理实例的调用处理程序
	public static InvocationHandler getInvocationHandler(Object proxy)
                                              throws IllegalArgumentException{}
}

 

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
/**
 * 深入了解代理类
 * 获取Collection接口的代理类,并查看其构造方法与方法列表
 */
public class ProxyTest {

	public static void main(String[] args) {
		Class clazz = Proxy.getProxyClass(Collection.class.getClassLoader(),
				Collection.class);
		String name = clazz.getName();
		System.out.println(name);
		
		System.out.println("-------Constructors list-------");
		Constructor[] constructors = clazz.getConstructors();
		for(Constructor constructor : constructors)
		{
			StringBuilder sb = new StringBuilder();
			String constructorName = constructor.getName();
			sb.append(constructorName);
			sb.append('(');
			Class[] constructorParams = constructor.getParameterTypes();
			for(Class clazzParam : constructorParams)
			{
				String paramName = clazzParam.getName();
				sb.append(paramName);
				sb.append(',');
			}
			if(constructorParams != null && constructorParams.length > 0)
				sb.deleteCharAt(sb.length()-1);
			sb.append(')');
			System.out.println(sb.toString());
		}
		
		System.out.println("\n-------Methods list-------");
		Method[] methods = clazz.getMethods();
		for(Method constructor : methods)
		{
			StringBuilder sb = new StringBuilder();
			String constructorName = constructor.getName();
			sb.append(constructorName);
			sb.append('(');
			Class[] constructorParams = constructor.getParameterTypes();
			for(Class clazzParam : constructorParams)
			{
				String paramName = clazzParam.getName();
				sb.append(paramName);
				sb.append(',');
			}
			if(constructorParams != null && constructorParams.length > 0)
				sb.deleteCharAt(sb.length()-1);
			sb.append(')');
			System.out.println(sb.toString());
		}
	}
}

 

七、动态代理步骤

 

  1. 创建一个实现接口InvocationHandler的类,它必须实现invoke方法
  2. 创建被代理的类以及接口
  3. 通过Proxy的静态方法newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个代理
  4. 通过代理调用方法
  5. 静态代理类是确实存在的 动态代理是运行期动态生成的 

八、示例

 

//抽象角色UserManager.java
public interface UserManager {

	public void save(String name,String pwd);
	public void update(String name,String pwd,String repwd);
	public void delete(int id);
	public String select(int id);
}

//真实角色:UserManagerImpl.java
public class UserManagerImpl implements UserManager {

	public void delete(int id) {
	System.out.println("------------UserManagerImpl.delete(id)------------");}

	public void save(String name, String pwd) {
	System.out.println("------------UserManagerImpl.save(name, pwd)------------");}

	public String select(int id) {
	System.out.println("------------UserManagerImpl.select(id)------------");
	return null;}

	public void update(String name, String pwd, String repwd) {
	System.out.println("-------UserManagerImpl.update(name, pwd,repwd)-----");}
}

//Handler1:  LogHandler.java
public class LogHandler implements InvocationHandler {

	Object targetObject;
	public LogHandler(Object targetObject) {
		this.targetObject = targetObject;
	}

	public Object invoke(Object proxy, Method method, Object[] args)
		throws Throwable { 

		log();
		Object ret=method.invoke(this.targetObject, args);
		return ret;
	}

	public void log()
	{
		System.out.println("------------write log into logfile--------------");
	}
}

//测试程序1Test.java

public class Test {

	public static void main(String[] args) {
		UserManager um=new UserManagerImpl();
		LogHandler lh=new LogHandler(um);
		UserManager userManager=(UserManager)Proxy.newProxyInstance(um.getClass().getClassLoader(), 
																							   um.getClass().getInterfaces(), 
																								lh);
	//    UserManager userManager=new UserManagerImpl();
		userManager.delete(5);
		userManager.save("lkl", "123");
		System.out.println(userManager.select(5));
	}
}

//Handler2:  LogDproxy.java

public class LogHandler implements InvocationHandler {

	Object targetObject;
	public Object newProxy(Object targetObject)
	{
		this.targetObject=targetObject;
		return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
						  targetObject.getClass().getInterfaces(),  this);
	}
	public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {  

		log();
		Object ret=method.invoke(this.targetObject, args);
		return ret;
	}
	public void log()
	{
		System.out.println("------------write log into logfile--------------");
	}
}

//测试程序2Test.java
public class Test {
	public static void main(String[] args) {
		//UserManager userManager=new UserManagerImpl();

		UserManager userManager=(UserManager)new LogHandler().newProxy(new UserManagerImpl());
		userManager.delete(5);
		userManager.save("lkl", "123");
	}
}

 

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;

/**
 * 获取ArrayList的代理类,加入时间测试功能,并使用
 */
public class ProxyTest {

	public static void main(String[] args) {
		ArrayList al = new ArrayList();
		Collection collection = (Collection) getProxy(al);
		collection.add("nihao");
		collection.add("hello");
		collection.add(5);
		System.out.println(collection.size());
		System.out.println(al.size());
		System.out.println(collection.hashCode());
		System.out.println(al.hashCode());
	}
	private static Object getProxy(final Object targetObj)
	{
		return Proxy.newProxyInstance(targetObj.getClass().getClassLoader(),
				targetObj.getClass().getInterfaces(),
				new InvocationHandler() {
					
					@Override
					public Object invoke(Object proxy, Method method, Object[] args)
							throws Throwable {
						Long startTime = System.currentTimeMillis();
						Object retVal = method.invoke(targetObj, args);
						Long endTime = System.currentTimeMillis();
						System.out.println(method.getName()+" running time:"+(endTime-startTime));
						return retVal;
					}
				});
	}
}

 

  • 从上例结果可以看出,代理类实际上是操作的原始类对象
  • 在代理对象上的操作,是通过通过调用InvocationHandler.invoke方法,调用被代理对象的相应方法
  • 从Object继承的方法,只有hashCodeequals , toString要委托为被代理对象
  • 其他的方法代理对象自身会重写

 九、动态代理工作原理

 


十、将系统功能代码模块化,即将切面代码也改为通过参数形式提供

 

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;

/**
 * 实现简单的框架功能
 */
public class ProxyTest {

	public static void main(String[] args) {
		ArrayList al = new ArrayList();
		Collection collection = (Collection) getProxy(al,new MyAdvice());
		collection.add("nihao");
		collection.add("hello");
		collection.add(5);
		System.out.println(collection.size());
		System.out.println(al.size());
		System.out.println(collection);
		System.out.println(al);

	}
	private static Object getProxy(final Object targetObj,final Advice advice)
	{
		return Proxy.newProxyInstance(targetObj.getClass().getClassLoader(),
				targetObj.getClass().getInterfaces(),
				new InvocationHandler() {
					
					@Override
					public Object invoke(Object proxy, Method method, Object[] args)
							throws Throwable {
						advice.beforeMethod(method);
						Object retVal = method.invoke(targetObj, args);
						advice.afterMethod(method);
						return retVal;
					}
				});
	}
}

 

import java.lang.reflect.Method;

public interface Advice {
	void beforeMethod(Method method);
	void afterMethod(Method method);
}

 

import java.lang.reflect.Method;

public class MyAdvice implements Advice {

	Long beginTime,endTime;
	@Override
	public void beforeMethod(Method method) {
		beginTime = System.currentTimeMillis();
	}

	@Override
	public void afterMethod(Method method) {
		endTime = System.currentTimeMillis();
		System.out.println(method.getName()+" method running time:"+(endTime-beginTime));
	}

}

 

十一、实现AOP功能的封装与配置

 

BeanFactory工厂类

  • 负责创建目标类或代理类的实例对象,并通过配置文件实现切换
  • 其,getBean方法根据参数字符串返回一个相应的实例对象
  • 如果参数字符串在配置文件中对应的类名不是ProxyFactoryBean,则直接返回该类的实例对象
  • 否则,返回该类实例对象的getProxy方法返回的对象
  • BeanFactory的构造方法接收代表配置文件的输入流对象
  • 配置文件如下:
  • #xxx=java.util.ArrayList
  • xxx=day29.ProxyFactoryBean
  • xxx.target=java.util.ArrayList
  • xxx.advice=day29.MyAdvice

ProxyFactoryBean充当封装生成动态代理的工厂

 

编写客户端应用

  • 编写实现Advice接口的类和在配置文件中进行配置
  • 调用BeanFactory获取对象
package day29;

import java.io.InputStream;

public class Aopframework {
	public static void main(String[] args)
	{
		InputStream ips = Aopframework.class.getResourceAsStream("config.properties");
		BeanFactory beanFactory = new BeanFactory(ips );
		Object bean = beanFactory.getBean("xxx");
		System.out.println(bean.getClass().getName());
	}
}

 

package day29;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class BeanFactory {
	//pro保存配置信息
	Properties pro = new Properties();
	public BeanFactory(InputStream ips){
		try {
			pro.load(ips);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public Object getBean(String name){
		String className = pro.getProperty(name);
		Object obj = null;
		try {
			obj = Class.forName(className).newInstance();
			if(obj instanceof ProxyFactoryBean)
			{
				ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean)obj;
				Advice advice = (Advice)Class.forName(pro.getProperty(name+".advice")).newInstance();
				Object target = Class.forName(pro.getProperty(name+".target")).newInstance();
				proxyFactoryBean.setAdvice(advice);
				proxyFactoryBean.setTargetObj(target);
				return proxyFactoryBean.getProxy();
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
		return obj;
	}
}

 

package day29;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyFactoryBean {
	Object targetObj;
	Advice advice;
	public Object getTargetObj() {
		return targetObj;
	}
	public void setTargetObj(Object targetObj) {
		this.targetObj = targetObj;
	}
	public Advice getAdvice() {
		return advice;
	}
	public void setAdvice(Advice advice2) {
		this.advice = advice2;
	}
	public Object getProxy()
	{
		return Proxy.newProxyInstance(targetObj.getClass().getClassLoader(),
				targetObj.getClass().getInterfaces(),
				new InvocationHandler() {
					
					@Override
					public Object invoke(Object proxy, Method method, Object[] args)
							throws Throwable {
						advice.beforeMethod(method);
						Object retVal = method.invoke(targetObj, args);
						advice.afterMethod(method);
						return retVal;
					}
				});
	}
}

 

  • 大小: 71.2 KB
  • 大小: 37.3 KB
分享到:
评论

相关推荐

    设计模式--代理模式

    代理模式是一种常用的设计模式,它在软件开发中扮演着重要角色,允许我们通过一个代理类来控制对原对象的访问。在《设计模式:可复用面向对象软件的基础》(通常称为GoF设计模式)中,代理模式被定义为“为其他对象...

    C#设计模式—代理模式应用实例

    代理模式是其中一种,它在软件设计中扮演着重要角色。本实例将详细阐述如何在C#中运用代理模式。 代理模式的主要思想是为一个对象提供一个替身或代理,以便控制对这个对象的访问。代理对象在客户端和目标对象之间起...

    网闸代理模式配置.docx

    深信服网闸代理模式配置是一项关键的安全管理任务,它涉及到网络访问控制和数据传输的安全性。网闸,全称为“安全隔离与信息交换系统”,主要用于实现不同安全等级网络之间的安全隔离,同时满足数据交换的需求。以下...

    CAS代理模式

    CAS代理模式是CAS系统中的一个重要特性,它允许授权用户通过代理凭证来访问受保护的资源,而无需再次进行身份验证。这种模式在多服务环境中特别有用,因为用户只需登录一次,之后可以访问所有关联的服务。 在Yale ...

    代理模式java代码 Proxy(4)

    代理模式是一种设计模式,它允许我们为一个对象创建一个代理对象,这个代理对象在客户端和目标对象之间起到中介的作用,可以实现额外的功能,比如监控、权限控制、事务管理等,而客户端无需关心这些细节。...

    代理模式及动态代理资料和源代码

    代理模式是一种常用的设计模式,它在软件开发中起到了中介或者代表的作用,允许我们通过一个对象来控制对另一个对象的访问。在Java和Spring框架中,动态代理更是扮演着重要角色,尤其是在实现面向切面编程(AOP)时...

    设计模式10代理模式PPT学习教案.pptx

    设计模式之代理模式 代理模式是结构型设计模式之一,它提供了一个对象的代理对象,以控制对原对象的访问。代理对象可以在客户端和目标对象之间起到中介的作用,可以通过代理对象去掉客户不能看到的内容和服务或者...

    代理模式 Proxy Pattern

    ### 代理模式 Proxy Pattern #### 概念定义 代理模式是一种结构型设计模式,它允许程序员为某对象创建一个代理对象来控制对该对象的访问。简单来说,就是在不修改原始类的基础上,通过引入代理对象来扩展类的行为...

    4代理模式1

    代理模式是一种设计模式,它允许我们为一个对象创建一个代理对象,从而在客户端与目标对象之间插入一层间接性。这层间接性提供了多种优势,包括但不限于远程访问、延迟加载、安全控制、性能优化等。代理模式的核心...

    代理模式,JDK动态代理,SpringAOP来龙去脉

    代理模式是一种设计模式,它允许我们为一个对象创建一个代理对象,这个代理对象在客户端与目标对象之间起到中介的作用,可以增强或修改目标对象的行为,同时保持相同的接口。在Java中,我们可以使用JDK的动态代理...

    动态代理+静态代理+单利模式+定时任务的SSM+maven框架

    静态代理是传统的代理模式实现,需要为每个目标类创建一个代理类,代理类和目标类实现相同的接口。静态代理的优点是代码清晰,但缺点是当需要代理的类增多时,代理类的数量也会同步增加,代码维护成本较高。 **6. ...

    代理模式java代码 Proxy(3)

    7. **开发实践**:在实际开发中,代理模式广泛应用于远程代理(如RPC)、安全代理(控制访问权限)、缓存代理(提高性能)以及智能引用(跟踪对象使用情况)等方面。 8. **代码实现细节**:`OrderProxy`类中的构造...

    23种设计模式详解PDF

    适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 行为型模式(11): 策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、...

    socket通信NIO代理模式demo实例

    本实例"socket通信NIO代理模式demo"将展示如何利用NIO来构建一个高性能的代理服务器。 代理模式是一种设计模式,它允许我们创建一个代理对象来控制对原对象的访问。在NIO代理模式中,代理服务器作为客户端与目标...

    23种设计模式 -设计模式图解.7z

    结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、...

    JAVA动态代理模式.pdf

    Java动态代理模式是一种设计模式,它允许在运行时创建具有相同接口的新对象,该对象能够作为其他对象的代理,执行额外的任务,如日志记录、性能监控或安全控制。动态代理通常利用Java的反射API来实现,使得我们无需...

    Centos7虚拟机配置代理

    ### Centos7虚拟机配置代理 #### 一、概述 在复杂的网络环境中,为了能够连接外部网络并进行软件包的下载等操作,通常需要对Centos7虚拟机进行代理配置。若未配置代理,则虚拟机可能无法访问互联网,进而影响正常...

    java设计模式示例

    结构型模式(7种):适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式。 行为型模式(11种):策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态...

    Java 经典设计模式讲解以及项目实战

    8 代理模式 9 装饰模式 10 原型模式 11 委派模式 12 适配器模式 设计模式综合运用 1 门面+模版方法+责任链+策略 2 门面+模版方法+责任链+策略+工厂方法 3 动态代理+Spring AOP 4 责任链模式进阶 Spring Framework...

Global site tag (gtag.js) - Google Analytics