SpringBoot IOC 容器

IOC 使用 (XML 引用)

定義一個(gè)接口和它的實(shí)現(xiàn)類

public interface FactoryInterface {
    String getName();
}

@Setter
public class FactoryInterfaceImpl implements FactoryInterface {

    private String name;

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

在resource下新建beans.xml 文件 并且設(shè)置成員變量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對(duì)象 最后輸出 “大白”

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

使用注解導(dǎo)入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使用 (注解+過濾器)

創(chuàng)建配置類, 添加注解,掃描base包為 "cn.pencilso.study" 并且導(dǎo)入有注解Repository 和 Service注解的Bean盲镶。

useDefaultFilters 默認(rèn)過濾器關(guān)閉安聘。設(shè)置為false 。

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


}

添加測(cè)試需要的bean

@Repository
public class UserDao {
}

@Service
public class UserService {
}

@Component
public class HttpPlugin {
}

嘗試打印加載到容器里的bean對(duì)象

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


-----最后輸出   userDao  userService已經(jīng)在容器里了 但是 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 這個(gè)過濾器嫉父,使用方法跟includeFilters 一致沛硅。

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

}

IOC 容器單例、多例

Bean 默認(rèn)是單例的绕辖,且是餓漢模式摇肌。容器啟動(dòng)的時(shí)候,就會(huì)加載Bean對(duì)象仪际。

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);
    }
}

-- 輸出內(nèi)容為  可以看到 并沒有去調(diào)用Bean围小,但是Bean也創(chuàng)建了。這是因?yàn)槟J(rèn)為餓漢單例树碱。
user model Initialization
  

-- 也可以設(shè)置為懶漢模式  通過@Lazy 注解 如下 
-- 通過添加Lazy注解后肯适,該Bean將會(huì)在第一次使用的的時(shí)候才會(huì)創(chuàng)建對(duì)象,容器啟動(dòng)的時(shí)候則不會(huì)創(chuàng)建對(duì)象成榜。
@Configuration
public class StudyIocConfig {
    @Lazy
    @Bean
    public UserModel userModel() {
        return new UserModel();
    }
}

接下來多次獲取Bean 并且比對(duì)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);
    }
}
--比對(duì)結(jié)果輸出為true  表明hashcode 一致
user model Initialization
true

如果說有多例的需求應(yīng)該怎么做呢框舔,可以采用 @Scope 注解

singleton: 單例的(默認(rèn))

prototype: 多例的

request: 同一次請(qǐng)求

session: 同一個(gè)會(huì)話級(jí)別

@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);
    }
}

--最后輸出結(jié)果,構(gòu)造方法執(zhí)行了兩次赎婚,比對(duì)結(jié)果也是false刘绣,這時(shí)候已經(jīng)是多例了。 每次獲取bean都是新的對(duì)象挣输。
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();
    }
}

//條件為 容器里已經(jīng)加載了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);
        }
    }
}
--最后打印輸出  有三個(gè)自定義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 后打印如下  這個(gè)時(shí)候userDao已經(jīng)不會(huì)加載到容器了停士,因?yàn)闂l件是先加載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 導(dǎo)入bean對(duì)象

/**
 * 可以直接指定需要導(dǎo)入的bean class 比如說UserModel.class
 * 其次可以通過ImportSelector 接口來進(jìn)行批量裝載bean
 * 也可以通過ImportBeanDefinitionRegistrar 對(duì)象進(jìn)行bean定義完丽,并且注冊(cè)向瓷。
 */
@Import(value = {UserModel.class, CustomImportSelector.class, CustomImportBeanDefinitionRegistrar.class})
public class StudyIocConfig {

}

/**
 * 這些類型根據(jù)給定的選擇條件(通常是一個(gè)或多個(gè)批注屬性)確定應(yīng)導(dǎo)入哪個(gè)類
 */
public class CustomImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{UserDao.class.getName()};
    }
}

/**
 * 根據(jù)需要注冊(cè)bean定義
 */
public class CustomImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //構(gòu)造bean定義
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(HttpPlugin.class);
        //注冊(cè)到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);
        }
    }
}

