记第一次springboot中间件开发---阿里云iot与自定义springboot starter集成封装

记第一次springboot中间件开发—阿里云iot与自定义springboot starter集成封装

每天多学一点点~
话不多说,这就开始吧…

1.前言

写代码至今也有三年了,一直有个梦想,能自己写一套中间件,与spring,springboot集成。正好这次公司有个项目需要集成阿里云的iot,虽然上云很简单,但是想着如何自己做成封装成一个jar,然后在业务工程中直接引用,下次有相同的项目来了,只需要引用jar,在yml中配置阿里云的参数即可,做到高扩展。

遂模仿springboot stater形式,参考dubbo的 Invocation理念,用动态代理的形式(其实aop就足矣),注入到spring的生命周期中。此文记录一下springboot的思路(spring其实一样,只是少了@ConditionalOnClass,@ConditionalOnProperty等动态注解)

2. 工程结构

在这里插入图片描述

  1. springboot-iot-starter springboot-starter 父工程
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
      <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/>
    </parent>

    <groupId>com.zjq.iot.springboot</groupId>
    <artifactId>springboot-iot-starter</artifactId>
    <version>MR.TieXinQiao</version>
    <name>springboot-iot-starter</name>
    <description>SpringBoot自定义starter</description>
    <packaging>pom</packaging>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <modules>
        <module>iot-spring-boot-starter</module>
        <module>iot-spring-boot-starter-autoconfigurer</module>
    </modules>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!--提供source-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-source-plugin</artifactId>
                <configuration>
                    <attach>true</attach>
                </configuration>
                <executions>
                    <execution>
                        <phase>compile</phase>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

这里面 只有一个 spring-boot-starter 的 依赖

  1. iot-spring-boot-starter 启动器工程
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.zjq.iot.springboot</groupId>
        <artifactId>springboot-iot-starter</artifactId>
        <version>MR.TieXinQiao</version>
    </parent>

    <groupId>com.zjq.iot.springboot</groupId>
    <artifactId>iot-spring-boot-starter</artifactId>
    <description>
        启动器(starter)是一个空的jar文件,仅仅提供辅助性依赖管理,这些依赖需要自动装配或其他类库。
    </description>

    <dependencies>
        <!--引入 自己写的 iot autoconfigure-->
        <dependency>
            <groupId>com.zjq.iot.springboot</groupId>
            <artifactId>iot-spring-boot-starter-autoconfigurer</artifactId>
            <version>MR.TieXinQiao</version>
        </dependency>
        <!--如果当前starter 还需要其他的类库就在这里引用-->
    </dependencies>
</project>

这是一个空的springboot工程,仅仅提供辅助性依赖管理,这些依赖需要自动装配或其他类库。

  1. iot-spring-boot-starter-autoconfigurer iot的自动装工程

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>


    <parent>
        <artifactId>springboot-iot-starter</artifactId>
        <groupId>com.zjq.iot.springboot</groupId>
        <version>MR.TieXinQiao</version>
    </parent>


    <groupId>com.zjq.iot.springboot</groupId>
    <artifactId>iot-spring-boot-starter-autoconfigurer</artifactId>
    <description>iot自动装配类</description>

    <dependencies>
        <!--web 类-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--‐导入配置文件处理器,配置文件进行绑定就会有提示-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <!--阿里云Java SDK公共包Maven依赖坐标 -->
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-core</artifactId>
            <version>4.5.6</version>
            <!--optional  true 依赖不会 传递  一般配合 @ConditionalOnClass(XXX.class) 使用 -->
            <optional>false</optional>
        </dependency>
        <!--阿里云 物联网  AMQP-->
        <!-- amqp 1.0 qpid client -->
        <dependency>
            <groupId>org.apache.qpid</groupId>
            <artifactId>qpid-jms-client</artifactId>
            <version>0.47.0</version>
        </dependency>
        <!-- util for base64-->
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.10</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
            <version>1.18.8</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.9</version>
        </dependency>

    </dependencies>


</project>

工程目录结构

