探讨Java代理模式与反射机制的实际应用
探讨Java代理模式与反射机制的实际应用(1)
Java提供了一套机制来动态执行方法和构造方法,以及数组操作等,这套机制就叫反射。而代理模式是为其他对象提供一种代理以控制对这个对象的访问,让我们的目标类和代理类实现同一接口,在代理类中调用目标类对象的方法。
反射机制是如今很多Java流行框架的实现基础,其中包括Spring、Hibernate等。如果我们将反射机制加入到Java的代理模式中,就可以实现一个公共的代理类,省去我们不少功夫。
- 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();??? ?
- ????????}??? ?????}??? ?
- ?? ?}? ?
- ?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);??? ?
- ????}??? ???????? ?
- }? ??
- 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()方法,测试代码如下:
探讨Java代理模式与反射机制的实际应用(2)
- Manager?man?=?new?Manager();????//目标对象??? ?MethodProxy?proxy?=?new?MethodProxy(man,?"say");????//方法代理对象??? ?
- proxy.doMethod();???//调用被代理的方法??? ?proxy.rebindMethod("love",?"Tom",?"Marry");?//重新绑定方法??? ?
- proxy.doMethod();???//调用被代理的方法? ??
- 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);??? ?}? ?
- ??/** ?
- ??*?获取方法上的注解 ???*?@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();????? ?}? ?
- ?@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;? ??
- 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方法略??? ?}? ?
- ?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();??? ?}? ?
- ???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(); ?
- ??}?
这就实现了,通过Java的反射机制来读取Annotation的值,并根据Annotation的值,来处理业务数据有效性的判断,或者面向切面动态地注入对象,或者作日志、拦截器等等。这种用法在所多框架中都常常看到, 我们在开发自己的Java组件时,不妨也采用一下吧!
【编辑推荐】