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;
					}
				});
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值