淺嘗Spring註解開發_自定義註冊元件、屬性賦值、自動裝配

蔚然丶丶發表於2022-04-29

Spring註解開發

淺嘗Spring註解開發,基於Spring 4.3.12
包含自定義掃描元件、自定義匯入元件、手動註冊元件、自動注入方法和引數、使用Spring容器底層元件等

配置

@Configuration配置類

告訴Spring這是一個配置類,代替以前的xml檔案,配置類=配置檔案

@Configuration
public class MainConfig {
	
	//給容器中註冊一個Bean;型別為返回值的型別,id預設是用方法名作為id
	@Bean("person")
	public Person person01(){
		return new Person("lisi", 20);
	}

}

註冊元件

給容器中註冊元件有以下方法:

  1. 包掃描+元件標註註解(@Controller/@Service/@Repository/@Component)[自己寫的類]

  2. @Bean[匯入的第三方包裡面的元件]

  3. @Import[快速給容器中匯入一個元件]

    1. @Import(要匯入到容器中的元件);容器中就會自動註冊這個元件,id預設是全類名
    2. ImportSelector:返回需要匯入的元件的全類名陣列;
    3. ImportBeanDefinitionRegistrar:手動註冊bean到容器中
  4. 使用Spring提供的 FactoryBean(工廠Bean);

    1. 預設獲取到的是工廠bean呼叫getObject建立的物件

    2. 要獲取工廠Bean本身,我們需要給id前面加一個&

      如:&colorFactoryBean

@Bean註冊

給容器中註冊一個Bean,型別為返回值的型別,id預設是用方法名作為id,也可以指定id

	@Bean("person")//指定Bean id
	public Person person01(){
		return new Person("lisi", 20);
	}

IoC容器

建立一個IoC容器AnnotationConfigApplicationContext,傳入配置類,獲取Bean

//建立IoC容器
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);

//獲取Bean物件
Person bean = applicationContext.getBean(Person.class);

//獲取所以Person型別Bean的名字
String[] namesForType = applicationContext.getBeanNamesForType(Person.class);
for (String name : namesForType) {
	System.out.println(name);
}

⭐@ComponentScan掃描元件

@Component(包括@Controller@Service@Repository和@Bean目的一樣)和@ComponentScan的區別

  • @Component:將普通JavaBean例項化到spring容器中,Spring容器統一管理,用起來不用自己new了,相當於配置檔案中的<bean id="" class=""/>
  • @ComponentScan:一般用在核心配置類,由你來定義哪些包需要被掃描。
  • @Component標在類上,@Bean標在方法上,兩者使用在普通類或方法上時,都不起作用,只有放在配置類中才能註冊元件(@Bean可以在寫在配置類中,@Component由於本身標在類上,所以不能寫在類中),而@ComponentScan就是給了@Component一種註冊進容器的辦法,只要掃描到@Component標註的類就直接註冊進容器
  • 自動掃描元件,可有多個@ComponentScan,或一個@ComponentScans

  • value:指定要掃描的包

    所有指定的包下的類都要進入掃描,是否能夠通過,需要看下面使用了什麼規則,有排除、包含、指定型別、正則、和自定義規則,自定義規則通過反射獲取包中所有類,然後判斷是否能夠進入ioc容器中。

  • excludeFilters = Filter[] :指定掃描的時候按照什麼規則排除那些元件

  • includeFilters = Filter[] :指定掃描的時候只需要包含哪些元件

    • ​ FilterType.ANNOTATION:按照註解
    • ​ FilterType.ASSIGNABLE_TYPE:按照給定的型別
    • ​ FilterType.ASPECTJ:使用ASPECTJ表示式
    • ​ FilterType.REGEX:使用正則指定
  • ​ FilterType.CUSTOM:使用自定義規則,需要TypeFilter實現類

  • useDefaultFilters:不使用預設的 Filter進行掃描。在includeFilters中使用,取消預設掃描,指定掃描物件,可以在SpringMVC中使用

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

FilterType.CUSTOM:使用自定義規則,需要實現TypeFilter介面

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);
		//根據自定義規則,只讓類名中包含er的類進入ioc容器。
		if(className.contains("er")){
			return true;
		}
		return false;
	}

}

