IOC 使用 (XML 引用)

定义一个接口和它的实现类

public interface FactoryInterface {
    String getName();
}

@Setter
public class FactoryInterfaceImpl implements FactoryInterface {

    private String name;

    @Override
    public String getName() {
        return name;
    }
}

在resource下新建beans.xml 文件 并且设置成员变量name值

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user" class="cn.pencilso.study.studyioc.xmlbean.FactoryInterfaceImpl">
        <property name="name" value="大白"/>
    </bean>
</beans>

获取bean对象 最后输出 “大白”

ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("beans.xml");
FactoryInterface factoryInterface = classPathXmlApplicationContext.getBean(FactoryInterface.class);
String name = factoryInterface.getName();
System.out.println("name:" + name);

使用注解导入xml的bean方式 @ImportResource

@SpringBootApplication
@ImportResource(locations = {"classpath:beans.xml"})
public class StudyIocApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(StudyIocApplication.class, args);
        FactoryInterface factoryInterface = run.getBean(FactoryInterface.class);
        String name = factoryInterface.getName();
        System.out.println("name:" + name);
    }
}

IOC使用 (注解+过滤器)

创建配置类, 添加注解,扫描base包为 "cn.pencilso.study" 并且导入有注解Repository 和 Service注解的Bean。

useDefaultFilters 默认过滤器关闭。设置为false 。

@ComponentScan(basePackages = {"cn.pencilso.study"}
        , includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Repository.class, Service.class})
}, useDefaultFilters = false)
@Configuration
public class StudyIocConfig {


}

添加测试需要的bean

@Repository
public class UserDao {
}

@Service
public class UserService {
}

@Component
public class HttpPlugin {
}

尝试打印加载到容器里的bean对象

AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
String[] beanDefinitionNames = annotationConfigApplicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
    System.out.println(beanDefinitionName);
}


-----最后输出   userDao  userService已经在容器里了 但是 httpPlugin 并没有在容器里。

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
studyIocConfig
userDao
userService

自定义过滤器 只要是 Component Service Repository 注解的都引入到bean容器

public class CompoentFilter implements TypeFilter {
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        String className = classMetadata.getClassName();
        System.out.println("className:" + className);

        Set<String> annotationTypes = metadataReader.getAnnotationMetadata().getAnnotationTypes();
        if (annotationTypes.contains(Component.class.getName()) ||
                annotationTypes.contains(Repository.class.getName()) ||
                annotationTypes.contains(Service.class.getName())
        ) {
            return true;
        }
        return false;
    }
}
@ComponentScan(basePackages = {"cn.pencilso.study"}
        , includeFilters = {
        @ComponentScan.Filter(type = FilterType.CUSTOM, value = {CompoentFilter.class})
}, useDefaultFilters = false)
@Configuration
public class StudyIocConfig {
}

最后输出 这次httpPlugin也出来了 。

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
studyIocConfig
httpPlugin
userDao
userService

排除bean引入,如果需要排除某些bean加载到容器里,可以用excludeFilters 这个过滤器,使用方法跟includeFilters 一致。

@ComponentScan(basePackages = {"cn.pencilso.study"}
        , excludeFilters = {
        @ComponentScan.Filter(type = FilterType.CUSTOM, value = {CompoentFilter.class})
}, useDefaultFilters = false)
@Configuration
public class StudyIocConfig {

}

IOC 容器单例、多例

Bean 默认是单例的,且是饿汉模式。容器启动的时候,就会加载Bean对象。

public class UserModel {
    public UserModel() {
        System.out.println("user model Initialization");
    }
}

@Configuration
public class StudyIocConfig {

    @Bean
    public UserModel userModel() {
        return new UserModel();
    }
}

public class StudyIocApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
    }
}

-- 输出内容为  可以看到 并没有去调用Bean,但是Bean也创建了。这是因为默认为饿汉单例。
user model Initialization
  

-- 也可以设置为懒汉模式  通过@Lazy 注解 如下 
-- 通过添加Lazy注解后,该Bean将会在第一次使用的的时候才会创建对象,容器启动的时候则不会创建对象。
@Configuration
public class StudyIocConfig {
    @Lazy
    @Bean
    public UserModel userModel() {
        return new UserModel();
    }
}

接下来多次获取Bean 并且比对hashcode

public class StudyIocApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
        UserModel user1 = annotationConfigApplicationContext.getBean(UserModel.class);
        UserModel user2 = annotationConfigApplicationContext.getBean(UserModel.class);
        System.out.println(user1 == user2);
    }
}
--比对结果输出为true  表明hashcode 一致
user model Initialization
true

如果说有多例的需求应该怎么做呢,可以采用 @Scope 注解

singleton: 单例的(默认)

prototype: 多例的

request: 同一次请求

session: 同一个会话级别

@Configuration
public class StudyIocConfig {

    @Scope(value = "prototype")
    @Bean
    public UserModel userModel() {
        return new UserModel();
    }
}

public class StudyIocApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
        UserModel user1 = annotationConfigApplicationContext.getBean(UserModel.class);
        UserModel user2 = annotationConfigApplicationContext.getBean(UserModel.class);
        System.out.println(user1 == user2);
    }
}

--最后输出结果,构造方法执行了两次,比对结果也是false,这时候已经是多例了。 每次获取bean都是新的对象。
user model Initialization
user model Initialization
false  

Conditional 通过条件控制bean是否被加载到容器

public class StudyIocConfig {

    @Bean
    public UserModel userModel() {
        return new UserModel();
    }

    @Conditional(value = {CustomCondition.class})
    @Bean
    public UserDao userDao() {
        return new UserDao();
    }
}

//条件为 容器里已经加载了userModel  
public class CustomCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return context.getBeanFactory().containsBean("userModel");
    }
}

public class StudyIocApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
        String[] beanDefinitionNames = annotationConfigApplicationContext.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }
}
--最后打印输出  有三个自定义bean,studyIocConfig、userModel、userDao
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
studyIocConfig
userModel
userDao
  
  
 --尝试去掉 userModel的bean 后打印如下  这个时候userDao已经不会加载到容器了,因为条件是先加载userModel到容器。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
studyIocConfig

Import 导入bean对象

/**
 * 可以直接指定需要导入的bean class 比如说UserModel.class
 * 其次可以通过ImportSelector 接口来进行批量装载bean
 * 也可以通过ImportBeanDefinitionRegistrar 对象进行bean定义,并且注册。
 */
@Import(value = {UserModel.class, CustomImportSelector.class, CustomImportBeanDefinitionRegistrar.class})
public class StudyIocConfig {

}

/**
 * 这些类型根据给定的选择条件(通常是一个或多个批注属性)确定应导入哪个类
 */
public class CustomImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{UserDao.class.getName()};
    }
}

/**
 * 根据需要注册bean定义
 */
public class CustomImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //构造bean定义
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(HttpPlugin.class);
        //注册到bean容器
        registry.registerBeanDefinition("httpPlugin", rootBeanDefinition);
    }
}

public class StudyIocApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
        String[] beanDefinitionNames = annotationConfigApplicationContext.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }
}

--输出结果 UserModel、UserDao、httpPlugin  这几个bean都已经被加载到容器当中。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
studyIocConfig
cn.pencilso.study.studyioc.model.UserModel
cn.pencilso.study.studyioc.dao.UserDao
httpPlugin

自定义FactoryBean

public class StudyIocConfig {

    @Bean
    public UserFacotryBean userFacotryBean() {
        return new UserFacotryBean();
    }
}

/**
 * 自定义Bean工厂
 * 应用场景可以在初始化这个bean的时候需要初始化其他的组件或者依赖之类。
 * 而且用了工厂后,默认则不是饿汉单例了,需要用到的时候才会创建。
 */
public class UserFacotryBean implements FactoryBean<UserModel> {
    @Override
    public UserModel getObject() throws Exception {
        return new UserModel();
    }

