作者:小凯
分享、让自己和他人都能有所收获!
今天和大家分享spring依赖注入,@Autowired 注入会用,@Resource 注入知道,但在项目看到一个没使用这2个注解的,直接在构造函数写了个两个入参 public AwardController(List awardServices, Map
二、实践案例
实例化注解:
1. 构造注入&List、Map
private final List awardServices;
private final Map awardServiceMap;
public AwardController(List awardServices, Map awardServiceMap) {
this.awardServices = awardServices;
this.awardServiceMap = awardServiceMap;
}
public Response distributeAward(@RequestParam String userId, @RequestParam String awardKey) {
try {
log.info("发放奖品服务 userId:{} awardKey:{}", userId, awardKey);
awardServiceMap.get(awardKey);
return Response.builder()
.code("0000")
.info("调用成功")
.data("发奖完成")
.build();
} catch (Exception e) {
return Response.builder()
.code("0001")
.info("调用失败")
.build();
}
}
2. 空注入判断
public class NullAwardService implements IAwardService {
@Override
public void doDistributeAward(String userId) {
}
}
@Autowired(required = false)
private NullAwardService nullAwardService;
3. 优先实例化
@Slf4j
@Service("openai_model")
// Primary 首选 Bean 对象标记
@Primary
@Order(1)
public class OpenAIModelAwardService implements IAwardService {
@Override
public void doDistributeAward(String userId) {
log.info("发奖服务,OpenAI 模型奖励 {}", userId);
}
}
@Resource
private IAwardService awardService;
@Test
public void test_awardService_primary() {
log.info("测试结果 {}", awardService.getClass());
}
// 测试结果 class cn.bugstack.xfg.dev.tech.domain.impl.OpenAIModelAwardService
4. 检测创建,避免重复
@Bean("redisson01")
// 当 Spring 应用上下文中不存在某个特定类型的 Bean 时,才会创建和配置标注了 @ConditionalOnMissingBean 的 Bean 对象
@ConditionalOnMissingBean
public String redisson01() {
return "模拟的 Redis 客户端 01";
}
@Bean("redisson02")
// 当 Spring 应用上下文中不存在某个特定类型的 Bean 时,才会创建和配置标注了 @ConditionalOnMissingBean 的 Bean 对象
@ConditionalOnMissingBean
public String redisson02() {
return "模拟的 Redis 客户端 02";
}
5. 配置是否创建对象
@Data
@ConfigurationProperties(prefix = "sdk.config", ignoreInvalidFields = true)
public class AutoConfigProperties {
/** 状态:open = 开启、close 关闭 */
private boolean enable;
/** 转发地址 */
private String apiHost;
/** 可以申请 sk-*** */
private String apiSecretKey;
}
@Bean
@ConditionalOnProperty(value = "sdk.config.enabled", havingValue = "true", matchIfMissing = false)
public String createTopic(@Qualifier("redisson01") String redisson, AutoConfigProperties properties) {
log.info("redisson {} {} {}", redisson, properties.getApiHost(), properties.getApiSecretKey());
return redisson;
}
sdk:
config:
enabled: false
apiHost: https://open.bigmodel.cn/
apiSecretKey: d570f7c5d289cdac2abdfdc562e39f3f.trqz1dH8ZK6ED7Pg
6. 自定义Condition,判断是否实例化对象
public class BeanCreateCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String active = System.getProperty("isOpenWhitelistedUsers");
return null != active && active.equals("true");
}
}
@Bean
@Conditional(BeanCreateCondition.class)
public List whitelistedUsers() {
return new ArrayList() {{
add("user001");
add("user002");
add("user003");
}};
}
static {
// BeanCreateCondition 会检测这个值,确定是否创建对象
System.setProperty("isOpenWhitelistedUsers", "false");
}
@Autowired(required = false)
@Qualifier("whitelistedUsers")
private List whitelistedUsers;
7. 根据环境配置实例化对象
@Slf4j
@Service
// 用于根据配置环境实例化 Bean 对象
@Profile({"prod", "test"})
@Lazy
public class AliPayAwardService implements IAwardService {
public AliPayAwardService() {
log.info("如一些支付场景,必须指定上线后才能实例化");
}
@Override
public void doDistributeAward(String userId) {
log.info("红包奖励 {}", userId);
}
}
spring:
config:
name: xfg-dev-tech-spring-dependency-injection
profiles:
active: dev
8. 引入 Spring 配置
@Slf4j
@SpringBootApplication
@Configurable
@PropertySource("classpath:properties/application.properties")
@ImportResource("classpath:spring/spring.xml")
@EnableScheduling
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
@Slf4j
public class SpringBeanTest {
public SpringBeanTest() {
log.info("我是通过 Spring 配置文件实例化的 Bean 对象");
}
}
9. 原型对象
@Component
@Scope("prototype")
public class LogicChain {
}
@Resource
private ApplicationContext applicationContext;
@Test
public void test_prototype() {
log.info("测试结果: {}", applicationContext.getBean(LogicChain.class).hashCode());
log.info("测试结果: {}", applicationContext.getBean(LogicChain.class).hashCode());
}
10. 其他注解
三、源码分析
这里的 Map 注入比较有特点,这里把它的流程和核心代码给大家描述下,方便感兴趣源码的伙伴,可以去看下源码调试跟进。
在 Spring 框架中,依赖注入(DI)是通过一系列的步骤和组件来实现的。对于构造函数注入,特别是注入 Map 类型的依赖,Spring 需要处理以下几个关键步骤:
1. 核心源码
具体到构造函数注入 Map 类型的依赖,Spring 主要通过以下源码来处理:
#1.1 AutowiredAnnotationBeanPostProcessor
public class AutowiredAnnotationBeanPostProcessor implements BeanPostProcessor {
// 省略其他代码
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
}
AutowiredAnnotationBeanPostProcessor 是处理依赖注入的核心类之一。它会扫描 Bean 的构造函数、字段和方法上的 @Autowired 注解,并进行相应的依赖注入。
1.2 ConstructorResolver
public class ConstructorResolver {
// 省略其他代码
public BeanWrapper autowireConstructor(
final String beanName, final RootBeanDefinition mbd, Constructor>[] chosenCtors, final Object[] explicitArgs) {
// 省略其他代码
Constructor> constructorToUse = null;
ArgumentsHolder argsHolderToUse = null;
Object[] argsToUse = null;
// 省略其他代码
for (Constructor> candidate : candidates) {
Class>[] paramTypes = candidate.getParameterTypes();
if (argsToUse == null) {
// 省略其他代码
argsHolder = createArgumentArray(
beanName, mbd, resolvedValues, bw, paramTypes, paramNames, getUserDeclaredConstructor(candidate), autowiring);
}
// 省略其他代码
}
// 省略其他代码
BeanWrapperImpl bw = new BeanWrapperImpl(beanInstance);
initBeanWrapper(bw);
return bw;
}
}
ConstructorResolver 是负责解析和调用构造函数的类。它会根据 BeanDefinition 和构造函数的参数类型,选择合适的构造函数并进行实例化。
1.3 DefaultListableBeanFactory
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry {
// 省略其他代码
@Override
protected Map findAutowireCandidates(String beanName, Class> requiredType, DependencyDescriptor descriptor) {
String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType);
Map result = new LinkedHashMap<>(candidateNames.length);
for (String candidate : candidateNames) {
if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
result.put(candidate, getBean(candidate));
}
}
if (result.isEmpty() && !indicatesMultipleBeans(requiredType)) {
DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
for (String candidate : candidateNames) {
if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor)) {
result.put(candidate, getBean(candidate));
}
}
}
return result;
}
}
DefaultListableBeanFactory 是 Spring 中最常用的 BeanFactory 实现类。它负责管理 Bean 的定义和生命周期,并提供依赖查找和注入的功能。
2. 具体流程