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

Java 反射機制完全指南:類信息、註解處理、泛型擷取與動態代理實戰

     編輯:Java 相關知識

前言

反射是 Java 動態性的核心,支撐著 Spring、Hibernate、MyBatis 等主流框架。本文系統講解反射三大核心能力:類結構探測、註解元數據處理、泛型類型擷取,並給出生產級代碼範例。

反射入口:獲取 Class 對象

// 1. 類字面量 (編譯期已知)
Class<User> clazz = User.class;

// 2. 實例 getClass() (運行期對象)
User user = new User();
Class<?> clazz2 = user.getClass();

// 3. 字符串全限定名 (配置驅動)
Class<?> clazz3 = Class.forName("com.example.User");

一、類結構深度探測

基本信息

System.out.println("完整類名: " + clazz.getName());        // com.example.User
System.out.println("簡單類名: " + clazz.getSimpleName());   // User
System.out.println("規範名: " + clazz.getCanonicalName());   // com.example.User

字段探測

// 所有聲明字段 (含 private,不含繼承)
Field[] declaredFields = clazz.getDeclaredFields();
for (Field f : declaredFields) {
    System.out.println(f.getModifiers() + " " + f.getType().getSimpleName() + " " + f.getName());
    // private String name
    // private int age
    // private String sex
}

// 僅 public 字段 (含繼承)
Field[] publicFields = clazz.getFields();

方法探測

Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method m : declaredMethods) {
    System.out.println(m.getName() + " -> " + m.getReturnType().getSimpleName() 
        + " " + Arrays.toString(m.getParameterTypes()));
}

// 精確獲取帶參方法
Method setSex = clazz.getDeclaredMethod("setSex", String.class);

構造器探測

Constructor<?>[] constructors = clazz.getConstructors(); // 僅 public
for (Constructor<?> c : constructors) {
    System.out.println(c.getParameterCount() + " 參數: " + Arrays.toString(c.getParameterTypes()));
}
// 0 參數: []
// 3 參數: [String, int, String]

實例化對象

// 無參構造器
User u1 = clazz.getDeclaredConstructor().newInstance();

// 帶參構造器
Constructor<User> ctor = clazz.getDeclaredConstructor(String.class, int.class, String.class);
User u2 = ctor.newInstance("Alice", 25, "Female");

二、註解元數據處理實戰

自定義註解定義

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
    String value() default "";
    String schema() default "public";
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    String name() default "";
    String type() default "varchar";
    int length() default 255;
    boolean nullable() default true;
    boolean primaryKey() default false;
}

實體類標註

註解解析器:自動生成建表 DDL

public class DdlGenerator {
    public static String generateCreateTable(Class<?> entityClass) {
        Table tableAnn = entityClass.getAnnotation(Table.class);
        String tableName = tableAnn != null && !tableAnn.value().isEmpty() 
            ? tableAnn.value() : entityClass.getSimpleName().toLowerCase();

        StringBuilder sql = new StringBuilder("CREATE TABLE ").append(tableName).append(" (\n");
        
        Field[] fields = entityClass.getDeclaredFields();
        List pkColumns = new ArrayList<>();

        for (int i = 0; i < fields.length; i++) {
            Field f = fields[i];
            Column colAnn = f.getAnnotation(Column.class);
            if (colAnn == null) continue; // 忽略無註解字段

            String colName = colAnn.name().isEmpty() ? f.getName() : colAnn.name();
            String colType = colAnn.type();
            if (colAnn.length() > 0 && !colType.contains("(")) {
                colType += "(" + colAnn.length() + ")";
            }
            String nullable = colAnn.nullable() ? "" : " NOT NULL";
            
            sql.append("  ").append(colName).append(" ").append(colType).append(nullable);
            if (i < fields.length - 1) sql.append(",");
            sql.append("\n");

            if (colAnn.primaryKey()) pkColumns.add(colName);
        }

        if (!pkColumns.isEmpty()) {
            sql.append("  PRIMARY KEY (").append(String.join(", ", pkColumns)).append(")\n");
        }
        sql.append(");");
        return sql.toString();
    }
}

// 輸出:
// CREATE TABLE t_user (
//   id bigint NOT NULL,
//   username varchar(50),
//   age int
//   PRIMARY KEY (id)
// );

三、泛型類型擷取 (TypeToken 模式)

問題:類型擦除導致運行期丟失泛型

public class GenericDao<T> {
    private Class<T> entityClass;
    
    // 錯誤: 運行期 T 擦除為 Object
    // public GenericDao() { this.entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; }
    
    // 正確: 子類指定具體類型時保留泛型信息
    public GenericDao(Class<T> entityClass) { this.entityClass = entityClass; }
}