    @Override
    public Class<UserModel> getObjectType() {
        return UserModel.class;
    }
    /**
     * 是否为单例
     *
     * @return
     */
    @Override
    public boolean isSingleton() {
        return true;
    }
}


public class StudyIocApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
        Object userBean = annotationConfigApplicationContext.getBean("userFacotryBean");
        //userModel
        System.out.println("userBean:"+userBean);
        //获取工厂本身的bean
        Object userFacotryBean = annotationConfigApplicationContext.getBean("&userFacotryBean");
        System.out.println("userFacotryBean:"+userFacotryBean);
    }
}

--最后输出结果
user model Initialization
userBean:cn.pencilso.study.studyioc.model.UserModel@25359ed8
userFacotryBean:cn.pencilso.study.studyioc.facotry.UserFacotryBean@21a947fe

Bean 的生命周期

在bean的注解中,有两个属性,一个是initMethod,还有一个是destroyMethod。

如果指定了的话,那么在bean初始化的时候会执行initMethod,bean销毁时会执行destroyMethod。

应用场景在于用作一些数据初始化和数据的释放。但是在bean为多例的情况下,ioc则不会管理bean的销毁方法。

注解方式管理生命周期

@Service
public class UserService {
    public UserService(){
        System.out.println("user service constructor");
    }

    @PostConstruct
    public void init(){
        System.out.println("user service init");
    }

    @PreDestroy
    public void destroy(){
        System.out.println("user service destroy");
    }
}


public class StudyIocConfig {
    @Bean
    public UserService userService() {
        return new UserService();
    }
}


public class StudyIocApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
        UserService user1 = annotationConfigApplicationContext.getBean(UserService.class);
        UserService user2 = annotationConfigApplicationContext.getBean(UserService.class);
        annotationConfigApplicationContext.close();
    }
}

--输出结果如下,先执行构造方法,其次是PostConstruct注解所标记的初始化方法,最后销毁的时候执行destroy 方法。
user service constructor
user service init
user service destroy
 

尝试把bean修改为多例,看是否还能再执行destroy 方法。

public class StudyIocConfig {
    @Scope("prototype")
    @Bean
    public UserService userService() {
        return new UserService();
    }
}

再次运行代码后结果输出如下。执行了两遍构造器和初始化方法,因为是多例模式,而我调用了两次,所以产生了两个对象。并且并没有调用PreDestroy所标记的销毁方法。

user service constructor
user service init
user service constructor
user service init

通过bean注解管理生命周期

public class StudyIocConfig {

    /**
     * 指定初始化方法为init,指定销毁方法为destroy
     * @return
     */
    @Bean(initMethod = "init", destroyMethod = "destroy")
    public UserService userService() {
        return new UserService();
    }
}

@Service
public class UserService {
    public UserService(){
        System.out.println("user service constructor");
    }

    public void init(){
        System.out.println("user service init");
    }

    public void destroy(){
        System.out.println("user service destroy");
    }
}

--最后执行结果如下,先执行的构造器,其次是初始化方法,销毁则执行destroy。效果与注解方式一致。
user service constructor
user service init
user service destroy

Bean的后置处理器 BeanPostProcessor

public class CustomBeanPostProcessor implements BeanPostProcessor {

    /**
     * Bean 的初始化方法之前
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + "  postProcessBeforeInitialization");
        return bean;
    }

    /**
     * Bean 的初始化方法之后
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + "  postProcessAfterInitialization");
        return bean;
    }
}


public class StudyIocConfig {

    /**
     * 指定初始化方法为init,指定销毁方法为destroy
     * @return
     */
    @Bean(initMethod = "init", destroyMethod = "destroy")
    public UserService userService() {
        return new UserService();
    }
    
    @Bean
    public CustomBeanPostProcessor beanPostProcessor(){
        return new CustomBeanPostProcessor();
    }
}


    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
        UserService userService = annotationConfigApplicationContext.getBean(UserService.class);
        annotationConfigApplicationContext.close();
    }

