第一篇:Java回顧之I/O
第二篇:Java回顧之網絡通信
第三篇:Java回顧之多線程
第四篇:Java回顧之多線程同步
第五篇:Java回顧之集合
第六篇:Java回顧之序列化
在這一篇文章里,我們關注反射及其相關話題。
反射可以幫助我們查看指定類型中的信息、創建類型的實例,調用類型的方法。我們平時使用框架,例如Spring、EJB、Hibernate等都大量的使用了反射技術。
反射簡單示例
下面來演示反射相關的基本操作
首先是基礎代碼,我們定義一個接口及其實現,作為我們反射操作的目標:
1 interface HelloWorldService 2 { 3 void sayHello(String name); 4 } 5 6 class MyHelloWorld implements HelloWorldService 7 { 8 public String name; 9 10 11 public void sayHello(String name) 12 { 13 System.out.println("Hello " + name + "."); 14 } 15 16 public void setName(String name) { 17 this.name = name; 18 } 19 20 public String getName() { 21 return name; 22 } 23 }
獲取方法及字段信息
下面的代碼會輸出給定類型中的方法和字段的聲明信息:
1 private static void printClassTypeInfo(String type) throws ClassNotFoundException 2 { 3 Class classType = Class.forName(type); 4 Method[] methods = classType.getDeclaredMethods(); 5 System.out.println("Methods info as below:"); 6 for(Method method : methods) 7 { 8 System.out.println(method.toGenericString()); 9 } 10 Field[] fields = classType.getFields(); 11 System.out.println("Fields info as below:"); 12 for (Field field : fields) 13 { 14 System.out.println(field.toGenericString()); 15 } 16 }
在使用反射時,我們一般會使用java.lang.reflect包中的內容。
然后我們調用下面的代碼:
1 printClassTypeInfo("sample.reflection.MyHelloWorld");
輸出結果如下:
Methods info as below: public void sample.reflection.MyHelloWorld.sayHello(java.lang.String) public java.lang.String sample.reflection.MyHelloWorld.getName() public void sample.reflection.MyHelloWorld.setName(java.lang.String) Fields info as below: public java.lang.String sample.reflection.MyHelloWorld.name
實例化對象
我們可以使用class.netInstance的方式來創建一個對象,代碼如下:
1 private static void createInstanceTest() throws ClassNotFoundException, InstantiationException, IllegalAccessException 2 { 3 Class classType = Class.forName("sample.reflection.MyHelloWorld"); 4 MyHelloWorld hello = (MyHelloWorld)classType.newInstance(); 5 hello.sayHello("Zhang San"); 6 }
輸出結果:
Hello Zhang San.
調用對象的方法
我們可以通過方法的名稱以及參數類型構建一個Method實例,然后調用Method的invoke方法,來觸發方法。
示例代碼如下:
1 private static void invokeMethodTest() throws InstantiationException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException 2 { 3 Class classType = Class.forName("sample.reflection.MyHelloWorld"); 4 MyHelloWorld hello = (MyHelloWorld)classType.newInstance(); 5 Method method = classType.getMethod("sayHello", new Class[]{String.class}); 6 method.invoke(hello, new Object[]{"Zhang San"}); 7 }
輸出結果同上。
修改字段的值
和C#不同,Java中一般使用setxxx和getxxx顯示為屬性賦值,因此Java中並沒有Property類型,而是有Field類型。
我們可以對Field的值進行修改,代碼如下:
1 private static void setFieldTest() throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException 2 { 3 Class classType = Class.forName("sample.reflection.MyHelloWorld"); 4 MyHelloWorld hello = (MyHelloWorld)classType.newInstance(); 5 System.out.println("name is " + hello.name); 6 Field field = classType.getField("name"); 7 field.set(hello, "Zhang San"); 8 System.out.println("name is " + hello.name); 9 }
執行結果如下:
name is null name is Zhang San
可以看出,我們成功的修改了name的值。
Annotation探索
一開始我們提到,反射是很多技術的基礎,Annotation就是這樣的,我們可以把Annotation看做是C#中的Attribute,它可以對類型、方法、屬性、字段、方法參數等信息進行修飾。我們可以使用“@+Annotation名”的方式來使用Annotation。
Annotation基本操作
來看下面的代碼,我們定義了基於Type、Method、Parameter和Field上面的Annotation示例:
1 @Target(ElementType.TYPE) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Documented 4 @interface ClassAnnotation 5 { 6 public String value(); 7 } 8 9 @Target(ElementType.METHOD) 10 @Retention(RetentionPolicy.RUNTIME) 11 @Documented 12 @interface MethodAnnotation 13 { 14 public String methodName(); 15 public String returnType(); 16 } 17 18 @Target(ElementType.PARAMETER) 19 @Retention(RetentionPolicy.RUNTIME) 20 @Documented 21 @interface ParameterAnnotation 22 { 23 public String value(); 24 } 25 26 @Target(ElementType.FIELD) 27 @Retention(RetentionPolicy.RUNTIME) 28 @Documented 29 @interface FieldAnnotation 30 { 31 public String value(); 32 }
接着,我們定義了一個MyClass類型,使用了上述的Annotation:
1 @ClassAnnotation("這是作用在類型上的Annotation") 2 class MyClass 3 { 4 @MethodAnnotation(methodName="printInfo", returnType="void") 5 public void printInfo(String info) 6 { 7 System.out.println(info); 8 } 9 10 @MethodAnnotation(methodName="printError", returnType="void") 11 public void printError(@ParameterAnnotation("這是作用在參數上的Annotation")String error) 12 { 13 System.err.println(error); 14 } 15 16 @FieldAnnotation("這是作用在字段上的Annotation") 17 public int count; 18 }
對於使用了Annotation,我們可以獲取其中的信息,下面兩種方式都可以獲取Annotation,第一種方式是通過反射遍歷類型及其方法、字段,一一讀取Annotation信息;第二種方式是讀取指定類型的Annotation:

