首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Java 反射機制深度解析:從基礎操作到動態代理實戰

     編輯:Java 程式設計入門

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

二、實例化與方法調用

1. 創建實例

Class.newInstance()(已過時,建議改用 getDeclaredConstructor().newInstance())可直接實例化:

Class clazz = Class.forName("sample.reflection.MyHelloWorld");
MyHelloWorld hello = (MyHelloWorld) clazz.getDeclaredConstructor().newInstance();
hello.sayHello("Zhang San"); // 輸出:Hello Zhang San.

2. 動態調用方法

透過 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 探索與 WebService 實戰

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 實例:JDK 內置發布

利用 @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 即可查看服務契約。

五、動態代理構建簡易 AOP 框架

Spring AOP 的核心正是 JDK 動態代理。我們分三步實現:

1. 定義攔截器接口

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);
}

2. 實現 InvocationHandler

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);
        }
    }
}

3. 代理工廠創建代理對象

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);
    }
}

4. 測試驗證

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

至此,我們已完整走過反射從元數據檢查、實例操作、標註處理到動態代理的全流程,掌握了框架級開發的核心技能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved