1. 程式人生 > 實用技巧 >Spring註解驅動——IOC

Spring註解驅動——IOC

體系結構概述

@Bean

之前時候,在xml配置檔案使用:

<bean id="person" class="com.atguigu.bean.Person"  scope="prototype" >
        <property name="age" value="${}"></property>
        <property name="name" value="zhangsan"></property>
    </bean>

實體類:

public class Person {
    private
String name;private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public
Person(String name, Integer age) { super(); this.name = name; this.age = age; } public Person() { super(); // TODO Auto-generated constructor stub } public String toString() { return "Person [name=" + name + ", age=" + age + ", nickName=" + nickName + "]"; } }

測試:

public class MainTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        Person bean = (Person) applicationContext.getBean("person");
        System.out.println(bean);
      
    }

}

註解方式

寫一個配置類:需要@Configuration註解

//配置類==配置檔案
@Configuration  //告訴Spring這是一個配置類
public class MainConfig {
    
    //給容器中註冊一個Bean;型別為返回值的型別,id預設是用方法名作為id,也可以指定id名為什麼
    @Bean("person")
    public Person person01(){
        return new Person("lisi", 20);
    }

}

測試:

public class MainTest {
    
    @SuppressWarnings("resource")
    public static void main(String[] args) {
       
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        Person bean = applicationContext.getBean(Person.class);
        System.out.println(bean);
        
        String[] namesForType = applicationContext.getBeanNamesForType(Person.class);
        for (String name : namesForType) {
            System.out.println(name);
        }
    
    }

}

@ComponentScan

之前在xml配置檔案裡配置:

<context:component-scan base-package="com.atguigu" use-default-filters="false"></context:component-scan>

現在配置在類上:

//配置類==配置檔案
@Configuration  //告訴Spring這是一個配置類
@ComponentScan(value="com.atguigu")  // value:指定要掃描的包
public class MainConfig {
    
    //給容器中註冊一個Bean;型別為返回值的型別,id預設是用方法名作為id
    @Bean("person")
    public Person person01(){
        return new Person("lisi", 20);
    }

}

@ComponentScan 的一般配置項:

@ComponentScan(value="com.atguigu",includeFilters = {
/*                        @Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
                        @Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}),*/
                        @Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})
                },useDefaultFilters = false)    
//@ComponentScan  value:指定要掃描的包
//excludeFilters = Filter[] :指定掃描的時候按照什麼規則排除那些元件
//includeFilters = Filter[] :指定掃描的時候只需要包含哪些元件

// 過濾規則:
//FilterType.ANNOTATION:按照註解 //FilterType.ASSIGNABLE_TYPE:按照給定的型別; //FilterType.ASPECTJ:使用ASPECTJ表示式 //FilterType.REGEX:使用正則指定 //FilterType.CUSTOM:使用自定義規則

可以修改預設的過濾規則,寫一個類,實現FilterType

public class MyTypeFilter implements TypeFilter {
    /**
     * metadataReader:讀取到的當前正在掃描的類的資訊
     * metadataReaderFactory:可以獲取到其他任何類資訊的
     */
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
            throws IOException {
        // TODO Auto-generated method stub
        //獲取當前類註解的資訊
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        //獲取當前正在掃描的類的類資訊
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        //獲取當前類資源(類的路徑)
        Resource resource = metadataReader.getResource();
        
        String className = classMetadata.getClassName();
        System.out.println("--->"+className);
        if(className.contains("er")){
            return true;
        }
        return false;
    }
}

@ComponentScan 可以在類上面寫多個,也可以通過一個@ComponentScans 註解來把多個@ComponentScan 註解配置放在裡面,兩個寫法都是一樣的可以生效

@ComponentScans(
        value = {
                @ComponentScan(value="com.atguigu",includeFilters = {
                        @Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})
                },useDefaultFilters = false)    
        }
        )

Spring 的預設註冊元件都是單例的,可以用@Scope 註解修改bean 的作用域

