`
lzh166
  • 浏览: 293968 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

代理模式与Java 动态代理类

阅读更多
1. 代理模式
代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式一般涉及到的角色有:
抽象角色:声明真实对象和代理对象的共同接口;
代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。
抽象角色:
abstract public class Subject    
{       
 abstract public void request();     
}  
 
真实角色:实现了Subject的request()方法。
public class RealSubject extends Subject     
{   
 public RealSubject()      
{    
   
}     
 //重写父类的方法  
 public void request() {     
 System.out.println("From real subject.");     
 }     
}
   
代理角色:
public class ProxySubject extends Subject {    
   
private RealSubject realSubject; //以真实角色作为代理角色的属性    
   
public ProxySubject() { }    
  
public void request() //该方法封装了真实对象的request方法    
   
{    
   
 preRequest();    
   
if( realSubject == null )      
{    
   
realSubject = new RealSubject();    
   
}    
   
realSubject.request(); //此处执行真实对象的request方法    
    
postRequest();    
    
}     
private void preRequest()    
{    
//something you want to do before requesting     
}      
private void postRequest()    
{     
//something you want to do after requesting      
}      
}  
 
客户端调用:
Subject sub=new ProxySubject();    
   
Sub.request(); 

由以上代码可以看出,客户实际需要调用的是RealSubject类的request()方法,现在用ProxySubject来代理RealSubject类,同样达到目的,同时还封装了其他方法(preRequest(),postRequest()),可以处理一些其他问题。

另外,如果要按照上述的方法使用代理模式,那么真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色,该如何使用代理呢?这个问题可以通过Java的动态代理类来解决。
2 .动态代理类 (重点)

Java动态代理类位于Java.lang.reflect包下,一般主要涉及到以下两个类

(1). Interface InvocationHandler:该接口中仅定义了一个方法
Object:invoke(Object obj,Method method, Object[] args)

在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。

(2).Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包含以下内容:

构造函数,估计用于给内部的h赋值
Protected Proxy(InvocationHandler h)


获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组
Static Class getProxyClass (ClassLoader loader, Class[] interfaces)


返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)。
Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)


所谓 Dynamic Proxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然啦,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。
在使用动态代理类时,我们必须实现InvocationHandler接口,以第一节中的示例为例:

抽象角色(之前是抽象类,此处应改为接口):

public interface Subject    
{    
abstract public void request();    
   
} 

具体角色RealSubject:同上;
代理角色:
import java.lang.reflect.Method;    
import java.lang.reflect.InvocationHandler;    
   
public class DynamicSubject implements InvocationHandler {    
   
private Object sub;    
   
public DynamicSubject() {    
}    
   
public DynamicSubject(Object obj) {    
sub = obj;    
}    
   
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {    
System.out.println("before calling " + method);    
method.invoke(sub,args);    
System.out.println("after calling " + method);    
return null;    
}    
}    

该代理类的内部属性为Object类,实际使用时通过该类的构造函数DynamicSubject(Object obj)对其赋值;此外,在该类还实现了invoke方法,该方法中的
method.invoke(sub,args); 

其实就是调用被代理对象的将要被执行的方法,方法参数sub是实际的被代理对象,args为执行被代理对象相应操作所需的参数。通过动态代理类,我们可以在调用之前或之后执行一些相关操作。

客户端:

import java.lang.reflect.InvocationHandler;    
import java.lang.reflect.Proxy;    
import java.lang.reflect.Constructor;    
import java.lang.reflect.Method;    
   
public class Client    
{    
static public void main(String[] args) throws Throwable    
{    
RealSubject rs = new RealSubject(); //在这里指定被代理类    
InvocationHandler ds = new DynamicSubject(rs); //初始化代理类    
Class cls = rs.getClass();    
   
//以下是分解步骤    
/*   
Class c = Proxy.getProxyClass(cls.getClassLoader(),cls.getInterfaces()) ;   
Constructor ct=c.getConstructor(new Class[]{InvocationHandler.class});   
Subject subject =(Subject) ct.newInstance(new Object[]{ds});   
*/   
   
//以下是一次性生成    
Subject subject = (Subject) Proxy.newProxyInstance(cls.getClassLoader(),    
cls.getInterfaces(),ds );    
subject.request();    
} 
  
通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实现了非常灵活的动态代理关系.

下面再来补充一下
1、InvocationHandler原理分析
Collection proxy3 = (Collection)Proxy.newProxyInstance(
				Collection.class.getClassLoader(),
				new Class[]{Collection.class}, 
				new InvocationHandler(){
					ArrayList target = new ArrayList();
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {
						long startTime = System.currentTimeMillis();
						Object retVal = method.invoke(target, args);
						long endTime = System.currentTimeMillis();
						System.out.println(method.getName()+":runtime is "+(endTime-startTime));
						return retVal;
					}
					
				});
System.out.println(proxy3.getClass().getName()); 


我们说如果我们调用代理类的一个方法,他会交给InvocationHandler的invoke方法去执行 返回的结果也是目标对象调用方法后的返回结果,那对于代理对象:
System.out.println(proxy3.getClass().getName()); 按照我们上面的理论,调用代理对象方法的时候会返回真实对象的返回结果,那应该是java.util.ArrayList
为什么会是$proxy0呢?
原因是调用代理对象的从Object继承的hashCode()  equals()  toString()这几个方法的时候才会把调用请求转发给InvocationHandler对象,而对于其他的方法
有自己的实现,所以getClass().getName()返回的是$proxy0


2、动态代理的工作原理图
(1)、客户端调用代理,代理的构造方法接受一个handler对象,客户端调用代理的各个方法,代理各个方法会把各个方法请求交给handler对象,这个handler对象又把各个请求分发给目标的相应方法



(2)、把嵌套在代理中的一些业务逻辑方法(log()方法)抽取出来,将动态代理中的“切面”问题封装到一个类中,而不要硬编码到动态代理类中,动态代理类中的target
要改成Object,以便让他更有通用性
(1)切面问题接口定义
	public interface Advice {
		public void beforeMethod(Method method);
		public void afterMethod(Method method);
	}
	(2)切面问题的具体实现
	public class MyAdvice implements Advice {
		long beginTime;
		public void afterMethod(Method method) {
			System.out.println("方法执行之后:");
			long endTime = System.currentTimeMillis();
			System.out.println(method.getName() + "方法运行时间为" + (beginTime - endTime));
			
		}

		public void beforeMethod(Method method) {
			System.out.println("方法执行之前:");
			beginTime = System.currentTimeMillis();
		}
	}
	(3)动态代理方法的封装
	private static Object getProxy(final Object target, final Advice advice) {
		Object proxy = Proxy.newProxyInstance(
						target.getClass().getClassLoader(), 
						target.getClass().getInterfaces(),
						new InvocationHandler() {
							public Object invoke(Object proxy, Method method, Object[] args)
									throws Throwable {
								
	//在方法执行之前插入自己的业务逻辑				advice.beforeMethod(method);
								Object retVal = method.invoke(target, args);
	//在方法执行之前插入自己的业务逻辑				advice.afterMethod(method);
								return retVal;
							}
						}
		);
		return proxy;
	}
	(4)测试运行
		ArrayList target = new ArrayList();
		Collection proxy = (Collection)getProxy(target, new MyAdvice());
		proxy.add("abc");


3、实现类似Spring可配置的Aop框架:

通过读取配置文件中配置。来实例化一个Bean,根据所读取到配置信息,来确认返回的是真实的一个对象,还是一个代理对象,如果我传递的这个类是个ProxyFactory类型的一个类(instanceof)则返回该类的一个代理对象,否则直接返回所指定类的一个真实的对象。在这里我创建了两个类,BeanFactory类和ProxyFactoryBean类(在这里就写个类而不去接口了,在spring中是由一个接口和实现类完成的),好了,下面把代码贴一下:

/**
 * Bean工厂
 * @author lzh
 */
public class BeanFactory {

	Properties pros = new Properties();
	public BeanFactory(){	}
	/**
	 * 通过构造方法获取配置文件中信息
	 * @param is 输入流
	 */
	public BeanFactory(InputStream is){
		try {
			pros.load(is);//读取文件信息
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	/**
	 * 得到bean的实例化对象
	 * @param name bean的名称
	 * @return  bean实例对象
	 */
	public Object getBean(String name){
		//通过配置文件得到对应的bean名称(name)所对应key的值
		String className = pros.getProperty(name);
		Object bean = null;
		try {
			//通过反射得到对应的class字节码
			Class clazz = Class.forName(className);
			bean = clazz.newInstance();//创建该字节码的实例
		} catch (Exception e) {
			e.printStackTrace();
		}
		//如果创建的这个对象是一个ProxyFactoryBean类型的,就返回代理对象,否则返回真实的对象   
		if (bean instanceof ProxyFactoryBean) {
			
			ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean) bean;
			
			Advice advice = null;
			Object target = null;
			try {
				//通过配置文件中读取对应名称的class字节码的实例(自己增加的业务逻辑和目标实例)
				advice = (Advice)Class.forName(pros.getProperty(name+".advice")).newInstance();
				target = Class.forName(pros.getProperty(name+".target")).newInstance();
			} catch (Exception e) {
				e.printStackTrace();
			}
			//将创建的实例对象设置给ProxyFactoryBean
			proxyFactoryBean.setAdvice(advice);
			proxyFactoryBean.setTarget(target);
			//调用ProxyFactoryBean类的getProxy(),得到代理实例对象
			Object proxy = proxyFactoryBean.getProxy();
			return proxy;
		}
		
		return bean;
	}
	
}

ProxyFactoryBean 类
/**
 * 充当生成代理的工厂
 * @author lzh
 *	需要为代理工厂提供目标对象和业务逻辑对象
 */
public class ProxyFactoryBean {
	
	private Advice advice;
	private Object target;
	
	public Advice getAdvice() {
		return advice;
	}

	public void setAdvice(Advice advice) {
		this.advice = advice;
	}

	public Object getTarget() {
		return target;
	}

	public void setTarget(Object target) {
		this.target = target;
	}

	/**
	 * 得到代理对象
	 * @return
	 */
	public Object getProxy() {
		Object proxy3 = Proxy.newProxyInstance(
				target.getClass().getClassLoader(),
				target.getClass().getInterfaces(),
				new InvocationHandler(){
					
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {
						advice.beforMethod(method);
						Object retVal = method.invoke(target, args);
						advice.afterMethod(method);
						return retVal;
					}
					
				});
		return proxy3;
	}
}


测试类:
	public static void main(String[] args) {
		
		InputStream is = AopFrameWorkTest.class.getResourceAsStream("config.properties");
		
		Collection c = (Collection)new BeanFactory(is).getBean("xxx");
		System.out.println(c.getClass().getName());
		//System.out.println(c.size());
		
	}

在测试之前需要说明一下,在我报下面有个配置文件需要加上,不过这里配置文件的位置是和
ProxyFactoryBean类位于同一位置的,下面贴一下配置文件的内容:
xxx=java.util.ArrayList
#xxx=cn.itcast.proxy.aopframework.ProxyFactoryBean
xxx.target=java.util.ArrayList
xxx.advice=cn.itcast.proxy.MyAdvice

以上我对java动态代理的一些小小总结,边整理,边转帖,边学习,呵呵,共同进步!


  • 大小: 49 KB
  • 大小: 15.8 KB
  • 大小: 30.3 KB
分享到:
评论

相关推荐

    对代理模式与Java动态代理类的理解

    对代理模式与Java动态代理类的理解说明

    java + 动态代理 + 动态代理实际应用场景

    2: 动态代理demo 举例实际应用场景(载入数据库驱动的时候,使用AIDL与系统Servic进行通信) 3: 动态代理使用到基础理论:ClassLoader 加载.class字节码文件得到 , Class对象, Class对象通过 newProxyInstance ...

    代理模式java代码 Proxy(5) 2个代理类

    代理模式java代码 Proxy(5) 2个代理类 开发宝典

    动态代理设计模式

    详细而又简单的讲述了java动态代理设计模式

    java中的三种代理模式

    该资源提供了三种代理模式的使用代码,其中包含每种模式的jar包、具体代码、Demo测试类,详细的注释帮助你来理解。

    java静态代理与动态代理

     代理模式是常用的Java 设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个...

    Java代理模式模板代码,包含动态代理与静态代理

    Java代理模式模板代码,包含动态代理与静态代理。 静态代理使用了传统的代理类来代理,动态代理中使用了jdk的反射原理进行代理

    java静态代理和动态代理详解

    代理类需要实现与被代理类相同的接口,并且在代理类中持有一个被代理对象的引用。当我们调用代理类的方法时,实际上会委托给被代理对象去执行。 静态代理的优点是简单易理解,开发过程比较直观。但缺点是每次添加新...

    java动态代理实现与原理详细分析.docx

     代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理...

    java设计模式【之】JDK动态代理【源码】【场景:帮爸爸买菜】.rar

    java设计模式【之】JDK动态代理...代理类,接收父亲类对象,利用反射机制,创建一个代理对象(在内存中动态创建代理对象) * Proxy.newProxyInstance (ClassLoader(类加载器),interface(接口),handler(监听处理器))

    java 设计模式 mvc模式 单例模式 代理 工厂 简单工厂

    java 设计模式 mvc模式 单例模式 代理 工厂 简单工厂

    设计模式之代理模式Java实现和类设计图

    设计模式之代理模式Java实现和类设计图,包括静态代理和动态代理

    java设计模式【之】Cglib动态代理【源码】【场景:帮爸爸买菜】

    * 可以在运行期,扩展java类与实现接口,在内存中创建一个子类对象,实现代理功能 * 底层通过字节码处理框架ASM,转换字节码并生成新的类 * 被代理类都不需要实现接口 * 代理类需要实现 MethodInterceptor(方法...

    java设计模式

    12.3.1 代理模式的优点 12.3.2 代理模式的应用 12.4 代理模式的扩展 12.4.1 普通代理 12.4.2 强制代理 12.4.3 代理是有个性的 12.4.4 虚拟代理 12.4.5 动态代理 12.5 最佳实践 第13章 原型模式 13.1 个性化电子账单 ...

    【资源免费下载】Java代码积累丨大话设计模式(Java实现版本)、线程协作

    代理模式 行为模式(类行为模式) 解释器模式 模板方法模式 行为模式(对象行为模式) 策略模式 观察者模式 状态模式 导入模式 迭代器模式 命令模式 职责链模式 进来者模式 访问者模式 数据结构 Stack - 使用泛型...

    java 23种设计模式.zip

    2.结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。 4.行为型模式:模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略...

    java常用23中设计模式

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

    cgLib与JDK动态代理的用法

    代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类...

    Java+单例+多例+工厂+枚举+动态代理===》演示

    单例设计模式案例演示 单例模式,是一种常用的软件设计模式。通过单例模式可以保证系统中,**应用该模式的这个类只有一个实例**。即一个类只有一个对象实例。 #### 单例设计模式实现步骤 ...动态代理案例演示

    proxy.rar java三种代理模式源码

    java三种代理模式的源码,包含泛型改写

Global site tag (gtag.js) - Google Analytics