Java 反射是框架開發的基石,Spring、Hibernate 等主流框架大量依賴它來實現動態類型檢查、實例創建與方法調用。本文將透過完整代碼示例,系統講解反射的核心用法,並手把手帶你構建一個簡易 AOP 框架。
反射的起點是 Class 對象。以下示例定義了一個服務接口及其實現,作為後續操作的目標:
interface HelloWorldService {
void sayHello(String name);
}
class MyHelloWorld implements HelloWorldService {
public String name;
public void sayHello(String name) {
System.out.println("Hello " + name + ".");
}
public void setName(String name) { this.name = name; }
public String getName() { return name; }
}
利用 Class.forName() 加載類型,再調用 getDeclaredMethods() 與 getFields() 即可列印所有方法與公開字段:
private static void printClassTypeInfo(String type) throws ClassNotFoundException {
Class> clazz = Class.forName(type);
Method[] methods = clazz.getDeclaredMethods();
System.out.println("Methods info as below:");
for (Method m : methods) System.out.println(m.toGenericString());
Field[] fields = clazz.getFields();
System.out.println("Fields info as below:");
for (Field f : fields) System.out.println(f.toGenericString());
}
執行 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.newInstance()(已過時,建議改用 getDeclaredConstructor().newInstance())可直接實例化:
Class> clazz = Class.forName("sample.reflection.MyHelloWorld");
MyHelloWorld hello = (MyHelloWorld) clazz.getDeclaredConstructor().newInstance();
hello.sayHello("Zhang San"); // 輸出:Hello Zhang San.
透過 Method.invoke() 實現運行時方法調用:
Method method = clazz.getMethod("sayHello", String.class);
method.invoke(hello, "Zhang San");
Java 無 Property 概念,直接操作 Field:
Field field = clazz.getField("name");
System.out.println("name is " + hello.name); // null
field.set(hello, "Zhang San");
System.out.println("name is " + hello.name); // Zhang San
Annotation 是反射的典型應用場景。定義四種元標註:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented
@interface ClassAnnotation { String value(); }
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented
@interface MethodAnnotation { String methodName(); String returnType(); }
@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) @Documented
@interface ParameterAnnotation { String value(); }
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented
@interface FieldAnnotation { String value(); }
標註目標類:
@ClassAnnotation("這是作用在類型上的Annotation")
class MyClass {
@MethodAnnotation(methodName="printInfo", returnType="void")
public void printInfo(String info) { System.out.println(info); }
@MethodAnnotation(methodName="printError", returnType="void")
public void printError(@ParameterAnnotation("這是作用在參數上的Annotation") String error) {
System.err.println(error);
}
@FieldAnnotation("這是作用在字段上的Annotation")
public int count;
}
兩種讀取方式:遍歷全量或精準獲取指定標註,輸出結果一致:
@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、@WebMethod 等標註,無需外部容器即可發布 SOAP 服務:
@WebService(targetNamespace="https://test", serviceName="HelloService")
public class HelloServiceProvider {
@WebResult(name="HelloString")
@WebMethod
public String sayHello(@WebParam(name="userName") String name) {
return "Hello " + name;
}
@Oneway @WebMethod(action="userLogin", operationName="userLogin")
public void login() { System.out.println("User has logged on."); }
public static void main(String[] args) {
new Thread(new HelloServicePublisher()).start();
}
}
class HelloServicePublisher implements Runnable {
public void run() {
Endpoint.publish("https://localhost:8888/test/HelloService", new HelloServiceProvider());
}
}
編譯後執行 wsgen -cp . HelloServiceProvider 生成 WSDL,瀏覽器訪問 https://localhost:8888/test/HelloService?wsdl 即可查看服務契約。
Spring AOP 的核心正是 JDK 動態代理。我們分三步實現:
interface AOPInterceptor {
void before(Method method, Object[] args);
void after(Method method, Object[] args);
void afterThrowing(Method method, Object[] args);
void afterFinally(Method method, Object[] args);
}
class DynamicProxyInvocationHandler implements InvocationHandler {
private Object target;
private AOPInterceptor interceptor;
public DynamicProxyInvocationHandler(Object target, AOPInterceptor interceptor) {
this.target = target;
this.interceptor = interceptor;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
interceptor.before(method, args);
Object ret = method.invoke(target, args);
interceptor.after(method, args);
return ret;
} catch (Throwable t) {
interceptor.afterThrowing(method, args);
throw t;
} finally {
interceptor.afterFinally(method, args);
}
}
}
class DynamicProxyFactoryImpl implements DynamicProxyFactory {
public T createProxy(Class clazz, T target, AOPInterceptor interceptor) {
InvocationHandler handler = new DynamicProxyInvocationHandler(target, interceptor);
return (T) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class>[]{clazz}, handler);
}
}
class MyInterceptor implements AOPInterceptor {
public void before(Method m, Object[] a) { System.out.println("方法開始執行"); }
public void after(Method m, Object[] a) { System.out.println("方法執行結束。"); }
public void afterThrowing(Method m, Object[] a) { System.out.println("方法拋出異常。"); }
public void afterFinally(Method m, Object[] a) { System.out.println("方法體Finally執行結束。"); }
}
// 在 MyHelloWorld.sayHello 末尾添加 throw new RuntimeException();
private static void test() {
MyInterceptor interceptor = new MyInterceptor();
HelloWorldService target = new MyHelloWorld();
DynamicProxyFactory factory = new DynamicProxyFactoryImpl();
HelloWorldService proxy = factory.createProxy(HelloWorldService.class, target, interceptor);
proxy.sayHello("Zhang San");
}
執行結果清晰展示了完整的攔截鏈:
方法開始執行
Hello Zhang San.
方法拋出異常。
方法體Finally執行結束。
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
...
Caused by: java.lang.reflect.InvocationTargetException
...
Caused by: java.lang.RuntimeException
至此,我們已完整走過反射從元數據檢查、實例操作、標註處理到動態代理的全流程,掌握了框架級開發的核心技能。