04.Spring Framework 之组件注入

本文深入解析Spring框架中的依赖注入机制,包括@Autowired、@Qualifier、@Primary等注解的使用方式及环境搭建,同时介绍了@Resource、@Inject和Aware接口的应用场景。

1. @Autowired & @Qualifier & @Primary

1.1 @Autowired
1.1.1 使用方式

@Autowired:自动注入,如果没有就会报错,可以指定 required=false 避免报错

  • 默认优先按照类型去容器中找对应的组件:applicationContext.getBean(Xxx.class),找到就赋值,如果找到多个相同类型的组件,再将属性的名称作为组件的 id 去容器中查找:applicationContext.getBean("xxx")

  • 标注在构造器上:如果组件只有一个有参构造器,这个有参构造器的 @Autowired 可以省略,参数位置的组件是从容器中获取的

  • 标注在方法位置:@Autowired 可以使用在 setter 方法上,Spring 容器创建当前对象时会自动调用方法完成赋值,方法使用的自定义类型参数的值从 IOC 容器中获取

  • 标注在参数上:@Bean 注解标注方法中的参数会从容器中获取,此时的 @Autowired 可以省略

1.1.2 环境搭建

代码已经上传至 https://github.com/masteryourself-tutorial/tutorial-spring ,详见 tutorial-spring-framework/tutorial-spring-framework-injection/tutorial-spring-framework-injection-autowired 工程

1. PersonController
@Controller
@ToString
public class PersonController {

    @Autowired
    private PersonService personService2;

}
2. StudentController
@Data
public class StudentController {

    private PersonService personService;

    /**
     * 只要一个有参的构造参数
     *
     * @param personService
     */
    public StudentController(PersonService personService) {
        this.personService = personService;
    }

}
3. TeacherController
@Controller
@ToString
public class TeacherController {

    private PersonService personService;

    /*@Autowired*/
    public TeacherController(/*@Autowired*/ PersonService personService2) {
        this.personService = personService2;
    }
}
4. UserController
@Controller
@ToString
public class UserController {

    private PersonService personService;

    @Autowired
    public void setPersonService(PersonService personService1) {
        this.personService = personService1;
    }
}
5. SpringConfig
@Configuration
@ComponentScan("pers.masteryourself.tutorial.spring.framework.injection")
public class SpringConfig {

    @Bean
    public PersonService personService1() {
        return new PersonService("1");
    }

    @Bean
    public PersonService personService2() {
        return new PersonService("2");
    }

    @Bean
    public StudentController studentController(/*@Autowired*/ PersonService personService1){
        return new StudentController(personService1);
    }

}
6. AutowiredApplication
public class AutowiredApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        PersonController personController = context.getBean(PersonController.class);
        // PersonController(personService2=PersonService(label=2))
        System.out.println(personController);
        UserController userController = context.getBean(UserController.class);
        // UserController(personService=PersonService(label=1))
        System.out.println(userController);
        TeacherController teacherController = context.getBean(TeacherController.class);
        // TeacherController(personService=PersonService(label=2))
        System.out.println(teacherController);
        StudentController studentController = context.getBean(StudentController.class);
        // StudentController(personService=PersonService(label=1))
        System.out.println(studentController);
    }

}
1.2 @Qualifier
1.2.1 使用方式

使用 @Qualifier 指定需要装配的组件的 id,而不是使用属性名

1.2.2 环境搭建

代码已经上传至 https://github.com/masteryourself-tutorial/tutorial-spring ,详见 tutorial-spring-framework/tutorial-spring-framework-injection/tutorial-spring-framework-injection-qualifier 工程

1. PersonController
@ToString
@Component
public class PersonController {

    @Autowired
    @Qualifier("personService1")
    private PersonService personService2;

}
1.3 @Primary
1.3.1 使用方式

让 Spring 进行自动装配的时候,默认使用首选的 bean;也可以强制使用 @Qualifier 明确指定需要装配的 bean 的名字

1.3.2 环境搭建

代码已经上传至 https://github.com/masteryourself-tutorial/tutorial-spring ,详见 tutorial-spring-framework/tutorial-spring-framework-injection/tutorial-spring-framework-injection-primary 工程

1. SpringConfig
@Configuration
@ComponentScan("pers.masteryourself.tutorial.spring.framework.injection")
public class SpringConfig {

    @Bean
    @Primary
    public PersonService personService1() {
        return new PersonService("1");
    }

    @Bean
    public PersonService personService2() {
        return new PersonService("2");
    }

}

2. @Resource & @Inject

2.1 @Resource

@Resource 是 java 规范定义的注解,可以和 @Autowired 一样实现自动装配功能,默认是按照组件名称进行装配的

不支持 @Primary 功能,不支持 @Autowired(reqiured=false)

2.2 @Inject

需要导入 javax.inject 的包,和 Autowired 的功能一样,但没有 required=false 的功能

3. Aware

3.1 使用方式

自定义组件实现 xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件

3.2 环境搭建

代码已经上传至 https://github.com/masteryourself-tutorial/tutorial-spring ,详见 tutorial-spring-framework/tutorial-spring-framework-injection/tutorial-spring-framework-injection-aware 工程