--输出结果如下,先执行构造方法,其次是后置处理器的postProcessBeforeInitialization方法,再然后是bean的初始化方法,初始化方法执行后是,后置处理器的postProcessAfterInitialization方法,销毁bean时执行destroy方法。
--应用场景可以在于拦截bean修改某些成员变量,不过我认为这种场景业务中还是很少遇到。
  
user service constructor
userService  postProcessBeforeInitialization
user service init
userService  postProcessAfterInitialization
user service destroy

贴一个初始化bean的源码截图,该代码块在AbstractAutowireCapableBeanFactory这个类中。

QQ20200419-193506@2x

Bean工厂后置处理器 BeanFactoryPostProcessor

Bean工厂后置处理器,在bean解析,但是未初始化之前调用。代码示例如下

/**
 * 自定义Bean工厂后置处理器
 */
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        //获取userService的bean定义
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");
      	//设置为多例模式
        beanDefinition.setScope("prototype");
    }
}

@Configuration
public class StudyIocConfig {

    @Bean
    public UserService userService() {
        return new UserService();
    }

    @Bean
    public CustomBeanFactoryPostProcessor customBeanFactoryPostProcessor(){
        return new CustomBeanFactoryPostProcessor();
    }
}

public class StudyIocApplication {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
        UserService userService1 = annotationConfigApplicationContext.getBean(UserService.class);
        UserService userService2 = annotationConfigApplicationContext.getBean(UserService.class);
        System.out.println(userService1==userService2);
    }
}

--输出结果如下,表明该bean已经变为多例了。即使在@Bean注解声明为单例后,也可以通过后置处理器修改为多例,或者懒加载等等。
false

InitializingBean接口使用

由bean实现的接口,这些bean需要在{@link BeanFactory}设置完所有属性*后作出反应:例如,执行自定义初始化,*或仅仅检查是否设置了所有必需属性。(翻译自源码注释)

也就是说 该接口是用来校验属性或者执行某些自定义初始化的,直接上代码。

/**
 * 这里定义了一个name属性,并且在afterPropertiesSet方法中进行校验,如果name为空的话,则抛出异常。
 */
@Data
public class TestInitializingBean implements InitializingBean {
    private String name;
    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.isTrue(!StringUtils.isEmpty(name), "name can not be null");
    }
}


public class StudyIocConfig {

    @Bean
    public TestInitializingBean initializingBean() {
        return new TestInitializingBean();
    }
}

public class StudyIocApplication {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
    }
}


--执行出错,输出如下。
Caused by: java.lang.IllegalArgumentException: name can not be null
	at org.springframework.util.Assert.isTrue(Assert.java:118)
	at cn.pencilso.study.studyioc.bean.TestInitializingBean.afterPropertiesSet(TestInitializingBean.java:16)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1855)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1792)
	... 11 more

对name属性进行赋值后则正常装载bean

@Data
public class TestInitializingBean implements InitializingBean {
    @Value("大白")
    private String name;
    
    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.isTrue(!StringUtils.isEmpty(name), "name can not be null");
    }
}

PropertySource 加载配置文件

user.properties 文件

user.nikename= 大白

Java 代码

@PropertySource(value = {"classpath:user.properties"},encoding = "UTF-8")
@Configuration
public class StudyIocConfig {
    @Bean
    public UserModel userModel(){
        return new UserModel();
    }
}

@Data
public class UserModel {
    @Value("#{30-10}")
    private int age;

    @Value("${user.nikename}")
    public String name;
}

@SpringBootApplication
public class StudyIocApplication {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
        UserModel bean = annotationConfigApplicationContext.getBean(UserModel.class);
        System.out.println("bean:"+bean);
    }
}

--最后输出结果
 bean:UserModel(age=20, name=大白)

但是其实PropertySource 默认是不支持加载yaml文件的,那么如果要加载yaml文件的话,需要多一些处理。

yaml文件

user:
  nikename: 大白

Java 代码实现

/**
 * 自定义yaml Factory
 */
