无敌大剑人 2024-07-02 17:44 采纳率: 0%
浏览 5

Java通过动态代理生成一个类的子类


import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.*;
import net.bytebuddy.matcher.ElementMatchers;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 动态代理工厂类,用于生成动态代理类。
 */
public class DynamicProxyFactory {

    private static final ConcurrentHashMap<String,  Class<?>> classCache = new ConcurrentHashMap<>();

    /**
     * 获取动态生成的子类,如果已经生成过则直接从缓存中返回
     *
     * @param originalClass 原始类,即要继承的类
     * @param fieldClassMap 字段类型映射表,key为字段名,value为字段对应的类类型
     * @param <T>           原始类的类型参数
     * @return Class<?>    动态生成的子类
     */
    public static <T> Class<? extends T> getCachedDynamicSubclass(Class<T> originalClass, Map<String, Class<?>> fieldClassMap) {
        String key = generateCacheKey(originalClass, fieldClassMap);
        return (Class<? extends T>) classCache.computeIfAbsent(key, item -> createDynamicSubclass(originalClass, fieldClassMap));
    }

    public void set(Object obj, String fieldName, Object value) {
        try {
            obj.getClass().getMethod("set" + capitalizeFirstLetter(fieldName), value.getClass()).invoke(obj, value);
        } catch (Exception e) {
            throw new IllegalStateException("设置字段失败", e);
        }
    }

    public void get(Object obj, String fieldName) {
        try {
            obj.getClass().getMethod("get" + capitalizeFirstLetter(fieldName)).invoke(obj);
        } catch (Exception e) {
            throw new IllegalStateException("获取字段失败", e);
        }
    }

    /**
     * 生成用于缓存的键。
     *
     * @param originalClass 原始类,即要继承的类
     * @param fieldClassMap 字段类型映射表,key为字段名,value为字段对应的类类型
     * @param <T>           原始类的类型参数
     * @return              生成的缓存键字符串
     */
    private static <T> String generateCacheKey(Class<T> originalClass, Map<String, Class<?>> fieldClassMap) {
        StringBuilder sb = new StringBuilder(originalClass.getName());
        sb.append("-");
        fieldClassMap.keySet().forEach(sb::append);
        return sb.toString();
    }

    /**
     * 创建一个动态生成的子类。
     *
     * @param originalClass 原始类,即要继承的类
     * @param fieldClassMap 字段类型映射表,key为字段名,value为字段对应的类类型
     * @param <T>           原始类的类型参数
     * @return              动态生成的子类的Class对象
     * @throws IllegalStateException 如果在创建动态子类时发生错误
     */
    private static <T> Class<?> createDynamicSubclass(Class<T> originalClass, Map<String, Class<?>> fieldClassMap)  {
        DynamicType.Builder<T> subclass = new ByteBuddy()
                .subclass(originalClass);

        for (Map.Entry<String, Class<?>> entry : fieldClassMap.entrySet()) {
            String field = entry.getKey();
            Class<?> fieldClass = entry.getValue();
            subclass = defineFieldAndAccessors(subclass, field, fieldClass);
        }

        subclass = defineCommonMethods(subclass);
        return subclass.make()
                .load(Thread.currentThread().getContextClassLoader())
                .getLoaded();
    }

    /**
     * 为给定的动态类型构建器定义字段和对应的访问器方法。
     *
     * @param builder     动态类型构建器
     * @param fieldName   字段名
     * @param fieldClass  字段的类类型
     * @param <T>         动态类型构建器对应的泛型类型
     * @return            返回更新后的动态类型构建器
     */
    private static <T> DynamicType.Builder<T> defineFieldAndAccessors(DynamicType.Builder<T> builder, String fieldName, Class<?> fieldClass) {
        return builder.defineField(fieldName, fieldClass, Visibility.PRIVATE)
                .defineMethod("get" + capitalizeFirstLetter(fieldName), fieldClass, Visibility.PUBLIC)
                .intercept(FieldAccessor.ofField(fieldName))
                .defineMethod("set" + capitalizeFirstLetter(fieldName), void.class, Visibility.PUBLIC)
                .withParameter(fieldClass)
                .intercept(FieldAccessor.ofField(fieldName));
    }