1 private static void annotationTest1() 2 { 3 MyClass temp = new MyClass(); 4 5 Annotation[] annotations = temp.getClass().getAnnotations(); 6 for(Annotation a : annotations) 7 { 8 System.out.println(a.toString()); 9 } 10 11 Method[] methods = temp.getClass().getDeclaredMethods(); 12 for(Method method : methods) 13 { 14 annotations = method.getAnnotations(); 15 for(Annotation a : annotations) 16 { 17 System.out.println(a.toString()); 18 } 19 Annotation[][] paraAnnotations = method.getParameterAnnotations(); 20 for(int i = 0; i < paraAnnotations.length; i++) 21 { 22 for (Annotation a : paraAnnotations[i]) 23 { 24 System.out.println(a.toString()); 25 } 26 } 27 } 28 29 Field[] fields = temp.getClass().getFields(); 30 for (Field field : fields) 31 { 32 annotations = field.getAnnotations(); 33 for(Annotation a : annotations) 34 { 35 System.out.println(a.toString()); 36 } 37 } 38 }

1 private static void annotationTest2() throws ClassNotFoundException 2 { 3 Class classType = Class.forName("sample.reflection.annotation.MyClass"); 4 boolean flag = classType.isAnnotationPresent(ClassAnnotation.class); 5 if (flag) 6 { 7 ClassAnnotation annotation = (ClassAnnotation) classType.getAnnotation(ClassAnnotation.class); 8 System.out.println(annotation.toString()); 9 } 10 Method[] methods = classType.getMethods(); 11 for(Method method : methods) 12 { 13 if (method.isAnnotationPresent(MethodAnnotation.class)) 14 { 15 System.out.println(((MethodAnnotation)method.getAnnotation(MethodAnnotation.class)).toString()); 16 } 17 Annotation[][] paraAnnotations = method.getParameterAnnotations(); 18 for(int i = 0; i < paraAnnotations.length; i++) 19 { 20 for (Annotation a : paraAnnotations[i]) 21 { 22 System.out.println(a.toString()); 23 } 24 } 25 } 26 Field[] fields = classType.getFields(); 27 for (Field field:fields) 28 { 29 if (field.isAnnotationPresent(FieldAnnotation.class)) 30 { 31 System.out.println(((FieldAnnotation)field.getAnnotation(FieldAnnotation.class)).toString()); 32 } 33 } 34 }
上述兩個方法的輸出都是一樣的,如下:
@sample.reflection.annotation.ClassAnnotation(value=這是作用在類型上的Annotation) @sample.reflection.annotation.MethodAnnotation(methodName=printInfo, returnType=void) @sample.reflection.annotation.MethodAnnotation(methodName=printError, returnType=void) @sample.reflection.annotation.ParameterAnnotation(value=這是作用在參數上的Annotation) @sample.reflection.annotation.FieldAnnotation(value=這是作用在字段上的Annotation)
在WebService中使用Annotation
上述代碼看上去可能有些枯燥,不能顯示出Annotation的威力,那么我們接下來看WebService,在WebService中,我們可以使用WebMethod、WebParam等Annotation來聲明方法或者參數。
接下來,我們來實現一個非常簡單的Web服務:
1 @WebService(targetNamespace="http://test", serviceName="HelloService") 2 public class HelloServiceProvider 3 { 4 @WebResult(name="HelloString") 5 @WebMethod 6 public String sayHello(@WebParam(name="userName") String name) 7 { 8 return "Hello " + name; 9 } 10 11 @Oneway 12 @WebMethod(action="userLogin", operationName="userLogin") 13 public void login() 14 { 15 System.out.println("User has logged on."); 16 } 17 18 public static void main(String[] args) 19 { 20 Thread thread = new Thread(new HelloServicePublisher()); 21 thread.start(); 22 } 23 }
然后定義一個Publisher:
1 class HelloServicePublisher implements Runnable 2 { 3 public void run() 4 { 5 Endpoint.publish("http://localhost:8888/test/HelloService", new HelloServiceProvider()); 6 } 7 }
在命令行中,我們定位到源代碼路徑,執行下面的命令:
wsgen -cp . HelloServiceProvider
wsgen位於JDK的bin目錄中。
然后我們啟動HelloServiceProvider,在瀏覽器中輸入如下地址:http://localhost:8888/test/HelloService,可以看到如下信息:
點擊WSDL鏈接,可以看到:

