SpringBoot 动态代理|反射|注解|AOP 优化代码(三)-注解

风控君
• 阅读 5121

上一篇SpringBoot 动态代理|反射|注解|AOP 优化代码(二)-反射

我们实现了通过反射完善找到目标类,然后通过动态代理提供默认实现,本篇我们将使用自定义注解来继续优化。

创建注解

1.创建枚举 ClientType,用来标明Handler的实现方式

public enum ClientType {
    FEIGN,URL
}

2.创建注解ApiClient,用来标明Handler的实现方式

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ApiClient {
    ClientType type();
}

3.创建HandlerRouterAutoImpl注解,来标记该HandlerRouter是否通过代理提供默认实现

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface HandlerRouterAutoImpl {
   /**
    * 在spring容器中对应的名称
    * @return
    */
   String name();
}

4.DeviceHandlerRouter添加注解,以动态代理提供默认实现

@HandlerRouterAutoImpl(name = "deviceHandlerRouter")
public interface DeviceHandlerRouter extends HandlerRouter<DeviceHandler> {
}

5.DeviceHandlerFeignImpl、DeviceHandlerUrlImpl 添加注解标明具体的实现方式

@ApiClient(type = ClientType.FEIGN)
@Component
@Slf4j
public class DeviceHandlerFeignImpl implements DeviceHandler {

    @Autowired
    private DeviceFeignClient deviceFeignClient;

    @Override
    public void remoteAddBatch(RemoteAddDeviceParam remoteAddDeviceParam, Integer envValue) {
        RestResult restResult = deviceFeignClient.create(remoteAddDeviceParam);
        ...
    }

    @Override
    public void remoteDeleteBatch(Integer envValue, List<String> snsList) {   
        RestResult restResult = deviceFeignClient.deleteBySnList(snsList);      
        ... 
    }   
  
}
@ApiClient(type = ClientType.URL)
@Component
@Slf4j
public class DeviceHandlerUrlImpl implements DeviceHandler {

    @Override
    public void remoteAddBatch(RemoteAddDeviceParam remoteAddDeviceParam, Integer envValue) {
        String url = getAddUrlByEnvValue(envValue);
        String response = OkHttpUtils.httpPostSyn(url, JSON.toJSONString(snsList), false);
        RestResult restResult = JSON.parseObject(response, RestResult.class);
        ...
    }

    @Override
    public void remoteDeleteBatch(Integer envValue, List<String> snsList) {
        String url = getDelUrlByEnvValue(envValue);
        String response = OkHttpUtils.httpPostSyn(url, JSON.toJSONString(snsList), false);
        RestResult restResult = JSON.parseObject(response, RestResult.class);
        ...
    }
}

6.通过注解扫描目标类

扫描HandlerRouterAutoImpl注解的类,通过动态代理提供默认的实现

    /**
     * 通过反射扫描出所有使用注解HandlerRouterAutoImpl的类
     * @return
     */
    private Set<Class<?>> getAutoImplClasses() {
        Reflections reflections = new Reflections(
                "io.ubt.iot.devicemanager.impl.handler.*",
                new TypeAnnotationsScanner(),
                new SubTypesScanner()
        );
        return reflections.getTypesAnnotatedWith(HandlerRouterAutoImpl.class);
    }

动态代理类中,获取业务接口的实现类,并获取ApiClient注解,然后分类,保存到Map中。以在调用getHandler方式时根据传入的环境值,返回不同实现方式的实例。

@Slf4j
public class DynamicProxyBeanFactory implements InvocationHandler {
    private String className;
    private Map<ClientType, Object> clientMap = new HashMap<>(2);

    public DynamicProxyBeanFactory(String className) {
        this.className = className;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //获取过一次后不再获取
        if (clientMap.size() == 0) {
            initClientMap();
        }
        
        //如果传入的参数是1,就返回通过Feign方式实现的类 (该逻辑只是用来举例)
        Integer env = (Integer) args[0];
        return 1 == env.intValue() ? clientMap.get(ClientType.FEIGN) : clientMap.get(ClientType.URL);
    }

    private void initClientMap() throws ClassNotFoundException {
        //获取classStr 接口的所有实现类
        Map<String,?> classMap =SpringUtil.getBeansOfType(Class.forName(className));
        log.info("DynamicProxyBeanFactory className:{} impl class:{}",className,classMap);

        for (Map.Entry<String,?> entry : classMap.entrySet()) {
            //根据ApiClientType注解将实现类分为Feign和Url两种类型
            ApiClient apiClient = entry.getValue().getClass().getAnnotation(ApiClient.class);
            if (apiClient == null) {
                continue;
            }
            clientMap.put(apiClient.type(), entry.getValue());
        }
        log.info("DynamicProxyBeanFactory clientMap:{}",clientMap);
    }


    public static <T> T newMapperProxy(String typeName,Class<T> mapperInterface) {
        ClassLoader classLoader = mapperInterface.getClassLoader();
        Class<?>[] interfaces = new Class[]{mapperInterface};
        DynamicProxyBeanFactory proxy = new DynamicProxyBeanFactory(typeName);
        return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy);
    }
}

以上我们通过注解、动态代理、反射就实现了通过注解,找到需要提供默认实现的HandlerRouter子类,并通过动态代理提供默认实现。
还有一个问题:通过代理生成的对象,该怎么管理,我们并不想通过代码,手动管理。如果能把动态代理生成的对象交给spring容器管理,其它代码直接自动注入就可以了。

下一篇:SpringBoot 动态代理|反射|注解(四)- 动态代理对象注入到Spring容器

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Easter79 Easter79
3年前
Spring的两种代理JDK和CGLIB的区别浅谈
一、原理区别:java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP 2、如果目标对象实现了接口,可以
Stella981 Stella981
3年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Easter79 Easter79
3年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Easter79 Easter79
3年前
Spring的两种动态代理:Jdk和Cglib 的区别和实现
一、原理区别:java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP 2、如果目标对象实现了接口,可以
Wesley13 Wesley13
3年前
Java提高班(六)反射和动态代理(JDK Proxy和Cglib)
反射和动态代理放有一定的相关性,但单纯的说动态代理是由反射机制实现的,其实是不够全面不准确的,动态代理是一种功能行为,而它的实现方法有很多。要怎么理解以上这句话,请看下文。一、反射反射机制是Java语言提供的一种基础功能,赋予程序在运行时<strong自省</strong(introspect,官方用语)的能力。通过反射我们可以直接
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这