--輸出結(jié)果 UserModel、UserDao舰涌、httpPlugin  這幾個(gè)bean都已經(jīng)被加載到容器當(dāng)中猖任。
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工廠
 * 應(yīng)用場(chǎng)景可以在初始化這個(gè)bean的時(shí)候需要初始化其他的組件或者依賴之類。
 * 而且用了工廠后瓷耙,默認(rèn)則不是餓漢單例了朱躺,需要用到的時(shí)候才會(huì)創(chuàng)建刁赖。
 */
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);
    }
}

--最后輸出結(jié)果
user model Initialization
userBean:cn.pencilso.study.studyioc.model.UserModel@25359ed8
userFacotryBean:cn.pencilso.study.studyioc.facotry.UserFacotryBean@21a947fe

Bean 的生命周期

在bean的注解中,有兩個(gè)屬性,一個(gè)是initMethod,還有一個(gè)是destroyMethod么伯。

如果指定了的話逃魄,那么在bean初始化的時(shí)候會(huì)執(zhí)行initMethod迂猴,bean銷毀時(shí)會(huì)執(zhí)行destroyMethod。

應(yīng)用場(chǎng)景在于用作一些數(shù)據(jù)初始化和數(shù)據(jù)的釋放。但是在bean為多例的情況下,ioc則不會(huì)管理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();
    }
}

--輸出結(jié)果如下,先執(zhí)行構(gòu)造方法良蛮,其次是PostConstruct注解所標(biāo)記的初始化方法抽碌,最后銷毀的時(shí)候執(zhí)行destroy 方法。
user service constructor
user service init
user service destroy
 

嘗試把bean修改為多例决瞳,看是否還能再執(zhí)行destroy 方法货徙。

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

再次運(yùn)行代碼后結(jié)果輸出如下。執(zhí)行了兩遍構(gòu)造器和初始化方法皮胡,因?yàn)槭嵌嗬J匠占眨艺{(diào)用了兩次,所以產(chǎn)生了兩個(gè)對(duì)象屡贺。并且并沒有調(diào)用PreDestroy所標(biāo)記的銷毀方法蠢棱。

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");
    }
}

--最后執(zhí)行結(jié)果如下烹笔,先執(zhí)行的構(gòu)造器,其次是初始化方法抛丽,銷毀則執(zhí)行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();
    }

--輸出結(jié)果如下允蜈,先執(zhí)行構(gòu)造方法,其次是后置處理器的postProcessBeforeInitialization方法蒿柳,再然后是bean的初始化方法饶套,初始化方法執(zhí)行后是,后置處理器的postProcessAfterInitialization方法,銷毀bean時(shí)執(zhí)行destroy方法垒探。
--應(yīng)用場(chǎng)景可以在于攔截bean修改某些成員變量妓蛮,不過我認(rèn)為這種場(chǎng)景業(yè)務(wù)中還是很少遇到。
  
user service constructor
userService  postProcessBeforeInitialization
user service init
userService  postProcessAfterInitialization
user service destroy

貼一個(gè)初始化bean的源碼截圖圾叼,該代碼塊在AbstractAutowireCapableBeanFactory這個(gè)類中蛤克。

QQ20200419-193506@2x

Bean工廠后置處理器 BeanFactoryPostProcessor

Bean工廠后置處理器捺癞,在bean解析,但是未初始化之前調(diào)用构挤。代碼示例如下

/**
 * 自定義Bean工廠后置處理器
 */
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        //獲取userService的bean定義
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");
        //設(shè)置為多例模式
        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);
    }
}

--輸出結(jié)果如下髓介,表明該bean已經(jīng)變?yōu)槎嗬恕<词乖贎Bean注解聲明為單例后筋现,也可以通過后置處理器修改為多例唐础,或者懶加載等等。
false

InitializingBean接口使用

由bean實(shí)現(xiàn)的接口矾飞,這些bean需要在{@link BeanFactory}設(shè)置完所有屬性*后作出反應(yīng):例如一膨,執(zhí)行自定義初始化,*或僅僅檢查是否設(shè)置了所有必需屬性凰慈。(翻譯自源碼注釋)

