`
rongxh2010
  • 浏览: 48845 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

从代理模式到Java反射机制

阅读更多

        代理模式,相信大多数人都非常熟悉,常见的实现方式是通过公共接口的方式,让我们的目标类和代理类实现同一接口,在代理类中调用目标类对象的方法。具体请看我另一个博客中的文章:Java的代理模式(通过公共接口实现) 。通过接口的方式,有个不好的地方,就是对每个目标类都要写一对与之相对应的接口和代理类,如果业务类很多,就是非常繁锁的工作了。

 

而加入反射机制的代理模式,可实现一个公共的代理类,省去我们不少功夫。Java的java.lang.reflect包及其子包中提供了Class、Method、Annotation等有用的类。下面,写个方法代理的类MethodProxy,实现动态地调用对象的方法。

 

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
 * 方法代理类
 * @author rongxinhua
 *
 */
public class MethodProxy {
	
	private Class clazz;	//对象所属的类
	private Object target;	//目标对象
	private Method method;	//目标方法
	private Object[] params;	//参数数组
	
	@SuppressWarnings("unchecked")
	public MethodProxy(Object target, String methodName, Object ... params) {
		rebindTarget(target, methodName, params);	//设置目标对象与方法
	}
	
	/**
	 * 重新设置目标对象与方法
	 * @param target
	 * @param methodName
	 * @param params
	 */
	public void rebindTarget(Object target, String methodName, Object ... params) {
		this.target = target;
		this.clazz = target.getClass();
		rebindMethod(methodName, params);	//设置目标方法
	}
	
	/**
	 * 重新设置目标方法
	 * @param methodName
	 * @param params
	 */
	public void rebindMethod(String methodName, Object ...params) {
		this.params = params;
		int paramLength = params.length;
		Class[] paramTypes = new Class[paramLength];
		for(int i = 0 ; i < paramLength ; i ++ ) {
			paramTypes[i] = params[i].getClass();
		}
		try {
			this.method = clazz.getMethod(methodName, paramTypes);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 动态调用已绑定的方法
	 */
	public void doMethod() {
		try {
			this.method.invoke(target, params);
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}

}

 

这样就可以实现动态地调用某个对象的某个方法了,写个测试代码如下:

public class Manager {
	
	public void say() {
		System.out.println("Nobody say nothing");
	}
	
	public void love(String boy, String girl) {
		System.out.println(boy + " love " + girl);
	}
	
}

 

 我们通过代理类来调用Manager类中的say()和love()方法,测试代码如下:

		Manager man = new Manager();	//目标对象
		MethodProxy proxy = new MethodProxy(man, "say");	//方法代理对象
		proxy.doMethod();	//调用被代理的方法
		proxy.rebindMethod("love", "Tom", "Marry");	//重新绑定方法
		proxy.doMethod();	//调用被代理的方法

 

这样就实现了动态代理调用对象的方法,上面代码输出结果就不贴出来了。如果要设置前置通知和后置通知等功能,也很容易实现,只需在“proxy.doMethod()”代码处的前面和后面设置即行。


扩展应用:我们在上面的MethodProxy类中加入以下方法:

	/**
	 * 获取方法上的注解
	 * @param anClazz 注解类
	 * @return
	 */
	public Annotation getAnnotation(Class anClazz) {
		return this.method.getAnnotation(anClazz);
	}

 

这个方法用来读取方法上的注解(Annotation),有什么用呢?我们写一个注解来测试下。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Low {
	int boyAge();	//男孩法定的谈恋爱年龄
	int girlAge();	//女孩法定的谈恋爱年龄
}

我们要引进Annotation相关的类:

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

 

我们另外写一个测试用的业务类:

public class LoveManager {
	
	@Low(boyAge=12, girlAge=10)
	public void beAbleToLove(Person boy, Person girl) {
		System.out.println(boy.getName() + " is able to love " + girl.getName());
	}
	
}

public class Person {
	private String name;
	private int age;
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
	//getter方法略
}

 

接写上例中的proxy对象测试代码:

		LoveManager loveManager = new LoveManager();
		Person boy = new Person("Tom", 13);
		Person girl = new Person("Marry", 10);
		proxy.rebindTarget(loveManager, "beAbleToLove", boy, girl);	//重新绑定对象和方法
		Low low = (Low)proxy.getAnnotation(Low.class);
		if(boy.getAge() < low.boyAge()) {
			System.out.println(boy.getName() + "还不到法定年龄,不能谈恋爱!");
		} else if(girl.getAge() < low.girlAge()) {
			System.out.println(girl.getName() + "还不到法定年龄,不能谈恋爱!");
		} else {
			proxy.doMethod();
		}

 

根据boy和girl的年龄大小,会相应地输出下列之一:

 

Tom还不到法定年龄,不能谈恋爱!

Marry还不到法定年龄,不能谈恋爱!

Tom is able to love Marry

 

这就实现了,通过Java的反射来读取Annotation的值,并根据Annotation的值,来处理业务数据有效性的判断,或者面向切面动态地注入对象,或者作日志、拦截器等等。这种用法在所多框架中都常常看到, 我们在开发自己的Java组件时,不妨也采用一下吧!

分享到:
评论
17 楼 zhaoy1233 2010-04-02  
正要学习反射机制,谢谢楼主分享。
16 楼 yewen0125 2010-04-02  
这里的反射就是反射,不要与代理扯到一起
15 楼 wkbulletin 2010-04-02  
<p>    以子类为参数定义的方法的,谁能告诉我这种情况下该如何解决?</p>
<br>
在实际项目中,只能约定熟成了.   比如Spring中的JdbcTemplate。
public List query(String sql, Object[] args, RowMapper rowMapper) throws DataAccessException  方法中rowMapping这种都是interface,在这种大环境下就可以这样。
<code>
/**
*
* 功能说明 : 对Spring JdbcTemplate 的aspect切面
*/

@Aspect
public class HistoryDataAspect implements Ordered {
protected final static Log logger = LogFactory.getLog("aspect:");

@SuppressWarnings("unchecked")
@Around("within(cn.huacai.aspect.HistoryJdbcTemplateAdapter)")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
String sql = null;
String argnames = "";
long t1 = 0;
Object result = null;
String methodName = pjp.getSignature().getName();
Object[] args = pjp.getArgs();
Class[] argsTypes = new Class[args.length];
if (methodName.startsWith("query") &amp;&amp; args.length &gt; 0) { // 只对query打头的方法进行特殊处理
sql = (String) args[0];
args[0] = HandlerGetHistoryHolder.getHistorySql(sql);
}
if (args != null) {
for (int i = 0; i  clz = args[i].getClass();
if (clz.getName().indexOf("$") == -1 || clz.getInterfaces().length == 0) {
argsTypes[i] = clz;
argnames += clz + ",";
} else {
Class&gt;[] inters = clz.getInterfaces();
argsTypes[i] = inters[0];
argnames += inters[0] + ",";
}
}
}
HistoryJdbcTemplateAdapter target = (HistoryJdbcTemplateAdapter) pjp.getTarget();
if (logger.isInfoEnabled()) {
t1 = System.currentTimeMillis();
}
try {
Method method = HistoryJdbcTemplateAdapter.adapteeClazz.getDeclaredMethod(methodName, argsTypes);
result = method.invoke(target.getJdbcTemplate(), args);
} catch (Exception e) {
e.printStackTrace();
result = pjp.proceed();
}
if (logger.isInfoEnabled()) {
if (argnames.length() &gt; 1)
argnames = argnames.substring(0, argnames.length() - 1);
String logmsg = "=========&gt;JdbcTempate." + methodName + "(" + argnames + ") ";
logger.info(logmsg + "执行时间:" + (System.currentTimeMillis() - t1) + "ms.");
if (sql != null)
logger.info(logmsg + "执行的sql:" + sql);
}
return result;
}
</code>
14 楼 CManLH 2010-04-01  
楼主的文章并没有讲到代理模式,代码很好的说了反射和注解的应用
13 楼 左岸咖啡 2010-04-01  
尽管没有很好地说明代理模式,还是感谢楼主分享。使我对java反射机制有了进一步的认识!
12 楼 dieslrae 2010-04-01  
只能说是反射,根本没看到代理模式的任何东西
11 楼 top_fly 2010-04-01  
说到反射机制就头痛,现在还是狠下心来看看,收益不少啊
10 楼 左岸咖啡 2010-04-01  
我觉得lz的代码没有体现代理模式,lz做的仅仅是提供一个封装类。
9 楼 chenyongxin 2010-04-01  
<div class="quote_title">wkbulletin 写道</div>
<div class="quote_div">
<div class="quote_title">rongxh2010 写道</div>
<div class="quote_div">
<br> /**<br>* 重新设置目标方法<br>* @param methodName<br>* @param params<br>*/<br>public void rebindMethod(String methodName, Object ...params) {<br>this.params = params;<br>int paramLength = params.length;<br>Class[] paramTypes = new Class[paramLength];<br>for(int i = 0 ; i &lt; paramLength ; i ++ ) {<br>paramTypes[i] = params[i].getClass();<br>}<br>try {<br>this.method = clazz.getMethod(methodName, paramTypes);<br>} catch (SecurityException e) {<br>e.printStackTrace();<br>} catch (NoSuchMethodException e) {<br>e.printStackTrace();<br>}<br>} </div>
<p><br>通过params 一定能得到正确的 method的 paramTypes吗?  子类可以代替父类,实现类可以代理接口作为参数.</p>
<p>这样 params[i].getClass 已经不是method meta里面的 class了. </p>
<p>只能说在通常情况下 ,能找到method</p>
</div>
<p><br>    是啊,如果class类中的方法的参数是个类,这个类存在子类,在直接调用class的时候,可以将子类作为参数出入,但通过类的反射,class.getMethod是找不到</p>
<p>    以子类为参数定义的方法的,谁能告诉我这种情况下该如何解决?</p>
8 楼 beyond.waa 2010-04-01  
正在学习java的反射机制 谢谢分享
7 楼 robin_hood 2010-04-01  
比较经典的文章
6 楼 wkbulletin 2010-04-01  
<div class="quote_title">rongxh2010 写道</div>
<div class="quote_div">
<br> /**<br> * 重新设置目标方法<br> * @param methodName<br> * @param params<br> */<br> public void rebindMethod(String methodName, Object ...params) {<br> this.params = params;<br> int paramLength = params.length;<br> Class[] paramTypes = new Class[paramLength];<br> for(int i = 0 ; i &lt; paramLength ; i ++ ) {<br> paramTypes[i] = params[i].getClass();<br> }<br> try {<br> this.method = clazz.getMethod(methodName, paramTypes);<br> } catch (SecurityException e) {<br> e.printStackTrace();<br> } catch (NoSuchMethodException e) {<br> e.printStackTrace();<br> }<br> } </div>
<p><br>通过params 一定能得到正确的 method的 paramTypes吗?  子类可以代替父类,实现类可以代理接口作为参数.</p>
<p>这样 params[i].getClass 已经不是method meta里面的 class了. </p>
<p>只能说在通常情况下 ,能找到method</p>
5 楼 Sirius.Wang 2010-04-01  
If abstract doMethod(), and let concrete class to customize doMethod(), that would be better. Otherwise, there is no way to demonstrate the concept of 'Proxy'.
4 楼 imi00 2010-04-01  
有个问题,所谓代理应该是操作类和被操作类之间的一层,代理类操作被操作类所提供的方法,加入其他行为,可以让操作类能把代理类当成原操作类无差别的使用。而这里,方法的形式不是都改变了吗?可以理解层利用反射机制的通用封装,但不是代理吧
3 楼 sidac 2010-04-01  
一直在用JDK1.4的规范,通过这篇文章看来,真需要对新的规范加紧学习,深入了解了
2 楼 EyejavaLi 2010-04-01  
不实现API中的InvocationHandler动态代理  不错

看来有必要去弄弄注解了
1 楼 heqishan 2010-03-31  
对反射机制不了解,刚好看这楼主这篇文章来学习。

相关推荐

    JAVA的反射机制与动态代理

    Java的反射机制与动态代理是Java编程中两个非常重要的高级特性,它们在许多实际场景中发挥着关键作用,如框架开发、插件系统、元数据处理等。下面将详细讲解这两个概念及其应用。 首先,Java的反射机制允许我们在...

    Java反射机制Demo

    ### Java反射机制详解 #### 一、什么是Java反射机制? Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的...

    JAVA 反射机制与动态代理ppt

    总的来说,Java反射机制和动态代理是Java平台强大而灵活的特性,它们使得Java程序能够在运行时具有更高的灵活性和可扩展性。然而,使用反射也可能带来性能开销和安全风险,因此在实际应用中需要权衡利弊,合理使用。

    Java反射机制和动态代理

    主要讲述Java反射机制与设计模式之一:代理模式的原理与应用;同时详细讲述了Java对代理模式的支持以及Java中动态代理的原理,应用与实践。

    java反射机制PPT

    Java反射机制是Java编程语言中一项强大的工具,它允许程序在运行时检查和操作类、接口、对象等的内部信息。反射机制的核心在于能够在运行时动态地获取类的信息,并且可以动态调用对象的方法,创建对象,甚至修改对象...

    java反射机制详解

    ### Java反射机制详解 #### 一、反射机制是什么 反射机制是Java编程语言的一个核心特性,它允许程序在运行时动态地获取类的信息,并且能够动态地创建对象和调用对象的方法。简单来说,反射机制使得Java程序可以...

    Java反射机制与动态代理

    Java反射机制是Java语言的一个强大特性,它允许程序在运行时检查和操作类、接口、字段和方法的信息。通过反射,我们可以在不知道具体类的情况下,动态地获取类的信息并创建对象,调用方法,修改字段值。这种机制在...

    java反射机制与动态代理

    总之,Java反射机制与动态代理为开发者提供了强大的灵活性,可以在运行时探索和修改程序行为,实现更复杂的设计模式和编程策略。然而,由于它们涉及底层操作,过度使用可能会影响程序性能和安全性,因此在实际应用中...

    Java代理模式Java动态代理

    Java动态代理是基于Java反射机制的一种实现方式,它允许我们在运行时动态地创建一个实现了某些接口的新类。Java动态代理主要用于创建代理对象,这些代理对象可以实现在特定时刻的行为增强。 **1. Java动态代理类的...

    JAVA反射机制与动态代理综合资料

    总的来说,这份“JAVA反射机制与动态代理综合资料”应该是一个全面的学习资源,涵盖了从基础概念到实际应用的多个方面,对于想要提升Java技能或深入理解J2EE开发的程序员来说,是非常宝贵的学习材料。通过学习和实践...

    Java反射机制说明

    Java反射机制是Java编程语言的核心特性之一,它赋予了程序在运行时检查和操作对象的能力。反射机制使得Java代码能够动态地获取类的信息(如类名、方法、字段),并且能够在运行时创建和调用对象的方法,这为编程带来...

    java反射机制.rar

    Java反射机制是Java编程语言中的一个强大特性,它允许程序在运行时检查和操作类、接口、字段和方法。在Java中,反射机制是通过java.lang.Class类和相关的API实现的,它为我们提供了动态访问和操作类的能力。下面将...

    java反射原理详解

    Java反射机制是Java语言的一种强大的特性,它允许程序在运行时动态地获取类的信息并操作类的对象。在Java中,反射主要涉及到`java.lang.Class`类、`java.lang.reflect`包中的类(如Constructor、Method、Field)以及...

    java反射机制学习(五):工厂模式

    在本篇“java反射机制学习(五):工厂模式”中,我们将探讨如何利用反射与工厂模式结合,提升代码的灵活性和可扩展性。 首先,工厂模式是一种常用的创建型设计模式,它提供了一种创建对象的最佳方式。传统的工厂模式...

    Java反射机制-PDF文档,详述了反射机制的原理和使用方法

    7. 动态代理:Java反射机制可以结合java.lang.reflect.Proxy类创建动态代理,实现代理模式,为接口的方法提供动态处理逻辑。 在实际开发中,反射机制常用于以下场景: - 插件化开发:通过反射加载插件中的类,实现...

    Java反射常用机制

    通过阅读《Java反射机制-较全面PPT.ppt》这样的资源,你可以更深入地理解这些概念,并学习如何在实际项目中安全有效地使用反射。这不仅有助于提高代码的灵活性,也有助于解决某些特定场景下的编程难题。在学习过程中...

    AOP动态代理(反射机制)

    6. **代理模式的其他实现**:除了Java的动态代理,还有其他实现方式,比如CGLIB库,它通过字节码生成技术创建代理对象,即使目标类没有实现接口也能进行代理。 学习AOP动态代理有助于我们理解面向切面编程的核心...

Global site tag (gtag.js) - Google Analytics