├─ main
│ 	├─ java
│ 	│ 	└─ com
│ 	│ 		└─ zjq
│ 	│ 			└─ iot
│ 	│ 				├─ anno
│ 	│ 				│ 	├─ EnableIotEngine.java
│ 	│ 				│ 	├─ Invocation.java
│ 	│ 				│ 	└─ IotService.java
│ 	│ 				├─ controller
│ 	│ 				│ 	└─ IndexController.java
│ 	│ 				├─ DirectoryTreeV1.java
│ 	│ 				├─ init
│ 	│ 				│ 	├─ AmqpJavaClient.java
│ 	│ 				│ 	└─ IotInit.java
│ 	│ 				├─ IotAutoConfitguration.java
│ 	│ 				├─ properties
│ 	│ 				│ 	└─ IotProperties.java
│ 	│ 				├─ proxy
│ 	│ 				│ 	└─ ProxyFactory.java
│ 	│ 				├─ register
│ 	│ 				│ 	├─ BeanCache.java
│ 	│ 				│ 	├─ IotEngine.java
│ 	│ 				│ 	└─ IotScannerRegistrar.java
│ 	│ 				└─ utils
│ 	│ 					├─ ClassUtil.java
│ 	│ 					└─ SpringBeanUtil.java
│ 	└─ resources
│ 		└─ META-INF
│ 			└─ spring.factories
└─ test
	└─ java
  1. 总结
    在这里插入图片描述
  • 启动器(starter)是一个空的jar文件,仅仅提供辅助性依赖管理,这些依赖可能用于自动装配或其他类库。
  • 需要专门写一个类似spring-boot-autoconfigure的配置模块
    用的时候只需要引入启动器starter,就可以使用自动配置了

命名规范
官方命名空间

  • 前缀:spring-boot-starter-
  • 模式:spring-boot-starter-模块名
  • 举例:spring-boot-starter-web、spring-boot-starter-jdbc

自定义命名空间

  • 后缀:-spring-boot-starter
  • 模式:模块-spring-boot-starter
  • 举例:mybatis-spring-boot-starter

3. iot的自动装配

  1. IotAutoConfitguration类 iot-springboot 自动装配启动类
    通过spi机制注入

spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.zjq.iot.IotAutoConfitguration

IotAutoConfitguration

/* ━━━━━━佛祖保佑━━━━━━
 *                  ,;,,;
 *                ,;;'(    社
 *      __      ,;;' ' \   会
 *   /'  '\'~~'~' \ /'\.)  主
 * ,;(      )    /  |.     义
 *,;' \    /-.,,(   ) \    码
 *     ) /       ) / )|    农
 *     ||        ||  \)
 *     (_\       (_\
 * ━━━━━━永无BUG━━━━━━
 * @author :zjq
 * @date :2021/3/26 13:32
 * @description: TODO               iot-springboot  自动装配
 * @version: V1.0
 * @slogan: 天下风云出我辈,一入代码岁月催
 */
@Configuration
@ConditionalOnClass(IotProperties.class)
@ConditionalOnProperty(prefix = "aliyun", name = "isUse", havingValue = "true")
@EnableConfigurationProperties(IotProperties.class)
//@ConditionalOnMissingBean(IotAutoConfitguration.class)      // 当给定的在 bean不存在时,则实例化当前Bean
public class IotAutoConfitguration {

    @Autowired
    private IotProperties iotProperties;

    /**
     * 启动 加载器
     *
     * @return
     */
    @Bean
    public IotInit iotInit() {
        return new IotInit();
    }

    /**
     * 加载 页面
     *
     * @return
     */
    @Bean
    public IndexController indexController() {
        return new IndexController(iotProperties);
    }

    @Bean
    public SpringBeanUtil springBeanUtil() {
        return new SpringBeanUtil();
    }

}

  1. 自定义注解
    EnableIotEngine
/* ━━━━━━佛祖保佑━━━━━━
 *                  ,;,,;
 *                ,;;'(    社
 *      __      ,;;' ' \   会
 *   /'  '\'~~'~' \ /'\.)  主
 * ,;(      )    /  |.     义
 *,;' \    /-.,,(   ) \    码
 *     ) /       ) / )|    农
 *     ||        ||  \)
 *     (_\       (_\
 * ━━━━━━永无BUG━━━━━━
 * @author :zjq
 * @date :2021/2/22 22:12
 * @description: TODO               自定义扫描注解
 * @version: V1.0
 * @slogan: 天下风云出我辈,一入代码岁月催
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Inherited
@Import({IotScannerRegistrar.class})  // 必须在这里导入  IotScannerRegistrar
public @interface EnableIotEngine {

    String[] value() default {};

    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};

//    Class<? extends Annotation> annotationClass() default Annotation.class;
}

IotService

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Inherited
public @interface IotService {

    /**
     * 名称
     *
     * @return
     */
    String value() default "";

    /**
     * 增强的类
     *
     * @return
     */