雖然Spring 是單例的,但它用的是餓漢式單例,在Spring 啟動時候,就已經把物件註冊到了Spring 容器裡,可以用@Lazy 註解讓它變成懶載入,不過不推薦修改

   //預設是單例項的
    /**
     * ConfigurableBeanFactory#SCOPE_PROTOTYPE    
     * @see ConfigurableBeanFactory#SCOPE_SINGLETON  
     * @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST  request
     * @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION     sesssion
     * @return\
     * @Scope:調整作用域
     * prototype:多例項的:ioc容器啟動並不會去呼叫方法建立物件放在容器中。
     *                     每次獲取的時候才會呼叫方法建立物件;
     * singleton:單例項的(預設值):ioc容器啟動會呼叫方法建立物件放到ioc容器中。
     *             以後每次獲取就是直接從容器(map.get())中拿,
     * request:同一次請求建立一個例項
     * session:同一個session建立一個例項
     * 
     * 懶載入:
     *         單例項bean:預設在容器啟動的時候建立物件;
     *         懶載入:容器啟動不建立物件。第一次使用(獲取)Bean建立物件,並初始化;
     */
//    @Scope("prototype")
    @Lazy
    @Bean("person")
    public Person person(){
        System.out.println("給容器中新增Person....");
        return new Person("張三", 25);
    }

@Conditional 按照條件給Spring 註冊bean,

   /**
     * @Conditional({Condition}) : 按照一定的條件進行判斷,滿足條件給容器中註冊bean
     * 
     * 如果系統是windows,給容器中註冊("bill")
     * 如果是linux系統,給容器中註冊("linus")
     */
    
    @Bean("bill")
    public Person person01(){
        return new Person("Bill Gates",62);
    }
    
    @Conditional(LinuxCondition.class)
    @Bean("linus")
    public Person person02(){
        return new Person("linus", 48);
    }

獲取是否是Linux系統的類:

import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

