jdk动态代理

本文介绍了如何使用Java的JDK动态代理实现,通过工厂类MyDynamicProxyFactory创建代理对象,局限于接口并利用单例模式管理实例,同时讨论了基于类名存储和代理增强的考虑因素。
摘要由CSDN通过智能技术生成

jdk动态代理实现

前言

本人逻辑基于代码展开,阅读前还望view code

原理

  • jdk代理基于接口实现,所以不可直接代理实现类,如果测试会发现形如 ..$proxy?...报出classCastException
  • 核心方法(args中的ClassLoader类加载器,Class<?>[] interfaces 接口方法,InvocationHandler实例),此方法应证jdk proxy基于interface实现(调用时不要将接口.class传入)
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 
  • 显然代理会针对一种行为,所以基于接口代理是存在其合理性,不过cglib的可操作性更强,无需接口
  • 实际生成实现对应接口的新类,jvm层面的
  • 演示代码的结构是工厂类MyDynamicFactory,接口MyInterface,接口实现类MyProxy

思考

  • 存在不少示例,是将工厂类内部设置了target,并且抽象了MyInvocationHandler然后调用时重写属性,个人认为一般代理增强的代码业务通用,非entity,所以想法是对要代理的类加入限制<T extends InvocationHandler> 这样减少类数量,减少耦合.,其次将增强逻辑至于同一类
  • 如果将代理增强逻辑独立实现,显然工厂类中需要关于不同类型的MyIncocationHandler的集合,加上后续存类实例的hashMap,安全性降低,并且复杂性提高,有可能需要concurrentHashMap实现
  • 工厂内部代理集合我采用hashMap<K,V>,其中K类型String,想必大家也能想到是全类名,V采用实体类的对象,但是这样会引发调用时无关的创建对象问题,所以实现类采用单例
  • hashMap线程不安全,主要是操作不原子,所以对工厂的put(String clzName加锁,对外不暴露hashMapoperation
  • 单例里有个逻辑是防止反射破坏的(反射调用constructor,加个static boolean标志查重)

代码

工厂类

@Slf4j
package com.pg.backend.designPattern.structuralPattern.proxyPattern.dynamicProxyPattern.main;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;

/**
 * JDK 代理基于接口,本实例
 * @author paul 2024/3/6
 */
@Slf4j
public class MyDynamicProxyFactory {
    //考虑到的想法是 ,基于 Enum ,但是考虑到类数量,基于hashMap<K,V> 全类名存储
    private final HashMap<String,Object> proxiedObjs = new HashMap<>();

    public  Object  getProxy( Object  o) {
        return Proxy.newProxyInstance(o.getClass().getClassLoader(), o.getClass().getInterfaces(), (InvocationHandler) o);
    }
    public Object getProxy(String clzName) {
        if (clzName == null || clzName.isEmpty()) {
            log.info("paramsError");
            return null;
        }
        Object o = this.get(clzName);
        return Proxy.newProxyInstance(o.getClass().getClassLoader(), o.getClass().getInterfaces(), (InvocationHandler) o);
    }
    @SneakyThrows
    public synchronized <T extends InvocationHandler>  void  put(T o) {
        proxiedObjs.put(o.getClass().getName(),o);
    }
    private synchronized Object get(String name) {
        return proxiedObjs.get(name);
    }
}

接口

package com.pg.backend.designPattern.structuralPattern.proxyPattern.dynamicProxyPattern.main;

/**
 * @author paul 2024/3/6
 */

public interface MyInterface {
    void test();
}


实现类

@Slf4j
package com.pg.backend.designPattern.structuralPattern.proxyPattern.dynamicProxyPattern.main;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @author paul 2024/3/6
 */
@Slf4j
public class MyProxy implements InvocationHandler,MyInterface {
    private static volatile MyProxy myProxy;

    private static boolean flag = false;

    private MyProxy() {
        if(flag) {
           throw new RuntimeException("canNotCreateMoreInstance");
        }
        flag  = true;
    }
    public static MyProxy getInstance() {
        if(myProxy == null) {
            synchronized (MyProxy.class) {
                if(myProxy == null){
                    myProxy = new MyProxy();
                }
            }
        }
        return myProxy;
    }
    public void test(){
        log.info("test...");
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info("beforeProxy");
        return method.invoke(MyProxy.getInstance(),args);
    }
}

框架

org.reflections 此框架处理非spring项目的aop或者动态增强很合适,比如类的收集,拥有注解的Field,在轮子项目里很合适
pom

    <dependency>
            <groupId>org.reflections</groupId>
            <artifactId>reflections</artifactId>
            <version>0.10.2</version>
        </dependency>

工具类

public class ReflectKit {
    private Reflections reflections;

    public ReflectKit(String packageAddress) {
    		// 配置类
        ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
        // 多组包扫描的分包
        String[] addresses = packageAddress.split(",");
        Stream.of(addresses).forEach(str -> configurationBuilder.addUrls(ClasspathHelper.forPackage(str.trim())));
        // 加入Scanner扫描器,如果扫描注解字段,不加Scanners.FieldsAnnotated是不会扫描到的
        configurationBuilder.setScanners(Scanners.FieldsAnnotated, Scanners.MethodsAnnotated,Scanners.TypesAnnotated);
        reflections =  new Reflections(configurationBuilder);
    }
    public <T extends Annotation> Set<Class<?>> getBeans(Class<T> anno) {
        return reflections.getTypesAnnotatedWith(anno);
    }
    public <T extends Annotation> Set<Field> getFields(Class<T> anno) {
        return reflections.getFieldsAnnotatedWith(anno);
    }
    
}

总结

  • 以上全部基于个人经验实现,若对您思维产生误导或者您认为错误,还请批评指正
  • 若存在不错java后端项目实践,渴望得到青睐

联系方式

  1. Email: yydspg@outlook.com
  2. github: https://github.com/yydspg/
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lamooo19

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值