//    String refrence() default "com.huanbao.iot.service.iot.AbstractDefaultIotService";
    String refrence() default "";

    String methodName() default "";   // 因为iot参数已经限制死了,所以没必要
}
  1. Invocation 主要是学习dubbo的Invocation
@Data
public class Invocation {

    /**
     * 代理工厂
     */
    private ProxyFactory handler;

    /**
     * 代理类
     */
    private Object proxy;

    /**
     * 方法
     */
    private Method method;

    /**
     *  参数
     */
//    private Object[] args;

    /**
     * 关联逻辑类
     */
    private String reference;

    /**
     *  方法名
     */
    private String methodName;


}
  1. 自定义扫描

BeanCache类 (想到了spring解决循环依赖用了三级缓存,这里为了不每次从容器中获取bean,遂写了个单例的bean的缓存池)

/* ━━━━━━佛祖保佑━━━━━━
 *                  ,;,,;
 *                ,;;'(    社
 *      __      ,;;' ' \   会
 *   /'  '\'~~'~' \ /'\.)  主
 * ,;(      )    /  |.     义
 *,;' \    /-.,,(   ) \    码
 *     ) /       ) / )|    农
 *     ||        ||  \)
 *     (_\       (_\
 * ━━━━━━永无BUG━━━━━━
 * @author :zjq
 * @date :2021/3/5 16:35
 * @description: TODO           bean 的缓存池
 * @version: V1.0
 * @slogan: 天下风云出我辈,一入代码岁月催
 */
public class BeanCache {


    private volatile static BeanCache instance;


    /* 一级缓存 存 定义好的bean*/
    public volatile static Map<String, Invocation> singletonObjects;

    public static BeanCache getInstance() {
        if (instance == null) {
            synchronized (BeanCache.class) {
                if (instance == null) {
                    instance = new BeanCache();
                }
            }
        }
        return instance;
    }


    /**
     * 获取值
     */
    public Invocation getBean(String beanName) {
        if (null == singletonObjects) {
            throw new RuntimeException("iot bean is null");
        }
        Invocation invocation = singletonObjects.get(beanName);
        return invocation;
    }

    public Map<String, Invocation> getBeanMap() {
        if (null == singletonObjects) {
            throw new RuntimeException("iot bean is null");
        }
        return singletonObjects;
    }

    /**
     * 赋值
     */
    public void putBean(String beanName, Invocation invocation) {
        if (null == singletonObjects) {
            singletonObjects = new ConcurrentHashMap<>();
        }
        singletonObjects.put(beanName, invocation);
    }


}

IotScannerRegistrar类 ( 在业务工程springboot的启动类上会加上 @EnableIotEngine(value = “com.huanbao.iot”) ,所以此处就是拿到注解的包路径,扫描包,并且把加了@IotService(value = “myHeXuIotService”, refrence = “com.huanbao.iot.service.iot.CommonIotServiceImpl”, methodName = “doLogic”) 这样的注解的类注册到容器,然后走代理增强逻辑)

/* ━━━━━━佛祖保佑━━━━━━
 *                  ,;,,;
 *                ,;;'(    社
 *      __      ,;;' ' \   会
 *   /'  '\'~~'~' \ /'\.)  主
 * ,;(      )    /  |.     义
 *,;' \    /-.,,(   ) \    码
 *     ) /       ) / )|    农
 *     ||        ||  \)
 *     (_\       (_\
 * ━━━━━━永无BUG━━━━━━
 * @author :zjq
 * @date :2021/3/5 14:44
 * @description: TODO                        注册 自定义 注解 bean
 * @version: V1.0
 * @slogan: 天下风云出我辈,一入代码岁月催
 */
@Slf4j
public class IotScannerRegistrar implements ImportBeanDefinitionRegistrar {


    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        /**
         * 从我们传入的配置类中来解析@EnableIotEngine 注解信息,然后吧 EnableIotEngine 注解的属性转化为 AnnotationAttributes类型(Map类型)
         */
        AnnotationAttributes iotScanAttrs = AnnotationAttributes
                .fromMap(importingClassMetadata.getAnnotationAttributes(EnableIotEngine.class.getName()));