<!-- Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.4-b01. --><!-- Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.4-b01. --><definitions targetNamespace="http://test" name="HelloService"><types><xsd:schema><xsd:import namespace="http://test" schemaLocation="http://localhost:8888/test/HelloService?xsd=1"/></xsd:schema></types><message name="sayHello"><part name="parameters" element="tns:sayHello"/></message><message name="sayHelloResponse"><part name="parameters" element="tns:sayHelloResponse"/></message><message name="userLogin"><part name="parameters" element="tns:userLogin"/></message><portType name="HelloServiceProvider"><operation name="sayHello"><input wsam:Action="http://test/HelloServiceProvider/sayHelloRequest" message="tns:sayHello"/><output wsam:Action="http://test/HelloServiceProvider/sayHelloResponse" message="tns:sayHelloResponse"/></operation><operation name="userLogin"><input wsam:Action="userLogin" message="tns:userLogin"/></operation></portType><binding name="HelloServiceProviderPortBinding" type="tns:HelloServiceProvider"><soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/><operation name="sayHello"><soap:operation soapAction=""/><input><soap:body use="literal"/></input><output><soap:body use="literal"/></output></operation><operation name="userLogin"><soap:operation soapAction="userLogin"/><input><soap:body use="literal"/></input></operation></binding><service name="HelloService"><port name="HelloServiceProviderPort" binding="tns:HelloServiceProviderPortBinding"><soap:address location="http://localhost:8888/test/HelloService"/></port></service></definitions>
JDK中自帶了Web服務器,我們不需要把上述代碼部署到其他服務器中。
動態代理機制
Spring中一大特色是AOP,面向方面編程也是框架設計一個趨勢。對於業務中的共通操作,諸如記錄日志、維護事務等,如果和業務邏輯糾纏在一起,會造成代碼職責不清,后續維護困難等問題。利用AOP,我們可以很好的分離共通操作和業務操作。
下面我們來實現一個簡單的AOP框架,要實現這樣一個框架,需要3部分:1)InvocationHandler,來觸發方法;2)Interceptor,來定義攔截器;3)DynamicProxy,來動態創建代理對象。
首先我們看Interptor的定義:
1 interface AOPInterceptor 2 { 3 public void before(Method method, Object[] args); 4 public void after(Method method, Object[] args); 5 public void afterThrowing(Method method, Object[] args); 6 public void afterFinally(Method method, Object[] args); 7 }
接下來是InvocationHandler:
1 class DynamicProxyInvocationHandler implements InvocationHandler 2 { 3 private Object target; 4 private AOPInterceptor interceptor; 5 6 public DynamicProxyInvocationHandler(Object target, AOPInterceptor interceptor) 7 { 8 this.target = target; 9 this.interceptor = interceptor; 10 } 11 12 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 13 { 14 try 15 { 16 interceptor.before(method, args); 17 Object returnValue = method.invoke(target, args); 18 interceptor.after(method, args); 19 return returnValue; 20 } 21 catch(Throwable t) 22 { 23 interceptor.afterThrowing(method, args); 24 throw t; 25 } 26 finally 27 { 28 interceptor.afterFinally(method, args); 29 } 30 } 31 }
最后是DynamicProxy:
1 class DynamicProxyFactoryImpl implements DynamicProxyFactory 2 { 3 public <T> T createProxy(Class<T> clazz, T target, AOPInterceptor interceptor) 4 { 5 InvocationHandler handler = new DynamicProxyInvocationHandler(target, interceptor); 6 return (T)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[] {clazz}, handler); 7 } 8 }
至此,我們構建了一個”簡易“的AOP攔截器。下面我們來創建一些測試代碼。
首先是實現AOPInterceptor接口:
1 class MyInterceptor implements AOPInterceptor 2 { 3 4 public void after(Method method, Object[] args) { 5 System.out.println("方法執行結束。"); 6 } 7 8 public void afterFinally(Method method, Object[] args) { 9 System.out.println("方法體Finally執行結束。"); 10 } 11 12 public void afterThrowing(Method method, Object[] args) { 13 System.out.println("方法拋出異常。"); 14 } 15 16 public void before(Method method, Object[] args) { 17 System.out.println("方法開始執行"); 18 } 19 }
然后利用本文一開始定義的HelloWorldService,來完成測試,需要在MyHello的sayHello方法最后,追加一行代碼:
1 throw new RuntimeException();
接着是測試代碼:
1 private static void test() 2 { 3 MyInterceptor interceptor = new MyInterceptor(); 4 HelloWorldService hello = new MyHelloWorld(); 5 DynamicProxyFactory factory = new DynamicProxyFactoryImpl(); 6 HelloWorldService proxy = factory.createProxy(HelloWorldService.class, hello, interceptor); 7 proxy.sayHello("Zhang San"); 8 }
最終,執行結果如下:
方法開始執行 Hello Zhang San. 方法拋出異常。 方法體Finally執行結束。 Exception in thread "main" java.lang.reflect.UndeclaredThrowableException at sample.reflection.dynamicproxy.$Proxy0.sayHello(Unknown Source) at sample.reflection.dynamicproxy.Sample.test(Sample.java:18) at sample.reflection.dynamicproxy.Sample.main(Sample.java:9) Caused by: java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at sample.reflection.dynamicproxy.DynamicProxyInvocationHandler.invoke(Sample.java:60) ... 3 more
可以看出,我們已經在業務執行的前、后、異常拋出后以及finally執行后進行了攔截,達到了我們期望的效果。