# spring-smart-di **Repository Path**: greamrod/spring-smart-di ## Basic Information - **Project Name**: spring-smart-di - **Description**: No description available - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 1 - **Created**: 2024-10-23 - **Last Updated**: 2024-12-25 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # spring-smart-di Spring 动态依赖注入扩展, 不再局限于简单的@Autowired 扩展注入注解如下 - @SmartAutowired - @AutowiredProxySPI - @AutowiredSPI # 2、快速开始 引入依赖 ```xml io.github.burukeyou spring-smart-di-all 0.2.0 ``` 在spring配置类上标记 @EnableSmartDI 注解 ## 2.1 @SmartAutowired注解使用 ### 2.1.1 默认的注入逻辑 与Autowired区别是 1)当依赖的具体的类为非接口时,而是具体类并且该具体类还多个子类,并且具体类本身是Bean,会将具体类注入。 如果@Autowired 会抛出异常因为有多个实现类 2)可以根据指定环境变量属性的值进行注入。并且该属性值可以为 @BeanAliasName 配置的别名 比如有以下三个SpringBean ```java @Component public class Weather { } @Component @BeanAliasName("天气A服务商") public class WeatherA extends Weather { } @Component @BeanAliasName("天气B服务商") public class WeatherB extends Weather { } ``` 然后使用@SmartAutowired注解进行依赖注入依然能够注入成功,注入的是Weather本身。 当然如果Weather是非SpringBean,与@Autowired一样会抛出有多个实现类的异常无法自动注入。 ```java @SmartAutowired private Weather weather; ``` 为什么要加入这个注入逻辑呢,因为我们的做抽象设计的时候,往往会写出非常复杂的类图关系的,一个类往往有多个实现类。 但我想注入那个类又不想搭配@Qualifier注解去硬编码指定注入的beanName(硬编码这对代码洁癖的来说非常重要),也不想每次使用@Primary 去指定默认注入哪个,因为是确定的,所以"智能化"了一点去帮助我们的依赖注入 ### 2.1.2 使用环境变量去配置注入的Bean 假如有以下配置 ```yml weather: impl: 天气A服务商 ``` 然后指定环境变量的key,则会使用该属性的具体值去执行依赖注入。 这个属性值可以是beanName,也可以是全路径类名,也可以是使用@BeanAliasName注解标记的名字 ```java @SmartAutowired("${weather.impl}") private Weather weather; ``` ## 2.2 AutowiredProxySPI注解 当需要注入某个接口有多个实现类,可以根据该接口类标记的`@ProxySPI`注解、`@EnvironmentProxySPI`去标记动态注入的逻辑 让我们以一个场景去看如何使用, 假如系统接入了多个短信服务商,然后用户可以在页面动态的切换不同的服务商, 如果让我们手写会如何实现。 - 第一步先在某个位置(不管是nacos还是数据库)配置当前使用的服务商的对应值比如 `sms.impl = "某腾短信"` - 第二步,在代码里执行发短信的时候,手动获取该`sms.impl`对应的服务商的实现类,伪代码可能如下 ```java // 1、获取当前使用的服务商 String name = get("sms.impl"); // 2、获取对应的实现类 SmsService smsService = springContext.getBean(name); // 3、使用smsService执行具体业务逻辑 smsService.sendMsg() ``` 但是现在让我们来 `@AutowiredProxySPI` 是如何自动屏蔽这些细节 需要搭配`@ProxySPI`来处理,假设我们的当前使用的服务商在环境变量中`${sms.impl}`, 便可以使用默认实现 `@EnvironmentProxySPI` 比如存在配置 ```yaml sms: impl: 某移短信服务 ``` 然后在接口类上标记`@EnvironmentProxySPI`即可, 会根据该属性值去获取具体注入哪个实现类。然后就可以使用我们的 @AutowiredProxySPI注解进行依赖注入了,并且注入的是一个代理对象,代理的逻辑是每次执行SmsService的任何方法时,都会去重新获取一次 当前使用的实现类,所以即使你修改了`${sms.impl}`配置,也能实时生效而无需重启服务器。 ```java @EnvironmentProxySPI("${sms.impl}") public interface SmsService { } @BeanAliasName("某腾短信服务") @Component public class ASmsService implements SmsService { } @BeanAliasName("某移短信服务") @Component public class BSmsService implements SmsService { } // 依赖注入 @AutowiredProxySPI private SmsService smsService; ``` `@EnvironmentProxySPI`是用来配置环境变量相关注入逻辑,如果想要自定义配置比如在数据库中可实现自己的ProxySPI注解。 比如`自定义DBProxySPI注解`,并标记上@ProxySPI实现并指定具体AnnotationProxyFactory即可。 然后DBProxySPI就可以像@EnvironmentProxySPI一样去使用了,下面是实现的伪代码 ```java @Inherited @Target({ElementType.FIELD,ElementType.METHOD,ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @ProxySPI(DbProxyFactory.class) public @interface DBProxySPI { String value(); } @Component public class DbProxyFactory implements AnnotationProxyFactory { @Autowired private SysConfigMapper sysConfigDao; @Override public Object getProxy(Class targetClass,DBProxySPI spi) { // todo 根据注解从数据库获取要注入的实现类 String configName = sysConfigDao.getConfig(spi.value()); return springContext.getBean(configName); } } ``` ## 2.3 AutowiredSPI注解 与@AutowiredProxySPI注解在使用上基本一致,区别是@AutowiredProxySPI注入的是代理对象, AutowiredSPI注入的当前使用的 具体的实现类,只在服务器启动的时候注入,也就是说更改配置后需要重启服务切换实现类才会生效。