//判斷是否linux系統
public class LinuxCondition implements Condition {
    /**
     * ConditionContext:判斷條件能使用的上下文(環境)
     * AnnotatedTypeMetadata:註釋資訊
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // TODO是否linux系統
        //1、能獲取到ioc使用的beanfactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        //2、獲取類載入器
        ClassLoader classLoader = context.getClassLoader();
        //3、獲取當前環境資訊
        Environment environment = context.getEnvironment();
        //4、獲取到bean定義的註冊類
        BeanDefinitionRegistry registry = context.getRegistry();
        
        String property = environment.getProperty("os.name");
        
        //可以判斷容器中的bean註冊情況,也可以給容器中註冊bean
        boolean definition = registry.containsBeanDefinition("person");
        if(property.contains("linux")){
            return true;
        }
        return false;
    }
}

獲取是否是Windows系統的類:

//判斷是否windows系統
public class WindowsCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        String property = environment.getProperty("os.name");
        if(property.contains("Windows")){
            return true;
        }
        return false;
    }
}

測試:

   @Test
    public void test03(){
        String[] namesForType = applicationContext.getBeanNamesForType(Person.class);
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        //動態獲取環境變數的值;Windows 10
        String property = environment.getProperty("os.name");
        System.out.println(property);
        for (String name : namesForType) {
            System.out.println(name);
        }
        Map<String, Person> persons = applicationContext.getBeansOfType(Person.class);
        System.out.println(persons);
    }

@Conditional 註解可以放在方法是,也可以放在類上

@Import

1、給容器匯入一個元件

要匯入的元件

public class Color {
    private Car car;

    public Car getCar() {
        return car;
    }
    public void setCar(Car car) {
        this.car = car;
    }

    @Override
    public String toString() {
        return "Color [car=" + car + "]";
    }
}

Car 類:

public class Car {
}

匯入:

@Import({Color.class})
//@Import匯入元件,id預設是元件的全類名
public class MainConfig2 {

2、也可以匯入多個:

新增兩個顏色的類:

public class Blue {
}
public class Yellow {

}

要寫一個類,實現ImportSelector 介面,然後通過這個類,就匯入了多個元件

//自定義邏輯返回需要匯入的元件
public class MyImportSelector implements ImportSelector {
    //返回值,就是到匯入到容器中的元件全類名
    //AnnotationMetadata:當前標註@Import註解的類的所有註解資訊
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        // TODO Auto-generated method stub
        //importingClassMetadata
        //方法不要返回null值
        return new String[]{"com.atguigu.bean.Blue","com.atguigu.bean.Yellow"};
    }
}

匯入多個:雖然MyImportSelector 看著 和匯入一個元件是一樣的,匯入了一個類而已,但是這個類裡添加了多個元件

@Import({Color.class,Red.class,MyImportSelector.class})
//@Import匯入元件,id預設是元件的全類名
public class MainConfig2 {

3、通過ImportBeanDefinitionRegistrar 介面手動匯入元件

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    /**
     * AnnotationMetadata:當前類的註解資訊
     * BeanDefinitionRegistry:BeanDefinition註冊類;
     *         把所有需要新增到容器中的bean;呼叫
     *         BeanDefinitionRegistry.registerBeanDefinition手工註冊進來
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        
        boolean definition = registry.containsBeanDefinition("com.atguigu.bean.Red");
        boolean definition2 = registry.containsBeanDefinition("com.atguigu.bean.Blue");
        if(definition && definition2){
            //指定Bean定義資訊;(Bean的型別,Bean。。。)
            RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
            //註冊一個Bean,指定bean名
            registry.registerBeanDefinition("rainBow", beanDefinition);
        }
    }

}

新加的彩虹類:

public class RainBow {

}

4、使用FactoryBean 註冊元件

建立Bean 的工廠:

//建立一個Spring定義的FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {

    //返回一個Color物件,這個物件會新增到容器中
    @Override
    public Color getObject() throws Exception {
        // TODO Auto-generated method stub
        System.out.println("ColorFactoryBean...getObject...");
        return new Color();
    }

    @Override
    public Class<?> getObjectType() {
        // TODO Auto-generated method stub
        return Color.class;
    }

    //是單例?
    //true:這個bean是單例項,在容器中儲存一份
    //false:多例項,每次獲取都會建立一個新的bean;
    @Override
    public boolean isSingleton() {
        // TODO Auto-generated method stub
        return false;
    }
}

用的時候:

   @Bean
    public ColorFactoryBean colorFactoryBean(){
        return new ColorFactoryBean();
    }

測試:

   @Test
    public void testImport(){
        printBeans(applicationContext);
        Blue bean = applicationContext.getBean(Blue.class);
        System.out.println(bean);
        
        //工廠Bean獲取的是呼叫getObject建立的物件
        Object bean2 = applicationContext.getBean("colorFactoryBean");
        Object bean3 = applicationContext.getBean("colorFactoryBean");
        System.out.println("bean的型別:"+bean2.getClass());
        System.out.println(bean2 == bean3);
        // 雖然匯入的是colorFactoryBean,但列印的還是color,如果非要獲取的是工廠bean名字,就在前面加一個& 就好了
        Object bean4 = applicationContext.getBean("&colorFactoryBean");
        System.out.println(bean4.getClass());
    }

總結:

   /**
     * 給容器中註冊元件;
     * 1)、包掃描+元件標註註解(@Controller/@Service/@Repository/@Component)[自己寫的類]
     * 2)、@Bean[匯入的第三方包裡面的元件]
     * 3)、@Import[快速給容器中匯入一個元件]
     *         1)、@Import(要匯入到容器中的元件);容器中就會自動註冊這個元件,id預設是全類名
     *         2)、ImportSelector:返回需要匯入的元件的全類名陣列; (比較重要,在SpringBoot 裡用的非常多)
     *         3)、ImportBeanDefinitionRegistrar:手動註冊bean到容器中
     * 4)、使用Spring提供的 FactoryBean(工廠Bean);
     *         1)、預設獲取到的是工廠bean呼叫getObject建立的物件
     *         2)、要獲取工廠Bean本身,我們需要給id前面加一個&
     *             &colorFactoryBean
     */
    @Bean
    public ColorFactoryBean colorFactoryBean(){
        return new ColorFactoryBean();
    }

生命週期

要註冊Bean 的類:

1、第一種方式需要的Bean

@Component
public class Car {
    
    public Car(){
        System.out.println("car constructor...");
    }
    
    public void init(){
        System.out.println("car ... init...");
    }
    
    public void detory(){
        System.out.println("car ... detory...");
    }

}

2、第二種方式的Bean

@Component
public class Cat implements InitializingBean,DisposableBean {
    
    public Cat(){
        System.out.println("cat constructor...");
    }

    @Override
    public void destroy() throws Exception {
        // TODO Auto-generated method stub
        System.out.println("cat...destroy...");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // TODO Auto-generated method stub
        System.out.println("cat...afterPropertiesSet...");
    }

}

3、第三種方式的

@Component
public class Dog implements ApplicationContextAware {  
    
    //@Autowired
    private ApplicationContext applicationContext;  // 實現上面類的介面,這樣匯入後,以後用的時候就可以拿到IOC容器
    
    public Dog(){
        System.out.println("dog constructor...");
    }
    
    //物件建立並賦值之後呼叫
    @PostConstruct
    public void init(){
        System.out.println("Dog....@PostConstruct...");
    }
    