@Scope作用域

調整作用域,單例項,多例項

  • prototype:多例項的:ioc容器啟動並不會去呼叫方法建立物件放在容器中。每次獲取的時候才會呼叫方法建立物件;
  • singleton:單例項的(預設值):ioc容器啟動會呼叫方法建立物件放到ioc容器中。以後每次獲取就是直接從容器(map.get())中拿
  • request:同一次請求建立一個例項。web環境中
  • session:同一個session建立一個例項。web環境中
	@Scope("singleton")
	@Lazy
	@Bean("person")
	public Person person(){
		System.out.println("給容器中新增Person....");
		return new Person("張三", 25);
	}

@Lazy懶載入

  • 在單例項bean應用:預設在容器啟動的時候建立物件,配置懶載入後,容器啟動不建立物件。第一次使用(獲取)Bean建立物件,並初始化,仍是單例項。

⭐@Conditional條件註冊Bean

指定條件向容器中注入Bean

@Conditional({Condition}) : 按照一定的條件進行判斷,滿足條件才將Bean註冊進容器,可以標在@Bean方法和@Configuration類上

  • 新增了@Conditional({Condition}) 的Bean方法或配置Bean的類,只有在滿足Condition中的條件才能執行(裝配Bean)
  • Condition:需要實現Condition介面的條件類,可以判斷容器中的bean註冊情況,也可以給容器中註冊bean

Condition條件實現類

建立兩個判斷條件,判斷系統是Windows還是Linux,自定義註冊不同的Bean,註解可以標在方法上,也可以標在類上

  • 實現Condition介面
//判斷是否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();
		//5、獲取系統名
		String property = environment.getProperty("os.name");
		
		//這裡判斷容器中的bean註冊情況,也可以使用BeanDefinitionRegistry.registerBeanDefinition()給容器中註冊bean
		boolean definition = registry.containsBeanDefinition("person");
		
		//判斷系統名是否包含linux
		if(property.contains("linux")){
			return true;
		}
		return false;
	}
}
//判斷是否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;
	}

}

標在方法上

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

標在類上

//類中元件統一設定。滿足當前條件,這個類中配置的所有bean註冊才能生效;
@Conditional({WindowsCondition.class})
@Configuration
public class MainConfig2 {
	//...
}

測試方法

  • 使用,可配置VM引數:-Dos.name=linux 測試

    public class IOCTest {
        
    	AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
    	
    	@Test
    	public void test03(){
    		//獲取所有Person型別的Bean名字
    		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);
    		}
    		
    		//獲取所有Person型別Bean的物件,名字是否是lunus或bill
    		Map<String, Person> persons = applicationContext.getBeansOfType(Person.class);
    		System.out.println(persons);
    		
    	}
    }
    

⭐@Import註冊

@Import的三種方法都是寫在@Import註解中,都是向容器中匯入元件

  • @Import(要匯入到容器中的元件):容器中就會自動註冊這個元件,id預設是全類名
  • ImportSelector:返回需要匯入的元件的全類名陣列
  • ImportBeanDefinitionRegistrar:手動註冊bean到容器中

@Import

  • @Import(要匯入到容器中的元件):容器中就會自動註冊這個元件,id預設是全類名com.xxx.bean.Color

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

    public class IOCTest {
        
    	AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
    	
    	//輸出:除了Spring自己的Bean還有Import的Color、Red
    	@Test
    	public void testImport(){
    		printBeans(applicationContext);
    	}
    	
    	//輸出容器中所有的Bean名
    	private void printBeans(AnnotationConfigApplicationContext applicationContext){
    		String[] definitionNames = applicationContext.getBeanDefinitionNames();
    		for (String name : definitionNames) {
    			System.out.println(name);
    		}
    	}
    }
    

⭐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"};
    	}
    
    }
    
  • 配置

    @Configuration
    //@Import匯入元件,MyImportSelector是自定義選擇的Bean
    @Import({Color.class,Red.class,MyImportSelector.class})
    public class MainConfig2 {
        //...
    }
    
  • 測試

    public class IOCTest {
        
    	AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
    	
    	//輸出:除了Spring自己的Bean還有MyImportSelector選中的Bean
    	@Test
    	public void testImport(){
    		printBeans(applicationContext);
    		Blue bean = applicationContext.getBean(Blue.class);
    		System.out.println(bean);
    	}
    	
    	//輸出容器中所有的Bean名
    	private void printBeans(AnnotationConfigApplicationContext applicationContext){
    		String[] definitionNames = applicationContext.getBeanDefinitionNames();
    		for (String name : definitionNames) {
    			System.out.println(name);
    		}
    	}
    }
    

⭐ImportBeanDefinitionRegistrar

自定義注入元件,在Spring註解原始碼中使用

  • 實現介面,指定規則,手動注入新的bean

    public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    
    	/**
    	 * AnnotationMetadata:當前類的註解資訊
    	 * BeanDefinitionRegistry內有BeanDefinition註冊類,所有Bean都在這裡註冊;
    	 * 		把所有需要新增到容器中的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定義資訊BeanDefinition型別;(Bean的型別,Bean作用域等...)
    			RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
    			//註冊一個新Bean,指定bean名
    			registry.registerBeanDefinition("rainBow", beanDefinition);
    		}
    	}
    
    }
    
  • 配置

    @Configuration
    //MyImportBeanDefinitionRegistrar是手動註冊到Bean容器中,可以指定規則
    @Import({Color.class,Red.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
    //@Import匯入元件,id預設是元件的全類名
    public class MainConfig2 {
    	//...
    }
    
  • 測試

    public class IOCTest {
        
    	AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
    	
    	//輸出:除了Spring自己的Bean還有手動定製規則新匯入的rainBow
    	@Test
    	public void testImport(){
    		printBeans(applicationContext);
    		Blue bean = applicationContext.getBean(Blue.class);
    		System.out.println(bean);
    	}
    	
    	//輸出容器中所有的Bean名
    	private void printBeans(AnnotationConfigApplicationContext applicationContext){
    		String[] definitionNames = applicationContext.getBeanDefinitionNames();
    		for (String name : definitionNames) {
    			System.out.println(name);
    		}
    	}
    }
    

⭐FactoryBean註冊

多用於第三方元件

介面,給容器中注入元件,使用Spring提供的 FactoryBean(工廠Bean)建立的Bean

  1. 預設獲取到的是工廠bean呼叫getObject建立的物件,而不是工廠物件本身

  2. 要獲取工廠Bean本身,我們需要給id前面加一個"&"如:context.getBean("&xxx")

  • 實現介面,建立工廠

    //建立一個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;
    	}
    
    }
    
  • 配置

    @Configuration
    public class MainConfig2 {
    	
    	/**
    	 * 給容器中註冊元件;
    	 * 4)、使用Spring提供的 FactoryBean(工廠Bean);
    	 * 		1)、預設獲取到的是工廠bean呼叫getObject建立的物件
    	 * 		2)、要獲取工廠Bean本身,我們需要給id前面加一個&
    	 * 			&colorFactoryBean
    	 */
    	@Bean
    	public ColorFactoryBean colorFactoryBean(){
    		return new ColorFactoryBean();
    	}
    	
    }
    
  • 測試

    public class IOCTest {
        
    	AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
    	
    	//直接使用輸出的不是工廠本身,而是工廠內getObject建立的Bean
    	@Test
    	public void testImport(){
            
    		//工廠Bean獲取的是呼叫getObject建立的Color物件
    		Object bean2 = applicationContext.getBean("colorFactoryBean");
    		Object bean3 = applicationContext.getBean("colorFactoryBean");
    		//輸出:com.xxx.bean.Color
    		System.out.println("bean的型別:"+bean2.getClass());
    		System.out.println(bean2 == bean3);
    		
    		//輸出:com.xxx.bean.ColorFactoryBean
    		Object bean4 = applicationContext.getBean("&colorFactoryBean");
    		System.out.println(bean4.getClass());
    	}
    	
    	//輸出容器中所有的Bean名
    	private void printBeans(AnnotationConfigApplicationContext applicationContext){
    		String[] definitionNames = applicationContext.getBeanDefinitionNames();
    		for (String name : definitionNames) {
    			System.out.println(name);
    		}
    	}
    }
    

屬性賦值

@Value賦值

基本數值

public class Person {
	