        if (iotScanAttrs != null) {
            /**
             * 调用重写的方法registerBeanDefinitions generateBaseBeanName(importingClassMetadata, 0) 我们即将注册的bean定义的名称
             * com.huanbao.iot.IotServerApplication#IotScannerRegistrar#0
             *
             */
            registerBeanDefinitions(iotScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));
        }
    }


    void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {


        /**
         * 解析@EnableIotEngine 扫描的的包或者是 class 对象
         */
        String[] basePackageArr = annoAttrs.getStringArray("value");
        if (!(basePackageArr.length > 0)) {
            throw new RuntimeException("iot scan cannot be null");
        }

        for (int i = 0; i < basePackageArr.length; i++) {
            List<Class<?>> clsList = ClassUtil.getAllClassByPackageName(basePackageArr[i]);
            for (Class n : clsList) {
                IotService annotation = (IotService) n.getAnnotation(IotService.class);
                if (annotation != null) {

                    // 注册成 bean定义
                    RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(n);
                    if (StringUtils.isNotBlank(annotation.value())) {
                        registry.registerBeanDefinition(annotation.value(), rootBeanDefinition);
                    } else {
                        registry.registerBeanDefinition(ClassUtil.getBeanName(n.getName()), rootBeanDefinition);
                    }

                    String refrence = annotation.refrence();
                    String referenceMethodName = annotation.methodName();

                    if (StringUtils.isBlank(refrence)) {
                        log.error("############ " + n.getName() + "iot bean has not assign refrence");
                        break;
                    }

                    if (StringUtils.isBlank(referenceMethodName)) {
                        log.error("############ " + n.getName() + "iot bean has not assign referenceMethodName");
                        break;
                    }

                    try {
                        // 获取接口 这里先只能获取jdk proxy ? why, 因为 cglib 我不会,后续 有空加入
                        Class<?>[] interfaces = n.newInstance().getClass().getInterfaces();
                        // 拿到代理 走增强
                        ProxyFactory handler = new ProxyFactory(n.newInstance(), refrence, referenceMethodName);
                        Object proxy = handler.getProxy(n, interfaces);
                        // 拿到接口方法
                        Method method = proxy.getClass().getInterfaces()[0].getMethod("doIotService", String.class, String.class, String.class);
                        method.setAccessible(true);

                        Invocation invocation = new Invocation();
                        invocation.setHandler(handler);
                        invocation.setProxy(proxy);
                        invocation.setMethod(method);
                        invocation.setReference(annotation.refrence());
                        invocation.setMethodName(referenceMethodName);

                        if (StringUtils.isNotBlank(annotation.value())) {
                            BeanCache.getInstance().putBean(annotation.value(), invocation);
                        } else {
                            BeanCache.getInstance().putBean(ClassUtil.getBeanName(n.getName()), invocation);
                        }

                        log.info("############ " + n.getName() + "  has already registed ");

                    } catch (Exception e) {
                        log.error("iot bean has error");
                        e.printStackTrace();
                    }
                }
            }
        }

        log.info("###########  all iot beans has already registed ##############");
    }

    /**
     * 方法实现说明:生成我们bean定义的名称
     *
     * @param importingClassMetadata 传入的配置类
     * @param index                  默认传入0
     * @author:xsls
     * @return: com.huanbao.iot.IotServerApplication#IotScannerRegistrar#0
     * @exception:
     * @date:2021/03/05 15:29
     */
    private static String generateBaseBeanName(AnnotationMetadata importingClassMetadata, int index) {
        return importingClassMetadata.getClassName() + "#" + IotScannerRegistrar.class.getSimpleName() + "#" + index;
    }
}
  1. 项目启动,从beanCache中获取bean
    IotInit类 实现 CommandLineRunner 项目启动时调用
/* ━━━━━━佛祖保佑━━━━━━
 *                  ,;,,;
 *                ,;;'(    社
 *      __      ,;;' ' \   会
 *   /'  '\'~~'~' \ /'\.)  主
 * ,;(      )    /  |.     义
 *,;' \    /-.,,(   ) \    码
 *     ) /       ) / )|    农
 *     ||        ||  \)
 *     (_\       (_\
 * ━━━━━━永无BUG━━━━━━
 * @author :zjq
 * @date :2021/3/26 9:32
 * @description: TODO           spring 的 启动类  顺序在 扫包 之后
 * @version: V1.0
 * @slogan: 天下风云出我辈,一入代码岁月催
 */