也就是說 該接口是用來校驗(yàn)屬性或者執(zhí)行某些自定義初始化的汞幢,直接上代碼。

/**
 * 這里定義了一個(gè)name屬性微谓,并且在afterPropertiesSet方法中進(jìn)行校驗(yàn)森篷,如果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);
    }
}


--執(zhí)行出錯(cuò)仲智,輸出如下。
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

對(duì)name屬性進(jìn)行賦值后則正常裝載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);
    }
}

--最后輸出結(jié)果
 bean:UserModel(age=20, name=大白)

但是其實(shí)PropertySource 默認(rèn)是不支持加載yaml文件的姻氨,那么如果要加載yaml文件的話钓辆,需要多一些處理。

yaml文件

user:
  nikename: 大白

Java 代碼實(shí)現(xiàn)

/**
 * 自定義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();
    }
}

--最后結(jié)果輸出如下
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();
    }
}

--同一種類型肴焊,單個(gè)bean的正常獲取前联,輸出結(jié)果如下。
userModel:UserModel(name=小白)

嘗試同一種類型的多個(gè)bean對(duì)象娶眷,這里在config類中聲明了兩個(gè)bean對(duì)象似嗤。

如下,name屬性對(duì)應(yīng)的數(shù)據(jù)不同届宠,userModel對(duì)應(yīng)的name是小白烁落,userModel2對(duì)應(yīng)的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();
    }
}

這時(shí)候再運(yùn)行的話會(huì)報(bào)錯(cuò)豌注,Spring會(huì)告訴你伤塌,你要加載的bean對(duì)象有兩個(gè)。而Spring不知道應(yīng)該幫你加載哪個(gè)轧铁。

這是因?yàn)锳utowired這個(gè)注解默認(rèn)是按照類的class來進(jìn)行匹配的每聪,而它匹配到兩個(gè),并且你沒有指定加載某一個(gè)bean名字,則會(huì)異常熊痴。

那么解決方案也比較簡(jiǎn)單他爸,有兩種,一種是直接通過變量名與bean名字一致的情況下果善,如下诊笤。

public class UserService {
    /**
     * 對(duì)應(yīng)bean名字  userModel
     */
    @Autowired
    private UserModel userModel;
    /**
     * 對(duì)應(yīng)bean名字  userModel2
     */
    @Autowired
    private UserModel userModel2;

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

輸出結(jié)果如下,符合預(yù)期巾陕。

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

那么再講第二種方案讨跟,第二種方案的話需要配合Qualifier 注解,如下鄙煤。

public class UserService {
    /**
     * 對(duì)應(yīng)bean名字  userModel
     */
    @Qualifier("userModel")
    @Autowired
    private UserModel userModel;
    /**
     * 對(duì)應(yīng)bean名字  userModel
     */
    @Qualifier("userModel")
    @Autowired
    private UserModel userModel2;

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

最后輸出結(jié)果如下晾匠,可以看到兩次輸出的name都是小白,這是因?yàn)橐呀?jīng)固定了按照bean的名字也就是userModel來獲取bean對(duì)象梯刚。

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

另外如果需要允許null值的話凉馆,在使用注解的時(shí)候?qū)equired設(shè)置為false 例如: @Autowired(required=false)

@Resource

Resource 這個(gè)注解是jdk所提供的,那么這個(gè)注解所帶來裝載順序亡资。

1澜共、優(yōu)先匹配bean名字,如果沒有指定名字的話锥腻,會(huì)獲取變量名作為bean名字去匹配 ;

2嗦董、倘若bean名字匹配不到,則根據(jù)類型也就是type進(jìn)行唯一性匹配 ;

3瘦黑、如果同時(shí)指定了type京革,和bean名字,則匹配這兩個(gè)條件的bean幸斥;

示范如下

public class XiaoMingModel extends UserModel{
}