	//使用@Value賦值;
	//1、基本數值
	@Value("張三")
	private String name;
}

SpEL表示式 #{}

public class Person {
	
	//使用@Value賦值;
	//1、基本數值
	//2、可以寫SpEL; #{}

	
	@Value("張三")
	private String name;
	
	//計算後值為18
	@Value("#{20-2}")
	private Integer age;
}

@PropertySource配置檔案取值

讀取properties配置檔案的值,例如讀取資料庫連線資訊

  • 讀取檔案
  • 取值

讀取配置檔案

使用@PropertySource讀取外部配置檔案

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

配置檔案person.properties

person.nickName=法外狂徒

配置檔案取值${}

取出配置檔案properties中的值

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

執行時環境變數取值

執行時properties配置檔案載入進環境變數,可以直接取出

public class IOCTest_PropertyValue {
	AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfPropertyValues.class);
	@Test
	public void test01(){
		//直接從applicationContext容器中獲取執行時環境變數ConfigurableEnvironment
		ConfigurableEnvironment environment = applicationContext.getEnvironment();
		//取值
		String property = environment.getProperty("person.nickName");
		System.out.println(property);
		applicationContext.close();
	}
}

自動裝配

AutowiredAnnotationBeanPostProcessor:解析完成自動裝配功能

幾種自動裝配:

  1. @Autowired:自動注入
  2. Spring還支援使用@Resource(JSR250)和@Inject(JSR330)[java規範的註解]
  3. @Autowired:構造器,引數,方法,屬性,都是從容器中獲取引數元件的值
  4. 自定義元件想要使用Spring容器底層的一些元件(ApplicationContext,BeanFactory,xxx)

@Autowired

自動注入:

  1. 預設優先按照型別去容器中找對應的元件,同applicationContext.getBean(BookDao.class),找到就賦值
  2. 如果找到多個相同型別的元件,再將屬性的名稱(@Bean的value或者方法名)作為元件的id去容器中查詢,同applicationContext.getBean("bookDao")
  3. 如果都相同就會報錯

@Qualifier

指定裝配:

  • @Qualifier("bookDao"):使用@Qualifier指定需要裝配的元件的id,而不是使用屬性名自動裝配,預設一定要將屬性賦值好,沒有就會報錯;
  • 如果沒有就不裝配,可以使用@Autowired(required=false)表示

@Primary

預設裝配:

  • @Primary:讓Spring進行自動裝配的時候,預設使用首選的bean;
  • 也可以繼續使用@Qualifier指定需要裝配的bean的名字

下面是兩個Bean的情況

@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;
	}
}
@Service
public class BookService {
	
	@Qualifier("bookDao")//指定,大於首選
	@Autowired(required=false)//可以不存在
	private BookDao bookDao;
}

@Resource

  • 可以和@Autowired一樣實現自動裝配功能,預設是按照元件名稱進行裝配的

  • 沒有能支援@Primary和@Autowired(reqiured=false)的功能

  • @Autowired跟spring強耦合,如果換成了JFinal等其他框架,功能就會失效。而@Resource是JSR-250提供的,它是Java標準,絕大部分框架都支援

	@Resource(name="bookDao2")
	private BookDao bookDao;

@Inject

  • 需要匯入javax.inject的包,和@Autowired的功能一樣。沒有@Autowired(reqiured=false)的功能

  • @Autowired:Spring定義的, @Resource、@Inject都是java規範,@Autowried不能脫離Spring

	@Inject
	private BookDao bookDao;

⭐@Autowired注入方法,引數

@Autowired:構造器,引數,方法,屬性都是從容器中獲取引數元件的值

使用[@Bean+方法引數]的方式在SpringBoot中使用很多,在使用各種配置時,只配置方法,方法中的引數直接從容器中獲取

  1. [標註在方法位置]:@Bean+方法引數,引數從容器中獲取,預設不寫@Autowired效果是一樣的,都能自動裝配
  2. [標在構造器上]:如果元件只有一個有參構造器,這個有參構造器的@Autowired可以省略,引數位置的元件還是可以自動從容器中獲取
  3. 放在引數位置