@Slf4j
public class IotInit implements CommandLineRunner {

    @Autowired
    private IotProperties iotProperties;


    @Override
    public void run(String... args) throws Exception {
        log.info("the iotProperties is :{} ", iotProperties);

        if (iotProperties.getIsUse().equals("true")) {
            log.info("############ 项目启动,开启 iot 服务端 #################");
            String clientId = getLocalIp();
            AmqpJavaClient.start(iotProperties.getAccessKeyId(), iotProperties.getAccessSercret(),
                    iotProperties.getConsumerGroupId(), iotProperties.getIotInstanceId(), clientId,
                    iotProperties.getConnectionUrl());
            log.info("############# iot 服务端 启动成功 ##################");
        }

    }


    public String getLocalIp() {
        InetAddress localHost = null;
        try {
            localHost = Inet4Address.getLocalHost();
        } catch (UnknownHostException e) {
            log.error(e.getMessage(), e);
        }
        // 返回格式为:xxx.xxx.xxx
        String ip = localHost.getHostAddress();
        return ip;
    }


}

AmqpJavaClient类 阿里云的demo,这里只贴出关键部分代码,调用IotEngine.doInvoke(args);走代理增强

  /**
     * 在这里处理您收到消息后的具体业务逻辑。
     */
    private static void processMessage(Message message) {
        try {
            byte[] body = message.getBody(byte[].class);
            String content = new String(body);
            String topic = message.getStringProperty("topic");
            String messageId = message.getStringProperty("messageId");
            LocalDateTime now = LocalDateTime.now();
            String time = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss").format(now);
            log.info("**************************************************" + time);
            log.info("receive message"
                    + ", topic = " + topic
                    + ", messageId = " + messageId
                    + ", content = " + content);

            /**
             * TODO   plan 0  自定义增强  自动 扫描
             */
            Object[] args = new Object[]{content, topic, messageId};
            IotEngine.doInvoke(args);


        } catch (Exception e) {
            log.error("processMessage occurs error ", e);
        }
    }

IotEngine类 从beanCache中取出需要调用的bean,从Invocation中获取各种参数,然后走代理

@Slf4j
public class IotEngine {


    public static volatile boolean isOpenIot = false;

    /**
     * 船新版本
     *
     * @param args
     */
    public static void doInvoke(Object[] args) {
        if (!isOpenIot) {
            log.warn("################## the iot server is closed ... so nothing is doinvoke .... ##############");
            return;
        }
        Map<String, Invocation> beanMap = BeanCache.getInstance().getBeanMap();

        if (!(beanMap.size() > 0)) {
            log.warn("################### the server has not any iot beans #######################");
            return;
        }
        for (Map.Entry<String, Invocation> m : beanMap.entrySet()) {
            Invocation v = m.getValue();
            ProxyFactory handler = v.getHandler();
            Method method = v.getMethod();
            Object proxy = v.getProxy();
            try {
                // 调用
                handler.invoke(proxy, method, args);
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
        }
    }

}
  1. 调用代理
    其实这里想要实现逻辑,直接用aop即可。但是很多框架底层都是反射和代理,遂自己也写了玩玩。目前只是实现了jdk的代理,cglib的还未加入。
    这里的本意: 比如 我们在自己的业务工程中,有一个业务TestService,一般我们会加上@Service注解。现在替换成@IotService(value = “testService”, refrence = “com.huanbao.iot.service.iot.CommonIotServiceImpl”, methodName = “doLogic”),即这个TestService
  2. 既注入到容器,可以被其他bean 用@Autowrised 调用;
  3. 又可以实现自己的业务逻辑,而关于iot的逻辑,则由指定的 refrence 这个类的 method来实现。
    这样,不管我们有多少个设备厂商,只关注与自己的业务逻辑。如果不同厂商的iot逻辑一样,则指定相同的 refrence 就好。
/* ━━━━━━佛祖保佑━━━━━━
 *                  ,;,,;
 *                ,;;'(    社
 *      __      ,;;' ' \   会
 *   /'  '\'~~'~' \ /'\.)  主
 * ,;(      )    /  |.     义
 *,;' \    /-.,,(   ) \    码
 *     ) /       ) / )|    农
 *     ||        ||  \)
 *     (_\       (_\
 * ━━━━━━永无BUG━━━━━━
 * @author :zjq
 * @date :2021/2/22 22:16
 * @description: TODO           代理类实现
 * @version: V1.0
 * @slogan: 天下风云出我辈,一入代码岁月催
 */
@Slf4j
public class ProxyFactory<T> implements InvocationHandler {