TypeToken 完整實現 (參考 Guava)

public abstract class TypeToken<T> {
    private final Type type;

    protected TypeToken() {
        Type superclass = getClass().getGenericSuperclass();
        if (superclass instanceof ParameterizedType) {
            this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0];
        } else {
            throw new IllegalStateException("必須在匿名內部類中指定具體類型");
        }
    }

    public Type getType() { return type; }
    public Class<?> getRawType() { 
        return (type instanceof Class) ? (Class<?>) type : (Class<?>) ((ParameterizedType) type).getRawType(); 
    }
}

// 使用:
TypeToken token = new TypeToken<List<String>>() {};
System.out.println(token.getType()); // java.util.List
System.out.println(token.getRawType()); // interface java.util.List

方法泛型參數/返回值解析

public class MethodGenericResolver {
    public static void main(String[] args) throws NoSuchMethodException {
        Method m1 = MethodGenericResolver.class.getDeclaredMethod("process", Map.class, List.class);
        Method m2 = MethodGenericResolver.class.getDeclaredMethod("result");

        // 參數泛型
        Type[] paramTypes = m1.getGenericParameterTypes();
        for (Type pt : paramTypes) {
            if (pt instanceof ParameterizedType) {
                ParameterizedType ppt = (ParameterizedType) pt;
                System.out.println("原始類型: " + ppt.getRawType());
                System.out.println("實際類型參數: " + Arrays.toString(ppt.getActualTypeArguments()));
            }
        }
        // 輸出:
        // 原始類型: interface java.util.Map
        // 實際類型參數: [class java.lang.String, class java.lang.String]
        // 原始類型: interface java.util.List
        // 實際類型參數: [class java.lang.Integer]

        // 返回值泛型
        Type returnType = m2.getGenericReturnType();
        if (returnType instanceof ParameterizedType) {
            ParameterizedType prt = (ParameterizedType) returnType;
            System.out.println("返回: " + prt.getRawType() + "<" + Arrays.toString(prt.getActualTypeArguments()) + ">");
        }
        // 輸出: 返回: interface java.util.Map<[class java.lang.String, class java.lang.String]>
    }

    public void process(Map<String, String> map, List<Integer> list) {}
    public Map<String, String> result() { return null; }
}

四、動態代理:JDK vs CGLIB

維度JDK ProxyCGLIB
前提條件目標實現接口無需接口,繼承類
核心機制接口實現類生成字節碼生成子類
關鍵類Proxy, InvocationHandlerEnhancer, MethodInterceptor
final 方法可代理無法攔截
性能調用略慢 (反射)FastClass 機制較快

JDK 動態代理實現

public class JdkProxyFactory {
    public static <T> T createProxy(T target, InvocationHandler handler) {
        return (T) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            handler
        );
    }
}

// 使用:
UserDao target = new UserDaoImpl();
UserDao proxy = JdkProxyFactory.createProxy(target, (proxyObj, method, args) -> {
    System.out.println("Before: " + method.getName());
    Object result = method.invoke(target, args);
    System.out.println("After: " + method.getName());
    return result;
});

CGLIB 代理實現

public class CglibProxyFactory {
    public static <T> T createProxy(Class<T> targetClass, MethodInterceptor interceptor) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);
        enhancer.setCallback(interceptor);
        return (T) enhancer.create();
    }
}

// 使用 (目標類無需接口):
UserService target = new UserServiceImpl();
UserService proxy = CglibProxyFactory.createProxy(UserService.class, (obj, method, args, proxy) -> {
    if (method.getName().startsWith("save")) {
        TransactionManager.begin();
    }
    try {
        Object result = proxy.invokeSuper(obj, args);
        if (method.getName().startsWith("save")) TransactionManager.commit();
        return result;
    } catch (Exception e) {
        if (method.getName().startsWith("save")) TransactionManager.rollback();
        throw e;
    }
});

性能優化與安全建議

  1. 緩存 Class/Field/Method/Constructor:反射操作昂貴,靜態緩存復用
  2. setAccessible(true) 慎用:破壞封裝,模塊化系統 (JDK 9+) 需 --add-opens
  3. 優先用 MethodHandle/VarHandle (JDK 7+):JIT 優化更好,調用點緩存
  4. 避免熱點路徑反射:代碼生成、Lambda 元工廠替代
  5. 安全管理器檢查:生產環境啟用 SecurityManager 限制反射權限

總結

反射三大核心價值:運行期類結構探測、註解驅動元編程、泛型類型擷取突破擦除。掌握 Class/Field/Method/Constructor/Type 體系,配合 TypeToken 模式解決泛型擦除,善用 JDK Proxy/CGLIB 實現 AOP 橫切關注點,是框架開發與高級應用開發的必備技能。

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