    //容器移除物件之前
    @PreDestroy
    public void detory(){
        System.out.println("Dog....@PreDestroy...");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        // TODO Auto-generated method stub
        this.applicationContext = applicationContext;
    }
}

4、使用Bean 的後置處理器

/**
 * 後置處理器:初始化前後進行處理工作
 * 將後置處理器加入到容器中*/
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // TODO Auto-generated method stub
        System.out.println("postProcessBeforeInitialization..."+beanName+"=>"+bean);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // TODO Auto-generated method stub
        System.out.println("postProcessAfterInitialization..."+beanName+"=>"+bean);
        return bean;
    }
}

註冊Bean

/**
 * bean的生命週期:
 *         bean建立---初始化----銷燬的過程
 * 容器管理bean的生命週期;
 * 我們可以自定義初始化和銷燬方法;容器在bean進行到當前生命週期的時候來呼叫我們自定義的初始化和銷燬方法
 * 
 * 構造(物件建立)
 *         單例項:在容器啟動的時候建立物件
 *         多例項:在每次獲取的時候建立物件\
 * 
 * BeanPostProcessor.postProcessBeforeInitialization
 * 初始化:
 *         物件建立完成,並賦值好,呼叫初始化方法。。。
 * BeanPostProcessor.postProcessAfterInitialization
 * 銷燬:
 *         單例項:容器關閉的時候
 *         多例項:容器不會管理這個bean;容器不會呼叫銷燬方法;
 * 
 * 
 * 遍歷得到容器中所有的BeanPostProcessor;挨個執行beforeInitialization,
 * 一但返回null,跳出for迴圈,不會執行後面的BeanPostProcessor.postProcessorsBeforeInitialization
 * 
 * BeanPostProcessor原理
 * populateBean(beanName, mbd, instanceWrapper);給bean進行屬性賦值
 * initializeBean
 * {
 * applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
 * invokeInitMethods(beanName, wrappedBean, mbd);執行自定義初始化
 * applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
 *}
 * 
 * 
 * 建立Bean的4種方式
 * 1)、指定初始化和銷燬方法;
 *         通過@Bean指定init-method和destroy-method;分別指定初始化 或 銷燬時候 呼叫的方法
 * 2)、通過讓Bean實現InitializingBean(定義初始化邏輯),
 *                 DisposableBean(定義銷燬邏輯);
*      實現這兩個介面,然後重寫裡面的兩個方法 * 3)、可以使用JSR250; * @PostConstruct:在bean建立完成並且屬性賦值完成;來執行初始化方法,定義在方法上 * @PreDestroy:在容器銷燬bean之前通知我們進行清理工作,也在方法上定義 * 4)、BeanPostProcessor【interface】:bean的後置處理器; * 在bean初始化前後進行一些處理工作; * postProcessBeforeInitialization:在初始化之前工作 * postProcessAfterInitialization:在初始化之後工作 * * Spring底層對 BeanPostProcessor 的使用; * bean賦值,注入其他元件,@Autowired,生命週期註解功能,@Async, 。。。BeanPostProcessor; *
*/ @ComponentScan("com.atguigu.bean") @Configuration public class MainConfigOfLifeCycle { //@Scope("prototype") @Bean(initMethod="init",destroyMethod="detory") public Car car(){ return new Car(); } }

測試:

public class IOCTest_LifeCycle {
    
    @Test
    public void test01(){
        //1、建立ioc容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
        System.out.println("容器建立完成...");
        
        //applicationContext.getBean("car");
        //關閉容器
        applicationContext.close();
    }

}

屬性賦值

@Value

public class Person {
    
    //使用@Value賦值;
    //1、基本數值
    //2、可以寫SpEL; #{}
    //3、可以寫${};取出配置檔案【properties】中的值(在執行環境變數裡面的值)
    
    @Value("張三")
    private String name;
    @Value("#{20-2}")  // 18
    private Integer age;
    
    @Value("${person.nickName}")  // 匯入的是配置檔案的值
    private String nickName;
   
person.nickName=\u5C0F\u674E\u56DB    // 小張三

之前在xml 檔案要配置的:

<context:property-placeholder location="classpath:person.properties"/>

現在可以在類上使用@PropertySource,如果要加入多個配置檔案,可以點進去看原始碼,比較簡單,可以放陣列型別

//使用@PropertySource讀取外部配置檔案中的k/v儲存到執行的環境變數中;載入完外部的配置檔案以後使用${}取出配置檔案的值
@PropertySource(value={"classpath:/person.properties"})
@Configuration
public class MainConfigOfPropertyValues {
    
    @Bean
    public Person person(){
        return new Person();
    }
}

自動裝配

第一種:我們開發中對dao、service、controller 都要加上三個註解,然後互相之間呼叫時候使用@Autowired把我們自定義的元件給自動裝配進來,它是按照型別來注入的,預設的Bean是類的首字母小寫,

  如果找到了多個Bean,再按照屬性名查詢,如果要必須指定注入哪個,就用@Qualifier("指定的bean的名字"),@Autowired 可以指定是否一定要找spring 裡的bean,加上required 屬性就行,

  還有一個註解@Primary首選去找到哪個bean,而@Qualifier 是必須要用哪個bean

  @Autowired 可以在 構造器,引數,方法,屬性,都可以使用

  第二種:還可以使用@Resource也可以完成自動裝配,不同的是@Resource 是java裡的註解,@Autowired 是spring裡的註解,@Resource 預設是按照屬性名裝配的,

    @Resource 不能和@Autowired 一樣搭配著其他兩個註解一塊使用

    @Inject也可以自動裝配,也是java的註解,但是需要匯入javax.inject的包,和Autowired的功能一樣。沒有required=false的功能;

/**
 * 自動裝配;
 *         Spring利用依賴注入(DI),完成對IOC容器中中各個元件的依賴關係賦值;
 * 
 * 1)、@Autowired:自動注入:
 *         1)、預設優先按照型別去容器中找對應的元件:applicationContext.getBean(BookDao.class);找到就賦值
 *         2)、如果找到多個相同型別的元件,再將屬性的名稱作為元件的id去容器中查詢
 *                             applicationContext.getBean("bookDao")
 *         3)、@Qualifier("bookDao"):使用@Qualifier指定需要裝配的元件的id,而不是使用屬性名
 *         4)、自動裝配預設一定要將屬性賦值好,沒有就會報錯;
 *             可以使用@Autowired(required=false);
 *         5)、@Primary:讓Spring進行自動裝配的時候,預設使用首選的bean;
 *                 也可以繼續使用@Qualifier指定需要裝配的bean的名字
 *         BookService{
 *             @Autowired
 *             BookDao  bookDao;
 *         }
 * 
 * 2)、Spring還支援使用@Resource(JSR250)和@Inject(JSR330)[java規範的註解]
 *         @Resource:
 *             可以和@Autowired一樣實現自動裝配功能;預設是按照元件名稱進行裝配的;
 *             沒有能支援@Primary功能沒有支援@Autowired(reqiured=false);
 *         @Inject:
 *             需要匯入javax.inject的包,和Autowired的功能一樣。沒有required=false的功能;
 *  @Autowired:Spring定義的; @Resource、@Inject都是java規範
 *     
 * AutowiredAnnotationBeanPostProcessor:解析完成自動裝配功能;        
 * 
 * 3)、 @Autowired:構造器,引數,方法,屬性;都是從容器中獲取引數元件的值
 *         1)、[標註在方法位置]:@Bean+方法引數;引數從容器中獲取;預設不寫@Autowired效果是一樣的;都能自動裝配
 *         2)、[標在構造器上]:如果元件只有一個有參構造器,這個有參構造器的@Autowired可以省略,引數位置的元件還是可以自動從容器中獲取
 *         3)、放在引數位置:
 * 
 * 4)、自定義元件想要使用Spring容器底層的一些元件(ApplicationContext,BeanFactory,xxx);
 *         自定義元件實現xxxAware;在建立物件的時候,會呼叫介面規定的方法注入相關元件;Aware;
 *         把Spring底層一些元件注入到自定義的Bean中;
 *         xxxAware:功能使用xxxProcessor;
 *             ApplicationContextAware==》ApplicationContextAwareProcessor;
 *     */
@Configuration
@ComponentScan({"com.atguigu.service","com.atguigu.dao",
    "com.atguigu.controller","com.atguigu.bean"})
public class MainConifgOfAutowired {
    
    @Primary
    @Bean("bookDao2")
    public BookDao bookDao(){
        BookDao bookDao = new BookDao();
        bookDao.setLable("2");
        return bookDao;
    }
    /**
     * @Bean標註的方法建立物件的時候,方法引數的值從容器中獲取
     * @param car
     * @return
     */
    @Bean
    public Color color(Car car){
        Color color = new Color();
        color.setCar(car);
        return color;
    }    
}

@Autowired 在 構造器,引數,方法,屬性 上面使用

//預設加在ioc容器中的元件,容器啟動會呼叫無參構造器建立物件,再進行初始化賦值等操作
@Component
public class Boss {        
    private Car car;
    
    //構造器要用的元件,都是從容器中獲取
    public Boss(Car car){
        this.car = car;
        System.out.println("Boss...有參構造器");
    }        
    public Car getCar() {
        return car;
    }
    //@Autowired 
    //標註在方法,Spring容器建立當前物件,就會呼叫方法,完成賦值;
    //方法使用的引數,自定義型別的值從ioc容器中獲取
    public void setCar(Car car) {
        this.car = car;
    }
    @Override
    public String toString() {
        return "Boss [car=" + car + "]";
    }    
}

Aware 注入spring底層元件測試

@Component
public class Red implements ApplicationContextAware,BeanNameAware,EmbeddedValueResolverAware {
    
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        // TODO Auto-generated method stub
        System.out.println("傳入的ioc:"+applicationContext);
        this.applicationContext = applicationContext;
    }
    @Override
    public void setBeanName(String name) {
        // TODO Auto-generated method stub
        System.out.println("當前bean的名字:"+name);
    }
    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        // TODO Auto-generated method stub
        String resolveStringValue = resolver.resolveStringValue("你好 ${os.name} 我是 #{20*18}");
        System.out.println("解析的字串:"+resolveStringValue);
    }
}