    /**
     * 为给定的动态类型构建器定义通用方法:toString、hashCode和equals,并返回更新后的构建器。
     *
     * @param builder 动态类型构建器
     * @param <T>     动态类型构建器对应的泛型类型
     * @return        更新后的动态类型构建器
     */
    private static <T> DynamicType.Builder<T> defineCommonMethods(DynamicType.Builder<T> builder) {
        return builder.method(ElementMatchers.named("toString"))
                .intercept(ToStringMethod.prefixedBySimpleClassName())
                .method(ElementMatchers.named("hashCode"))
                .intercept(HashCodeMethod.usingDefaultOffset())
                .method(ElementMatchers.named("equals"))
                .intercept(EqualsMethod.requiringSuperClassEquality());
    }

    /**
     * 将字符串的首字母转换为大写,其余字母保持不变。
     *
     * @param str 要转换的字符串
     * @return 转换后的字符串,如果输入字符串为null或空字符串,则返回原字符串
     */
    private static String capitalizeFirstLetter(String str) {
        if(str == null || str.isEmpty()) {
            return str;
        } else {
            return Character.toUpperCase(str.charAt(0)) + str.substring(1);
        }
    }
}

花了一下午的时间写了一个 动态代理工厂类,感觉没有什么应用场景,我需要解决的问题 也没有用它解决,想发给大家看看是否有需要用到的,或者是否有需要改进。

  • 写回答

2条回答 默认 最新

  • 专家-赤兔[在线] 全栈领域优质创作者 2024-07-02 17:44
    关注

    引自免费微信小程序:皆我百晓生

    你的动态代理工厂类设计得很好,并且代码结构清晰,方法职责明确。尽管你提到没有实际的应用场景,但动态代理在很多情况下都非常有用,尤其是当你需要创建灵活的对象或者处理大量的类和对象时。这里有一些可能的应用场景和需要改进的地方:

    应用场景:

    1. 单元测试模拟:你可以使用动态代理来模拟依赖项,以便在测试环境中注入自定义行为。这在测试复杂系统时非常有用。
    2. 数据库访问层封装:你可以动态生成数据访问层类,以简化数据库操作。这些类可以包含CRUD操作和其他数据库相关功能。
    3. 动态扩展功能:你可以使用动态代理来扩展现有类的功能,而不必修改原始类。这在插件系统或模块化设计中非常有用。

    改进建议:

    1. 异常处理:在setget方法中,你可以考虑添加更具体的异常处理,以便更好地处理不同类型的异常。例如,如果字段不存在或类型不匹配,可以抛出更有用的异常信息。
    2. 缓存键生成策略:在generateCacheKey方法中,你可以考虑使用更复杂的缓存键生成策略,以确保不同的字段映射不会意外地产生相同的缓存键。这可以避免缓存冲突和潜在的问题。
    3. 方法访问级别的修改:在某些情况下,你可能需要允许动态生成的子类具有不同的访问级别(如默认构造函数是否公开)。你可以在subclass构建器中添加适当的修改来适应这些情况。
    4. 性能优化:虽然使用Byte Buddy等库可以方便地生成动态类,但在高并发或性能敏感的场景下,可能需要考虑性能优化。你可以通过分析和调整代码来减少反射调用和不必要的操作,以提高性能。
    5. 文档和注释:为了更好地理解代码的目的和功能,你可以添加更多的注释和文档。这有助于其他开发人员理解你的代码并对其进行维护。

    总的来说,你的动态代理工厂类是一个很好的起点。通过了解具体的应用场景和需求,你可以进一步优化和扩展它,以满足特定的需求。

    评论 编辑记录

报告相同问题?

问题事件

  • 创建了问题 7月2日

悬赏问题

  • ¥15 35114 SVAC视频验签的问题
  • ¥15 impedancepy
  • ¥15 在虚拟机环境下完成以下,要求截图!
  • ¥15 求往届大挑得奖作品(ppt…)
  • ¥15 如何在vue.config.js中读取到public文件夹下window.APP_CONFIG.API_BASE_URL的值
  • ¥50 浦育平台scratch图形化编程
  • ¥20 求这个的原理图 只要原理图
  • ¥15 vue2项目中,如何配置环境,可以在打完包之后修改请求的服务器地址
  • ¥20 微信的店铺小程序如何修改背景图
  • ¥15 UE5.1局部变量对蓝图不可见