public class YamlPropertySourceFactory extends DefaultPropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        List<PropertySource<?>> sources = new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource());
        return sources.get(0);
    }
}

@PropertySource(value = {"classpath:user.yaml"},encoding = "UTF-8",factory = YamlPropertySourceFactory.class)
@Configuration
public class StudyIocConfig {
    @Bean
    public UserModel userModel(){
        return new UserModel();
    }
}

--最后结果输出如下
bean:UserModel(age=20, name=大白)

@Autowired

@Data
public class UserModel {
    public String name;
}

@Configuration
public class StudyIocConfig {
    @Bean
    public UserModel userModel(){
        UserModel userModel = new UserModel();
        userModel.setName("小白");
        return userModel;
    }

    @Bean
     public UserService userService(){
        return new UserService();
    }
}

public class UserService {

    @Autowired
    private UserModel user;

    public void printlnUser() {
        System.out.println("userModel:" + user);
    }

}

public class StudyIocApplication {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
        UserService userService = annotationConfigApplicationContext.getBean(UserService.class);
        userService.printlnUser();
    }
}

--同一种类型,单个bean的正常获取,输出结果如下。
userModel:UserModel(name=小白)

尝试同一种类型的多个bean对象,这里在config类中声明了两个bean对象。

如下,name属性对应的数据不同,userModel对应的name是小白,userModel2对应的name是大白。

@Configuration
public class StudyIocConfig {

    /**
     * bean 名字为userModel
     * @return
     */
    @Bean
    public UserModel userModel(){
        UserModel userModel = new UserModel();
        userModel.setName("小白");
        return userModel;
    }

    /**
     * bean 名字为userModel2
     * @return
     */
    @Bean
    public UserModel userModel2(){
        UserModel userModel = new UserModel();
        userModel.setName("大白");
        return userModel;
    }

    @Bean
     public UserService userService(){
        return new UserService();
    }
}

这时候再运行的话会报错,Spring会告诉你,你要加载的bean对象有两个。而Spring不知道应该帮你加载哪个。

这是因为Autowired这个注解默认是按照类的class来进行匹配的,而它匹配到两个,并且你没有指定加载某一个bean名字,则会异常。

那么解决方案也比较简单,有两种,一种是直接通过变量名与bean名字一致的情况下,如下。

public class UserService {
    /**
     * 对应bean名字  userModel
     */
    @Autowired
    private UserModel userModel;
    /**
     * 对应bean名字  userModel2
     */
    @Autowired
    private UserModel userModel2;

    public void printlnUser() {
        System.out.println("userModel:" + userModel);
        System.out.println("userModel2:" + userModel2);
    }
}

输出结果如下,符合预期。

userModel:UserModel(name=小白)
userModel2:UserModel(name=大白)

那么再讲第二种方案,第二种方案的话需要配合Qualifier 注解,如下。

public class UserService {
    /**
     * 对应bean名字  userModel
     */
    @Qualifier("userModel")
    @Autowired
    private UserModel userModel;
    /**
     * 对应bean名字  userModel
     */
    @Qualifier("userModel")
    @Autowired
    private UserModel userModel2;

    public void printlnUser() {
        System.out.println("userModel:" + userModel);
        System.out.println("userModel2:" + userModel2);
    }
}

最后输出结果如下,可以看到两次输出的name都是小白,这是因为已经固定了按照bean的名字也就是userModel来获取bean对象。

userModel:UserModel(name=小白)
userModel2:UserModel(name=小白)

另外如果需要允许null值的话,在使用注解的时候将required设置为false 例如: @Autowired(required=false)

@Resource

Resource 这个注解是jdk所提供的,那么这个注解所带来装载顺序。

1、优先匹配bean名字,如果没有指定名字的话,会获取变量名作为bean名字去匹配 ;

2、倘若bean名字匹配不到,则根据类型也就是type进行唯一性匹配 ;

3、如果同时指定了type,和bean名字,则匹配这两个条件的bean;

示范如下

public class XiaoMingModel extends UserModel{
}

