探討Java代理模式與反射機(jī)制的實(shí)際應(yīng)用
Java提供了一套機(jī)制來(lái)動(dòng)態(tài)執(zhí)行方法和構(gòu)造方法,以及數(shù)組操作等,這套機(jī)制就叫反射。而代理模式是為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn),讓我們的目標(biāo)類和代理類實(shí)現(xiàn)同一接口,在代理類中調(diào)用目標(biāo)類對(duì)象的方法。
反射機(jī)制是如今很多Java流行框架的實(shí)現(xiàn)基礎(chǔ),其中包括Spring、Hibernate等。如果我們將反射機(jī)制加入到Java的代理模式中,就可以實(shí)現(xiàn)一個(gè)公共的代理類,省去我們不少功夫。
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- /**
- * 方法代理類
- * @author rongxinhua
- *
- */
- public class MethodProxy {
- private Class clazz; //對(duì)象所屬的類
- private Object target; //目標(biāo)對(duì)象
- private Method method; //目標(biāo)方法
- private Object[] params; //參數(shù)數(shù)組
- @SuppressWarnings("unchecked")
- public MethodProxy(Object target, String methodName, Object ... params) {
- rebindTarget(target, methodName, params); //設(shè)置目標(biāo)對(duì)象與方法
- }
- /**
- * 重新設(shè)置目標(biāo)對(duì)象與方法
- * @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); //設(shè)置目標(biāo)方法
- }
- /**
- * 重新設(shè)置目標(biāo)方法
- * @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();
- }
- }
- /**
- * 動(dòng)態(tài)調(diào)用已綁定的方法
- */
- 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; //對(duì)象所屬的類
- private Object target; //目標(biāo)對(duì)象
- private Method method; //目標(biāo)方法
- private Object[] params; //參數(shù)數(shù)組
- @SuppressWarnings("unchecked")
- public MethodProxy(Object target, String methodName, Object ... params) {
- rebindTarget(target, methodName, params); //設(shè)置目標(biāo)對(duì)象與方法
- }
- /**
- * 重新設(shè)置目標(biāo)對(duì)象與方法
- * @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); //設(shè)置目標(biāo)方法
- }
- /**
- * 重新設(shè)置目標(biāo)方法
- * @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();
- }
- }
- /**
- * 動(dòng)態(tài)調(diào)用已綁定的方法
- */
- public void doMethod() {
- try {
- this.method.invoke(target, params);
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- }
- }
- }
- 這樣就可以實(shí)現(xiàn)動(dòng)態(tài)地調(diào)用某個(gè)對(duì)象的某個(gè)方法了,寫(xiě)個(gè)測(cè)試代碼如下:
- 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);
- }
- }
我們通過(guò)代理類來(lái)調(diào)用Manager類中的say()和love()方法,測(cè)試代碼如下:
- Manager man = new Manager(); //目標(biāo)對(duì)象
- MethodProxy proxy = new MethodProxy(man, "say"); //方法代理對(duì)象
- proxy.doMethod(); //調(diào)用被代理的方法
- proxy.rebindMethod("love", "Tom", "Marry"); //重新綁定方法
- proxy.doMethod(); //調(diào)用被代理的方法
- Manager man = new Manager(); //目標(biāo)對(duì)象
- MethodProxy proxy = new MethodProxy(man, "say"); //方法代理對(duì)象
- proxy.doMethod(); //調(diào)用被代理的方法
- proxy.rebindMethod("love", "Tom", "Marry"); //重新綁定方法
- proxy.doMethod(); //調(diào)用被代理的方法
#p#
這樣就實(shí)現(xiàn)了動(dòng)態(tài)代理調(diào)用對(duì)象的方法,上面代碼輸出結(jié)果就不貼出來(lái)了。如果要設(shè)置前置通知和后置通知等功能,也很容易實(shí)現(xiàn),只需在“proxy.doMethod()”代碼處的前面和后面設(shè)置即行。擴(kuò)展應(yīng)用:我們?cè)谏厦娴腗ethodProxy類中加入以下方法:
- /**
- * 獲取方法上的注解
- * @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);
- }
這個(gè)方法用來(lái)讀取方法上的注解(Annotation),有什么用呢?我們寫(xiě)一個(gè)注解來(lái)測(cè)試下。
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.METHOD)
- @interface Low {
- int boyAge();
- int girlAge();
- }
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.METHOD)
- @interface Low {
- int boyAge();
- int girlAge();
- }
我們要引進(jìn)Annotation相關(guān)的類:
- 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;
我們另外寫(xiě)一個(gè)測(cè)試用的業(yè)務(wù)類:
- 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方法略
- }
接寫(xiě)上例中的proxy對(duì)象測(cè)試代碼:
- LoveManager loveManager = new LoveManager();
- Person boy = new Person("Tom", 13);
- Person girl = new Person("Marry", 10);
- proxy.rebindTarget(loveManager, "beAbleToLove", boy, girl); //重新綁定對(duì)象和方法
- Low low = (Low)proxy.getAnnotation(Low.class);
- if(boy.getAge() < low.boyAge()) {
- System.out.println(boy.getName() + "還不到法定年齡,不能談戀愛(ài)!");
- } else if(girl.getAge() < low.girlAge()) {
- System.out.println(girl.getName() + "還不到法定年齡,不能談戀愛(ài)!");
- } 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); //重新綁定對(duì)象和方法
- Low low = (Low)proxy.getAnnotation(Low.class);
- if(boy.getAge() < low.boyAge()) {
- System.out.println(boy.getName() + "還不到法定年齡,不能談戀愛(ài)!");
- } else if(girl.getAge() < low.girlAge()) {
- System.out.println(girl.getName() + "還不到法定年齡,不能談戀愛(ài)!");
- } else {
- proxy.doMethod();
- }
這就實(shí)現(xiàn)了,通過(guò)Java的反射機(jī)制來(lái)讀取Annotation的值,并根據(jù)Annotation的值,來(lái)處理業(yè)務(wù)數(shù)據(jù)有效性的判斷,或者面向切面動(dòng)態(tài)地注入對(duì)象,或者作日志、攔截器等等。這種用法在所多框架中都常常看到, 我們?cè)陂_(kāi)發(fā)自己的Java組件時(shí),不妨也采用一下吧!
【編輯推薦】