@Profile 可以切換專案的當前環境,如果不用@Profile 指定,預設不管哪個環境都可以註冊的,@Profile 也可以寫在類上

/**
 * Profile:
 *         Spring為我們提供的可以根據當前環境,動態的啟用和切換一系列元件的功能;
 * 
 * 開發環境、測試環境、生產環境;
 * 資料來源:(/A)(/B)(/C);
 * 
 * 
 * @Profile:指定元件在哪個環境的情況下才能被註冊到容器中,不指定,任何環境下都能註冊這個元件
 * 
 * 1)、加了環境標識的bean,只有這個環境被啟用的時候才能註冊到容器中。預設是default環境
 * 2)、寫在配置類上,只有是指定的環境的時候,整個配置類裡面的所有配置才能開始生效
 * 3)、沒有標註環境標識的bean在,任何環境下都是載入的;
 */

@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MainConfigOfProfile implements EmbeddedValueResolverAware{
    
    @Value("${db.user}")
    private String user;    
private StringValueResolver valueResolver; // 值解析器 private String driverClass; @Bean public Yellow yellow(){ return new Yellow(); } @Profile("test") @Bean("testDataSource") public DataSource dataSourceTest(@Value("${db.password}")String pwd) throws Exception{ ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setUser(user); dataSource.setPassword(pwd); dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test"); dataSource.setDriverClass(driverClass); return dataSource; } @Profile("dev") @Bean("devDataSource") public DataSource dataSourceDev(@Value("${db.password}")String pwd) throws Exception{ ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setUser(user); dataSource.setPassword(pwd); dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/ssm_crud"); dataSource.setDriverClass(driverClass); return dataSource; } @Profile("prod") @Bean("prodDataSource") public DataSource dataSourceProd(@Value("${db.password}")String pwd) throws Exception{ ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setUser(user); dataSource.setPassword(pwd); dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/scw_0515"); dataSource.setDriverClass(driverClass); return dataSource; } @Override public void setEmbeddedValueResolver(StringValueResolver resolver) { // TODO Auto-generated method stub this.valueResolver = resolver; driverClass = valueResolver.resolveStringValue("${db.driverClass}"); } }

配置檔案 dbconfig.properties

db.user=root
db.password=123456
db.driverClass=com.mysql.jdbc.Driver

測試:

public class IOCTest_Profile {
    //1、使用命令列動態引數: 在虛擬機器引數位置載入 -Dspring.profiles.active=test
    //2、程式碼的方式啟用某種環境;
    @Test
    public void test01(){
        AnnotationConfigApplicationContext applicationContext = 
                new AnnotationConfigApplicationContext();
        //1、建立一個applicationContext
        //2、設定需要啟用的環境,可以設定多個,,隔開
        applicationContext.getEnvironment().setActiveProfiles("dev");
        //3、註冊主配置類
        applicationContext.register(MainConfigOfProfile.class);
        //4、啟動重新整理容器
        applicationContext.refresh();
        
        
        String[] namesForType = applicationContext.getBeanNamesForType(DataSource.class);
        for (String string : namesForType) {
            System.out.println(string);
        }        
        Yellow bean = applicationContext.getBean(Yellow.class);
        System.out.println(bean);
        applicationContext.close();
    }
}