1. MyAware
@Component
public class MyAware implements ApplicationEventPublisherAware, BeanFactoryAware, EnvironmentAware,
        EmbeddedValueResolverAware, BeanNameAware, ApplicationContextAware, ImportAware {

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println(String.format("{%s} ==========================>>>>>>>>>>>>>>>>>>>>>>>>>>>", "setBeanFactory"));
        System.out.println(beanFactory.getBean("myAware"));
    }

    @Override
    public void setBeanName(String beanName) {
        System.out.println(String.format("{%s} ==========================>>>>>>>>>>>>>>>>>>>>>>>>>>>", "setBeanName"));
        System.out.println("beanName is:" + beanName);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println(String.format("{%s} ==========================>>>>>>>>>>>>>>>>>>>>>>>>>>>", "setApplicationContext"));
        System.out.println("applicationContext 是否属于 AnnotationConfigApplicationContext:" +
                (applicationContext instanceof AnnotationConfigApplicationContext));
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        System.out.println(String.format("{%s} ==========================>>>>>>>>>>>>>>>>>>>>>>>>>>>", "setApplicationEventPublisher"));
        System.out.println("applicationEventPublisher is:" + applicationEventPublisher);
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        System.out.println(String.format("{%s} ==========================>>>>>>>>>>>>>>>>>>>>>>>>>>>", "setEmbeddedValueResolver"));
        System.out.println(resolver.resolveStringValue("my os is [${os.name}]"));
    }

    @Override
    public void setEnvironment(Environment environment) {
        System.out.println(String.format("{%s} ==========================>>>>>>>>>>>>>>>>>>>>>>>>>>>", "setEnvironment"));
        System.out.println("当前 encoding:" + environment.getProperty("file.encoding"));
    }

    @Override
    public void setImportMetadata(AnnotationMetadata annotationMetadata) {
        System.out.println(String.format("{%s} ==========================>>>>>>>>>>>>>>>>>>>>>>>>>>>", "setImportMetadata"));
        Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(Import.class.getName());
        System.out.println("获取注解信息:" + attributes);
    }
}

4. @Profile

4.1 使用方式

@Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件

加了环境标识的 bean,只有这个环境被激活的时候才能注册到容器中。默认是 default 环境

写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效

没有标注环境标识的 bean 在任何环境下都是加载的

4.2 环境搭建

代码已经上传至 https://github.com/masteryourself-tutorial/tutorial-spring ,详见 tutorial-spring-framework/tutorial-spring-framework-injection/tutorial-spring-framework-injection-profile 工程

1. SpringConfig
@Configuration
public class SpringConfig {

    @Profile("default")
    @Bean
    public Datasource localDatasource(){
        return new Datasource("local");
    }

    @Profile("dev")
    @Bean
    public Datasource devDatasource(){
        return new Datasource("dev");
    }

    @Profile("test")
    @Bean
    public Datasource testDatasource(){
        return new Datasource("test");
    }

    @Profile("prod")
    @Bean
    public Datasource prodDatasource(){
        return new Datasource("prod");
    }

}
4.3 激活
1. 通过启动参数激活

通过在 VM options 中配置启动参数 -Dspring.profiles.active=dev,test

2. 通过代码激活
public static void main(String[] args) {
    // 1. 创建一个 AnnotationConfigApplicationContext
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    // 2. 设置需要激活的环境
    context.getEnvironment().setActiveProfiles("dev", "test");
    // 3. 注册主配置类
    context.register(SpringConfig.class);
    // 4. 刷新容器
    context.refresh();
    String[] names = context.getBeanNamesForType(Datasource.class);
    for (String name : names) {
        // devDatasource
        // testDatasource
        System.out.println(name);
    }
}
### 解决 Spring Framework 中 CacheManager 注入失败的问题 Spring Framework 的缓存管理器 (`CacheManager`) 是用于管理和提供缓存实例的核心组件之一。如果 `CacheManager` 注入失败,通常可能是由于配置错误、依赖冲突或其他上下文初始化问题引起的。 以下是可能导致注入失败的原因以及解决方案: #### 1. 配置不完整或错误 确保在 Spring 上下文中正确声明并定义了 `CacheManager` 实现类。例如,在 XML 配置文件中可以这样设置 Guava 缓存管理器[^2]: ```xml <bean id="cacheManager" class="org.springframework.cache.guava.GuavaCacheManager"> <property name="caches"> <set> <value>default</value> <value>books</value> </set> </property> </bean> ``` 对于基于 Java Config 的方式,则可以通过如下代码实现: ```java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.google.common.cache.CacheBuilder; @Configuration public class CacheConfig { @Bean public org.springframework.cache.CacheManager cacheManager() { return new org.springframework.cache.concurrent.ConcurrentMapCacheManager("default", "books"); } } ``` #### 2. 缺少必要的依赖项 确认项目中已引入所需的库版本。例如,如果您使用的是 Guava 缓存管理器,则需要添加以下 Maven 或 Gradle 依赖项: Maven: ```xml <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>30.1-jre</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> ``` Gradle: ```gradle implementation 'com.google.guava:guava:30.1-jre' implementation 'org.springframework.boot:spring-boot-starter-cache' ``` #### 3. 方法级缓存未启用 当您尝试通过注解(如 `@Cacheable`)来利用缓存功能时,需确保启用了方法级别的缓存支持。可以在主应用程序类上标注 `@EnableCaching` 来激活此特性[^1]: ```java import org.springframework.cache.annotation.EnableCaching; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @EnableCaching public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` #### 4. 多个 CacheManager 定义引发歧义 如果有多个 `CacheManager` 被注册到 Spring 应用程序上下文中而没有指定默认使用的那个,可能会导致注入失败。在这种情况下,应明确指明哪个 `CacheManager` 将被优先考虑。例如,您可以为特定的方法或者字段显式地注入所需的一个 `CacheManager` 实例: ```java @Autowired @Qualifier("customCacheManager") private CacheManager customCacheManager; ``` 另外还可以通过自定义 Bean 名称的方式区分不同的缓存管理者对象。 --- ### 总结 上述分析涵盖了常见的几种原因及其对应的修复措施。实际操作过程中可以根据具体的异常堆栈信息进一步排查具体位置和细节上的差异之处。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值