public class UserService {
    /**
     * 按照固定的bean名字获取对象
     */
    @Resource(name = "userModel")
    private UserModel userModel;
    /**
     * 根据指定的class类型 进行匹配装载
     */
    @Resource(type = XiaoMingModel.class)
    private UserModel xiaoMingModel;

    public void printlnUser() {
        System.out.println("userModel:" + userModel);
        System.out.println("userModel2:" + xiaoMingModel);
    }
}

@Configuration
public class StudyIocConfig {

    /**
     * bean 名字为userModel
     *
     * @return
     */
    @Bean
    public UserModel userModel() {
        UserModel userModel = new UserModel();
        userModel.setName("小白");
        return userModel;
    }

    /**
     * bean 名字为userModel2
     *
     * @return
     */
    @Bean
    public UserModel userModel2() {
        UserModel userModel = new UserModel();
        userModel.setName("大白");
        return userModel;
    }

    @Bean
    public XiaoMingModel xiaoming() {
        XiaoMingModel xiaoMingModel = new XiaoMingModel();
        xiaoMingModel.setName("小明同学");
        return xiaoMingModel;
    }

    @Bean
    public UserService userService() {
        return new UserService();
    }
}

最后输出结果如下,变量userModel指定加载的bean是“userModel” ,所以这里打印小白是符合预期的。

其次,变量userModel2指定加载的bean类型是XiaoMingModel.class,所以这里打印小明同学也是符合预期的。

userModel:UserModel(name=小白)
userModel2:UserModel(name=小明同学)

@Profile

根据环境来决定是否加载ben对象,常见于开发环境、测试环境、生产环境,切换bean实现。

在配置文件中声明当前的环境

spring:
  profiles:
    active: test

其次在代码中配置bean,并且配置Profile注解环境。

@PropertySource(value = {"classpath:application.yaml"}, encoding = "UTF-8", factory = YamlPropertySourceFactory.class)
@Configuration
public class StudyIocConfig {
    /**
     * 环境为prod时,则装载
     * @return
     */
    @Profile("prod")
    @Bean
    public UserModel userModelProd() {
        UserModel userModel = new UserModel();
        userModel.setName("大白");
        return userModel;
    }

    /**
     * 环境为dev 或者  test时 则装载
     * @return
     */
    @Profile(value = {"dev", "test"})
    @Bean
    public UserModel userModelDev() {
        UserModel userModel = new UserModel();
        userModel.setName("小白");
        return userModel;
    }

    @Bean
    public UserService userService() {
        return new UserService();
    }
}

public class UserService {

    @Autowired
    private UserModel userModel;

    public void printlnUser() {
        System.out.println("userModel:" + userModel);
    }
}

--最后运行结果输出如下,在配置文件里设置环境为 active:test ,所以它最后装载的bean是userModelDev 。

--输出name==小白,是符合预期的。

userModel:UserModel(name=小白)

EmbeddedValueResolverAware

可以通过实现该接口,获取配置文件值。

yaml配置文件如下

mysql:
  jdbcUrl: jdbc:mysql://127.0.0.1:3306/xxxxxx

Java代码如下

public class UserService implements EmbeddedValueResolverAware {

    private String jdbcUrl;

    /**
     * 将StringValueResolver设置为用于解析嵌入的定义值
     *
     * @param resolver
     */
    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        jdbcUrl = resolver.resolveStringValue("${mysql.jdbcUrl}");
    }

    public void printlnJdbc() {
        System.out.println("jdbcUrl:" + jdbcUrl);
    }
}

@PropertySource(value = {"classpath:application.yaml"}, encoding = "UTF-8", factory = YamlPropertySourceFactory.class)
@Configuration
public class StudyIocConfig {

    @Bean
    public UserService userService() {
        return new UserService();
    }
}

public class StudyIocApplication {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
        UserService userService = annotationConfigApplicationContext.getBean(UserService.class);
        userService.printlnJdbc();
    }
}

最后结果输出如下,结果是符合预期的。

jdbcUrl:jdbc:mysql://127.0.0.1:3306/xxxxxx