方法上

  • @Autowired直接標在方法上

    	@Autowired 
    	//標註在方法,Spring容器建立當前物件,就會呼叫方法,完成賦值;
    	//方法使用的引數,自定義型別的值從ioc容器中獲取
    	public void setCar(Car car) {
    		this.car = car;
    	}
    
  • 如果是@Bean標註的方法,引數位置不標@Autowired,引數也可以自動裝配,都是從容器中獲取

    	/**
    	 * @Bean標註的方法建立物件的時候,方法引數的值從容器中獲取
    	 * @param car car也是從容器中獲取的值,前提是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;
    	
    	//構造器要用的元件,都是從容器中獲取
    	//@Autowired
    	public Boss(Car car){
    		this.car = car;
    		System.out.println("Boss...有參構造器");
    	}
    }
    

引數上

  • 標在引數前

    	public void setCar(@Autowired  Car car) {
    		this.car = car;
    	}
    

⭐Aware使用Spring底層元件

  • 自定義元件想要使用Spring容器底層的一些元件(如:ApplicationContext,BeanFactory,xxx)只要自定義元件實現xxxAware

  • 在建立物件的時候,會呼叫介面規定的方法注入相關元件,把Spring底層一些元件注入到自定義的Bean中

  • xxxAware:對應功能使用xxxProcessor

    ApplicationContextAware==>ApplicationContextAwareProcessor

    實現過程在BeanPostProcessor後置處理器中

使用不同的底層元件

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

	//使用IOC
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		// TODO Auto-generated method stub
		System.out.println("傳入的ioc:"+applicationContext);
		this.applicationContext = applicationContext;
	}
	
	//使用BeanName
	@Override
	public void setBeanName(String name) {
		// TODO Auto-generated method stub
		System.out.println("當前bean的名字:"+name);
	}

	//使用EmbeddedValueResolve解析元件
	@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:Spring為我們提供的可以根據當前環境,動態的啟用和切換一系列元件的功能;

環境:開發環境、測試環境、生產環境,分別對應:
資料來源:devDataSource、testDataSource、prodDataSource

@Profle:指定元件在哪個環境的情況下才能被註冊到容器中,不指定,任何環境下都能註冊這個元件

  • 標註
    1. 加了環境標識的bean,只有這個環境被啟用的時候才能註冊到容器中。預設是default環境
    2. 寫在配置類上,只有是指定的環境的時候,整個配置類裡面的所有配置才能開始生效
    3. 沒有標註環境標識的bean,在任何環境下都是載入的;
  • 使用
    1. 如果對Bean標註了@Profile("default"),即使不啟用,也預設使用此環境。即使標註了,如果沒有對應的環境則都不啟用,除了"default"
    2. 在啟動時新增VM引數-Dspring.profiles.active=test,會使用此環境
    3. 不使用有參的AnnotationConfigApplicationContext建立ioc,使用無參構造器,在註冊配置類之前設定啟用環境。
    4. [SpringBoot]在類上使用註解@ActiveProfiles("test")啟用

建立多種資料來源環境匹配不同資料庫

//使用多種方法讀取properties配置檔案資訊
//第一種方法讀取配置檔案
@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MainConfigOfProfile implements EmbeddedValueResolverAware{
	
	//第二種方法讀取配置檔案
	@Value("${db.user}")
	private String user;
	
	private StringValueResolver valueResolver;
	
	private String  driverClass;
	
	//普通的Bean,任何時候都可以載入進容器
	@Bean
	public Yellow yellow(){
		return new Yellow();
	}
	
	//測試環境標識
	@Profile("test")
	//@Profile("default")
	@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}");
	}

}

測試

//啟用環境
public class IOCTest_Profile {
	
	//1、使用命令列動態引數: 在虛擬機器引數位置載入 -Dspring.profiles.active=test
	//2、程式碼的方式啟用某種環境;
	@Test
	public void test01(){
        //1、建立一個applicationContext
		AnnotationConfigApplicationContext applicationContext = 
				new AnnotationConfigApplicationContext();
		//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);
		}
		
		//普通Bean不受影響,一直可用
		Yellow bean = applicationContext.getBean(Yellow.class);
		System.out.println(bean);
		applicationContext.close();
	}
}

輸出

devDataSource
com.atguigu.bean.Yellow@1ab3a8c8

相關文章