记第一次springboot中间件开发—阿里云iot与自定义springboot starter集成封装
每天多学一点点~
话不多说,这就开始吧…
1.前言
写代码至今也有三年了,一直有个梦想,能自己写一套中间件,与spring,springboot集成。正好这次公司有个项目需要集成阿里云的iot,虽然上云很简单,但是想着如何自己做成封装成一个jar,然后在业务工程中直接引用,下次有相同的项目来了,只需要引用jar,在yml中配置阿里云的参数即可,做到高扩展。
遂模仿springboot stater形式,参考dubbo的 Invocation理念,用动态代理的形式(其实aop就足矣),注入到spring的生命周期中。此文记录一下springboot的思路(spring其实一样,只是少了@ConditionalOnClass,@ConditionalOnProperty等动态注解)
2. 工程结构
- 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 的 依赖
- 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工程,仅仅提供辅助性依赖管理,这些依赖需要自动装配或其他类库。
- 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
- 总结
- 启动器(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的自动装配
- 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();
}
}
- 自定义注解
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参数已经限制死了,所以没必要
}
- 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;
}
- 自定义扫描
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;
}
}
- 项目启动,从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();
}
}
}
}
- 调用代理
其实这里想要实现逻辑,直接用aop即可。但是很多框架底层都是反射和代理,遂自己也写了玩玩。目前只是实现了jdk的代理,cglib的还未加入。
这里的本意: 比如 我们在自己的业务工程中,有一个业务TestService,一般我们会加上@Service注解。现在替换成@IotService(value = “testService”, refrence = “com.huanbao.iot.service.iot.CommonIotServiceImpl”, methodName = “doLogic”),即这个TestService - 既注入到容器,可以被其他bean 用@Autowrised 调用;
- 又可以实现自己的业务逻辑,而关于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. 测试调用
- 在自己的业务工程中加入
<!--自己写的 iot-springboot -->
<dependency>
<groupId>com.zjq.iot.springboot</groupId>
<artifactId>iot-spring-boot-starter</artifactId>
<version>MR.TieXinQiao</version>
</dependency>
-
在springboot启动类上加上 @EnableIotEngine(value = “com.huanbao.iot”)
-
在ymk文件中加上 阿里云iot参数
-
写一个 业务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,里面用到了
- spring生命周期
- springboot的启动原理
- 动态代理
- 反射
等知识点。写的有点凌乱,在此谨记录一下过程。
但是,有时候真的不要过度封装,就比如这个业务,其实直接普通的调用方法也可以,真的没必要,只是有空,遂研究研究~
6.结语
世上无难事,只
怕有心人,每天积累一点点,fighting!!!
2021,加油,fighting,希望可以少些crud啦!