    //目标类
    private Object target;

    // 增强类
    private String reference;

    // 增强类调用的方法
    private String referenceMethodName;


    public ProxyFactory(Object target, String reference, String referenceMethodName) {
        this.target = target;
        this.reference = reference;
        this.referenceMethodName = referenceMethodName;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        doReference(args);
        // issue 这里要不要调用 ? 调用,自动实现业务逻辑; 不调用,其实也可,但是参数已经限制了必须是iot那几个
        return method.invoke(this.target, args);
    }

    @SuppressWarnings("unchecked")
    public <T> T getProxy(Class clazz, Class<?>[] interfaceClass) {
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), interfaceClass, this);
    }

    /**
     * 调用 增强类 即iot增强业务类
     *
     * @param args
     */
    public void doReference(Object[] args) {
        try {
            if (StringUtils.isBlank(reference)) {
                log.warn("#############   reference  class is not assign ##################");
                return;
            }
            if (StringUtils.isBlank(referenceMethodName)) {
                log.warn("#############   methodName  is not assign ##################");
                return;
            }
            Class<?> referenceClass = Class.forName(reference);
            // issue 参数这里拿的还是原方法的,要不要指定 reference 参数? 其实感觉没必要
            Method abstarctMethod = referenceClass.getMethod(referenceMethodName, String.class, String.class, String.class);
//            Method[] declaredMethods = referenceClass.getInterfaces()[0].getDeclaredMethods();
//            Method abstarctMethod = declaredMethods[0];
            Service service = referenceClass.getAnnotation(Service.class);
            Object referenceBean;
            if (service != null) {
                referenceBean = SpringBeanUtil.getBean(referenceClass);
            } else {
                // 不一定非要实现 spring 自己的service ,如果是普通的类,直接 newInstance 就好
                referenceBean = referenceClass.newInstance();
            }
            abstarctMethod.invoke(referenceBean, args);
        } catch (Exception e) {
            log.warn("#############   reference class ot found ##################");
            e.printStackTrace();
        }

    }


    @SuppressWarnings("unchecked")
    public static <T> T getProxy(final Class interfaceClass) {
        return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return null;
            }
        });

    }


}

4. 测试调用

  1. 在自己的业务工程中加入
 <!--自己写的 iot-springboot -->
        <dependency>
            <groupId>com.zjq.iot.springboot</groupId>
            <artifactId>iot-spring-boot-starter</artifactId>
            <version>MR.TieXinQiao</version>
        </dependency>
  1. 在springboot启动类上加上 @EnableIotEngine(value = “com.huanbao.iot”)

  2. 在ymk文件中加上 阿里云iot参数
    在这里插入图片描述

  3. 写一个 业务Service,加上 @IotService 注解 ,然后再指定的refrence 类上实现iot逻辑

@IotService(value = "myHeXuIotService", refrence = "com.huanbao.iot.service.iot.CommonIotServiceImpl", methodName = "doLogic")
public class HeXuBusinessServiceImpl implements LogicService {

    @Autowired
    private RunDataMapper runDataMapper;

    @Override
    public void doIotService(String content, String topic, String messageId) {
        System.out.println("实现 合续自己的   业务逻辑");
    }


}

5. 总结

这个是 第一次 自己 根据 spring特性封装的一个自定义starter,里面用到了

  1. spring生命周期
  2. springboot的启动原理
  3. 动态代理
  4. 反射
    等知识点。写的有点凌乱,在此谨记录一下过程。

但是,有时候真的不要过度封装,就比如这个业务,其实直接普通的调用方法也可以,真的没必要,只是有空,遂研究研究~

6.结语

世上无难事,只
怕有心人,每天积累一点点,fighting!!!
2021,加油,fighting,希望可以少些crud啦!

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值