public class UserService {
    /**
     * 按照固定的bean名字獲取對(duì)象
     */
    @Resource(name = "userModel")
    private UserModel userModel;
    /**
     * 根據(jù)指定的class類型 進(jìn)行匹配裝載
     */
    @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("小明同學(xué)");
        return xiaoMingModel;
    }

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

最后輸出結(jié)果如下匹摇,變量userModel指定加載的bean是“userModel” ,所以這里打印小白是符合預(yù)期的甲葬。

其次廊勃,變量userModel2指定加載的bean類型是XiaoMingModel.class,所以這里打印小明同學(xué)也是符合預(yù)期的演顾。

userModel:UserModel(name=小白)
userModel2:UserModel(name=小明同學(xué))

@Profile

根據(jù)環(huán)境來決定是否加載ben對(duì)象供搀,常見于開發(fā)環(huán)境隅居、測(cè)試環(huán)境钠至、生產(chǎn)環(huán)境,切換bean實(shí)現(xiàn)胎源。

在配置文件中聲明當(dāng)前的環(huán)境

spring:
  profiles:
    active: test

其次在代碼中配置bean棉钧,并且配置Profile注解環(huán)境。

@PropertySource(value = {"classpath:application.yaml"}, encoding = "UTF-8", factory = YamlPropertySourceFactory.class)
@Configuration
public class StudyIocConfig {
    /**
     * 環(huán)境為prod時(shí)涕蚤,則裝載
     * @return
     */
    @Profile("prod")
    @Bean
    public UserModel userModelProd() {
        UserModel userModel = new UserModel();
        userModel.setName("大白");
        return userModel;
    }

    /**
     * 環(huán)境為dev 或者  test時(shí) 則裝載
     * @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);
    }
}

--最后運(yùn)行結(jié)果輸出如下宪卿,在配置文件里設(shè)置環(huán)境為 active:test 的诵,所以它最后裝載的bean是userModelDev 。

--輸出name==小白佑钾,是符合預(yù)期的西疤。

userModel:UserModel(name=小白)

EmbeddedValueResolverAware

可以通過實(shí)現(xiàn)該接口,獲取配置文件值休溶。

yaml配置文件如下

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

Java代碼如下

public class UserService implements EmbeddedValueResolverAware {

    private String jdbcUrl;

    /**
     * 將StringValueResolver設(shè)置為用于解析嵌入的定義值
     *
     * @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();
    }
}

最后結(jié)果輸出如下代赁,結(jié)果是符合預(yù)期的。

jdbcUrl:jdbc:mysql://127.0.0.1:3306/xxxxxx
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末兽掰,一起剝皮案震驚了整個(gè)濱河市芭碍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌孽尽,老刑警劉巖窖壕,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異杉女,居然都是意外死亡瞻讽,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門宠纯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來卸夕,“玉大人,你說我怎么就攤上這事婆瓜】旒” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵廉白,是天一觀的道長(zhǎng)个初。 經(jīng)常有香客問我,道長(zhǎng)猴蹂,這世上最難降的妖魔是什么院溺? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮磅轻,結(jié)果婚禮上珍逸,老公的妹妹穿的比我還像新娘。我一直安慰自己聋溜,他們只是感情好谆膳,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著撮躁,像睡著了一般漱病。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天杨帽,我揣著相機(jī)與錄音漓穿,去河邊找鬼。 笑死注盈,一個(gè)胖子當(dāng)著我的面吹牛晃危,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播老客,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼山害,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了沿量?” 一聲冷哼從身側(cè)響起浪慌,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎朴则,沒想到半個(gè)月后权纤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡乌妒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年汹想,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片撤蚊。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡古掏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出侦啸,到底是詐尸還是另有隱情槽唾,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布光涂,位于F島的核電站庞萍,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏忘闻。R本人自食惡果不足惜钝计,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望齐佳。 院中可真熱鬧私恬,春花似錦、人聲如沸炼吴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缺厉。三九已至永高,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間提针,已是汗流浹背命爬。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辐脖,地道東北人饲宛。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像嗜价,于是被迫代替她去往敵國(guó)和親艇抠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容