修改对象的行为和值(代理)

发布时间:2024年01月06日


前言

最近遇到一个需求,需要在某个位置,统一处理对象的一些属性值:
方案有两种:

  1. 直接复制一份,将属性覆盖后,返回一个新对象
  2. 搞一个代理类,代理这个对象,修改对象的原有行为和值,从而达到修改属性值的目的

一、复制一个对象

这里有现成的hutool 方法,当然不用自己造轮子了,废话不多说,直接上代码:

这里有个类,我设置了id为12 ,我想将id属性值修改为2

public class MainTet {
   public static void main(String[] args) {

       Menu menu = new Menu();
       menu.setMenuCode("12");
       menu.setId(12);

       Map<String, Object> overrideProps = new HashMap<>();
       overrideProps.put("id", 2);

       Menu menu1 = overrideField(menu,overrideProps);
       System.out.println(JSONUtil.toJsonStr(menu1));
       System.out.println(JSONUtil.toJsonStr(menu));

   }
	private static <T> T overrideField(T menu,Map<String, Object> overrideProps) {
       Object object = BeanUtil.copyProperties(menu, menu.getClass());
       for (Map.Entry<String, Object> entry : overrideProps.entrySet()) {
           BeanUtil.setFieldValue(object, entry.getKey(), entry.getValue());
       }
       return (T) object;
   }
}
    

新建一个类的结果
可以看到,返回了新对象,然后值已经被修改,原值仍然是12;

二、代理对象

这个就不一般了,它不是新建了对象,而是创建了一个代理类,类似于spring中的代理对象,然后它不修改之前对象的属性,而是增强
大致思路:

  1. 创建代理对象
  2. 拦截方法,拦截后执行代理方法,而非原方法
public class MainTet {
   public static void main(String[] args) {

       Menu menu = new Menu();
       menu.setMenuCode("12");
       menu.setId(12);

       Map<String, Object> overrideProps = new HashMap<>();
       overrideProps.put("id", 2);

		Menu proxy = getProxy(menu,overrideProps);
        System.out.println(proxy.getId());
   }
   
   private static <T> T getProxy(T menu, Map<String, Object> overrideProps) {

        // 创建代理对象
        ProxyFactory proxyFactory = new ProxyFactory(menu);
        proxyFactory.setProxyTargetClass(Boolean.TRUE);

        Map<String, Object> overrideMap = new HashMap<>();

        // 设置代理对象的代理方法
        // 例如 key为id 那么 overrideMap 中的key为getId 和 isid
        overrideProps.forEach((k, v) -> {
            String methodName = "get" + k.substring(0, 1).toUpperCase() + k.substring(1);
            overrideMap.put(methodName, v);
            methodName = "is" + k.substring(0, 1).toUpperCase() + k.substring(1);
            overrideMap.put(methodName, v);
        });

        // 利用方法拦截器拦截调用方法,执行代理方法
        proxyFactory.addAdvice(new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                String methodName = invocation.getMethod().getName();
                if (overrideMap.containsKey(methodName)) {
	                // 直接返回覆盖值,不执行原方法
                    return overrideMap.get(methodName);
                }
                // 执行原方法
                return invocation.proceed();
            }
        });
        return (T) proxyFactory.getProxy();
    }
}

打印结果

也被更为了2

重点来了

  • 我在Menu对的getId() 方法中打了断点,但是debug运行过程中,并不会到此处断点
    方法断点

因为,代理后,根本不会执行真正的getId 方法,而是在 MethodInterceptor的 invoke 中,根据判断,直接返回覆盖值


总结

第二种方式比较高大上,相当于手动创建了代理对象,并修改了对象的原有属性和行为!

文章来源:https://blog.csdn.net/qq_32419139/article/details/135156376
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。