Spring 註解下IOC&DI

IOC&DI

組件註冊

簡單配置

配置Configuration類(主配置類),等價於在xml方式當中配置xml配置文件

主配置類**@Configuration**註解標註

配置Bean

@Bean標註返回bean的方法,可以傳入參數指定bean的id

如果不指定則方法名就是bena的id

ApplicationContext使用實現類AnnotationConfigApplicationContext

構造參數爲配置類class對象,獲取bean 依舊使用getBean方法

MainConfig.java

@Configuration
public class MainConfig{
    @Bean("person")				//參數爲指定bean的name
    public Person getPerson(){
        return new Person("Hari",12);
    }
}

MainTest.java

public class MainTest{
    public void main(String args[]){
        ApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class);
        Person p = (Person)ctx.getBean("person");
    }
}

組件掃描

組件註解標註,包掃描配置到容器

在配置類當中加入**@ComponentScan**註解,

屬性value接受參數爲字符串數組,掃描的包數組

組件過濾

屬性excludeFilters指定排除掃描類的過濾規則

屬性includeFilters指定包含掃描類的過濾規則

​ 過濾規則屬性接受參數爲**@Filter**數組

在這裏插入圖片描述

常用兩種爲annotationassinable

includeFilters有默認過濾規則即自動掃描帶有 @Component、@Repository、@Service 和 @Controller 的類

如果需要完全自定義過濾規則,需要先把默認過濾規則關閉,即設置**@ComponentScan註解的useDefaultFiltersfalse**

以includeFilters爲用例

@ComponentScan(value="com.nond",
               useDefaultFilters=false,
             includeFilters={
                  @Filter(type = FilterType.ANNOTATION,classes = Service.class)  //掃描所有@Service標註的類
              	}
              )
public class MainConfig{
    ...
}

@Filter標籤內Type爲過濾類型,使用FilterType當中的靜態常量,

annotation、assinable、custom:根據類過濾,第二個屬性使用classes

aspectj、regex:根據表達式過濾,第二個屬性使用pattern

組件標註照舊使用@Component、@Repository、@Service 和 @Controller

如果需要配置多種掃描策略

還可以使用@ComponentScans註解,該註解接受@ComponentScan[]數組,配置多種掃描策略

CUSTOM方式過濾

即使用自定義的過濾規則進行過濾

@Filter註解當中type使用``FilterType.CUSTOM`靜態常量,classes傳入自定義的匹配規則類即可

創建自定義匹配規則類

實現TypeFilter接口

實現接口的public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)方法

通過參數metaReader可以獲得當前掃描中的類的註解信息數據、類信息數據、類資源路徑信息數據,分別使用getAnnotationMetadata()getClassMetadata()getResource()方法獲取

通過metadataReaderFactory參數的getMetadataReader()方法還能夠獲取到其他類的MetadataReader

其中返回值爲是否匹配成功

實例

/*
自定義匹配規則類
規則爲裝配類名當中包含My的類到容器當中
*/

public class CustomFilter implements TypeFilter {

	public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
			throws IOException {
		return metadataReader.getClassMetadata().getClassName().contains("My");
	}

}



//主配置類註解
@ComponentScan(
		useDefaultFilters = false,
		includeFilters = {@Filter(type = FilterType.CUSTOM,classes = CustomFilter.class)}
		)


/**
*定義有類MyComputer,YourComputer
*/

//測試代碼
@Test
public void test() {
	ApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class);
	String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
	for(String str:beanDefinitionNames)
	{
		System.out.println(str);
	}
}

/*輸出結果
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
myComputer
*/

Bean作用域

Bean常用有四種作用域

  • ConfigurableBeanFactory.SCOPE_PROTOTYPE多實例
  • ConfigurableBeanFactory.SCOPE_SINGLETON單實例
  • WebApplicationContext.SCOPE_REQUEST請求
  • WebApplicationContext.SCOPE_SESSION會話

配置Bean的作用域

@Scope註解,在方法或類使用

@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public class YourComputer {...}






@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
@Bean
public YourComputer yourComputer(){
    return new YourComputer();
}

懶加載

單實例bean默認在容器創建時創建

多實例bean在需要取用bean時才創建

針對單實例bean,可以使用@Lazy註解標註使之懶加載,即取用bean時創建

條件注入

滿足條件時才把bean注入到容器當中

使用**@Conditional**註解標註方法或類,根據定製的Condition條件進行判斷

該註解接受一個Condition接口數組

實現Conndition接口

複寫public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata)方法

通過方法的兩個參數能夠獲取,運行環境信息和註解信息

match方法的返回值爲 boolean,決定條件是否成立

實例

//MyCondition.java
public class MyCondition implements Condition {
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		return false;
	}
}


//MyComputer.java
@Component
@Conditional(value = {MyCondition.class})
public class MyComputer {...}



//MainTest.java
ApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class);
String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
for(String str:beanDefinitionNames)
{
	System.out.println(str);
}



Output:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig

使用@Import導入bean

@Import註解的作用是將多個配置類整合到一個主配置類當中,避免所有的配置寫在一個配置類

Spring4.2之前,只支持使用**@Import**註解導入配置類,後面版本支持將普通類導入並將其聲明成一個bean

多配置類整合

@Import(value = {config1.class,condig2.class})

同時使用這種方式還能導入無參構造器創建的對象

使用ImportSelector接口導入

實現public String[] selectImports(AnnotationMetadata importingClassMetadata)方法

返回值爲全類名列表

@Import註解通過全類名列表導入這些類的無參構造器創建的bean

MainConfig.java
@Import(value = {MyImportSelector.class})
@Configuration
public class MainConfig{
    ...
}

MyImportSelector.java
public class MyImportSelector implements ImportSelector {

	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		return new String[] {"com.nond.importselector.Person"};
	}

}

Person.java
public class Person{...}

使用ImportBeanDefinitionRegistrar接口

使用這種接口導入對Bean有更多的操作權限

實現ImportBeanDefinitionRegistrar接口

實現public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)方法

創建BeanDefinition使用實現類RootBeanDefinition

調用registry.registerBeanDefinition(“beanID”,BeanDefinition)

其中使用importingClassMetaData能夠獲取到使用Import註解標註類的類信息

MyImportBeanDefinitionRegistar.java

public class MyImportBeanDefinitionRegistar implements ImportBeanDefinitionRegistrar {

	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Person.class);
		registry.registerBeanDefinition("person", rootBeanDefinition);
	}
}

FactoryBean

FactoryBean的作用是隱藏實例化一些複雜Bean的細節

FactoryBean跟普通Bean不同,其返回的對象不是指定類的一個實例,而是該FactoryBean的getObject方法所返回的對象。創建出來的對象是否屬於單例由isSingleton中的返回決定。

實現FactoryBean接口

public class PersonFactoryBean implements FactoryBean{

	public Object getObject() throws Exception {
		return new Person("Hari",16);
	}

	public Class getObjectType() {
		return Person.class;
	}

	/**
	 * 該方法的返回值決定使用FactoryBean創建的Bean是否是單例的
	 * true:創建的Bean爲單例
	 * false:創建的Bean爲多例
	 */
	public boolean isSingleton() {
		return false;
	}
	
}

配置到Config類

	@Bean
	public PersonFactoryBean person() {
		return new PersonFactoryBean();
	}

如果需要獲得FactoryBean本身,只需要在獲取Bean的時候在id前加&即可

ctx.getBean("&myFactoryBean")

生命週期

初始化後與銷燬前方法

給Bean指定初始化(Bean創建並且賦值完成後)和銷燬(Bean銷燬之前)方法

  1. 在類中創建初始化和銷燬方法並在**@Bean** 註解當中使用init-method屬性和destory-method屬性指定初始化和銷燬方法

    Person.java
    public class Person {
    	public Person() {
    		System.out.println("Construct method call...");
    	}
    	
    	public void init() {
    		System.out.println("init method call...");
    	}
    	
    	public void destory() {
    		System.out.println("destory method call...");
    	}
    }
    
    MainConfig.java
    @Configuration
    public class MainConfig {
    
    	@Bean(initMethod = "init",destroyMethod = "destory")
    	public Person person() {
    		return new Person();
    	}
    }
    
    Output:
    
    	Construct method call...
    	init method call...
    
  2. Bean類實現DisposableBean,InitializingBean接口,分別對應銷燬和初始化

    Person.java
    @Component
    public class Person implements InitializingBean,DisposableBean{
    	public Person() {
    		System.out.println("Construct method call...");
    	}
    	
    	
    	public void destroy() throws Exception {
    		System.out.println("destroy() call...");
    	}
    
    	public void afterPropertiesSet() throws Exception {
    		System.out.println("afterPropertiesSet() call...");
    	}
    }
    
    MainConfig.java
    @Configuration
    @ComponentScan("com.nond.lifecycle")
    public class MainConfig {...}
    
    
    Output:
    	Construct method call...
    	afterPropertiesSet() call...
    
  3. JSR250標準,使用**@PostConstruct**,@PreDestory註解標註bean的初始化銷燬方法(需要導入JSR250依賴)

    Person.java
    
    @Component
    public class Person{
    	public Person() {
    		System.out.println("Construct method call...");
    	}
    	
    	@PreDestroy
    	public void destroy(){
    		System.out.println("destroy() call...");
    	}
    	
    	@PostConstruct
    	public void init(){
    		System.out.println("init() call...");
    	}
    }
    
    Output:
    	Construct method call...
    	init() call...
    
    

BeanPostProcessor處理器

BeanPostProcessor處理器在bean初始化方法調用前後進行處理

該處理器裝配到容器當中會後,面向所有的容器當中的bean,即對每個裝配到容器中的bean都進行處理

創建及使用

實現PostBeanProcessor接口

實現其兩個方法,兩個方法的返回值都是處理後的bean

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException初始化方法前

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException初始化方法後

然後將其裝配到容器當中

Bean生命週期中有關於BeanPostProcessor的流程

  1. bean創建
  2. bean賦值
  3. postProcessBeforeInitialization()
  4. bean初始化
  5. postProcessAfterInitialization()

如果在postProcessBeforeInitialization返回的bean是null,流程終止,直接跳出返回null

在Spring底層當中大量的使用到了BeanProcessor,如在初始化前後,bean 屬性注入,組件注入等

屬性賦值

使用@Value標籤

該標籤的作用是用於給屬性賦值

標籤能夠傳入基本數據類型

能夠使用Spel表達式#{...}

還能獲取到資源文件當中的信息(運行環境的環境變量)${...}

獲取properties文件屬性

@PropertySource註解在MainConfig當中說明引入的資源文件

@PropertySource("classpath:/db.properties")

然後在@Value標籤使用${...}

或者用運行環境獲取環境變量方式獲取

Environment environment = ctx.getEnvironment();
environment.getProperty("person.name");

自動裝配

@Autowired

使用@Autowired標註的屬性,自動在容器當中尋找類型相匹配的bean裝配

如果在容器當中,類型匹配的bean有多個,默認情況下注入的是和屬性名相同的bean

還可以使用@Primary註解標註bean,說明在有多個類型匹配的bean情況下,首選使用使用該bean注入

還可以使用@Qualifier註解標註需要自動裝配的bean,說明一定要注入名爲**@Qualifier**註解value 值的bean

如果在容器當中沒有找到bean則不需要注入,則可以使用@autowired註解的required屬性,false爲該屬性在沒有找到能夠注入的bean時不注入,如果沒有指定這個註解的屬性,在沒有找到bean的情況下會報錯

@Autowired註解不止能夠標註在屬性上

還能夠標註在構造方法上setter方法上,以及標註在方法參數上

注意點1

如果被標註需要裝配到容器當中的類,該類只有一個有參構造器且其中參數需要容器當中取用

則在這種情況下可以不使用**@Autowired**註解標註也能夠自動裝配

注意點2

在主配置類當中用**@Bean**標註方法裝配的bean

參數需要自動裝配,這種情況下也可以不使用**@Autowired**的標註也會自動裝配

除了Autowired註解以外還有@Resource(JSR250),@Inject(JSR330)

即對JCP規範的支持

@Resource註解

​ 通過設置name,type兩個屬性能夠確定尋找bean的範圍

​ name屬性,尋找容器當中ID和name屬性值匹配的bean,如果不存在,報錯

​ type,屬性,尋找容器當中類型相匹配的bean,如果不存在或者存在多個,報錯

@Inject註解

​ 和autowired相比缺少了required=false功能,需要導入javax.inject包

自動裝配Spring底層的bean

需要裝配Spring底層bean的類實現xxxxxAware接口

實現接口的方法,通過其中的參數就能拿到底層的bean

通過這種方式可以拿到字符串解析器,容器等對象

這種實現接口自動裝配的方法也是通過PostBeanProcessor方式實現的

給bean裝配容器對象實例

@Component
public class Person implements ApplicationContextAware{
	ApplicationContext ctx;

	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.ctx = applicationContext;
	}
}

@Profile的使用

在生產過程中有多種程序的運行環境,如開發環境,測試環境,生產環境

在多種生產環境之下需要暴露的bean也不盡相同,比如各種環境下的數據源

使用profile能夠給bean指定暴露在哪種運行環境下

四種環境

  • default默認
  • dev開發
  • test測試
  • prod生產

沒有使用@Profile標註的bean,在任何情況下都能夠使用

改變運行環境

方法1

設置運行參數-Dspring.profiles.active=xxx

方法2

通過applicationContext設置運行環境

通過無參構造器創建一個空的applicationContext

applicationContext.getEnvironment().setProfiles(args…)能夠同時設置多個運行環境

applicationContext.register(mainconfig.class)把主配置類註冊進去

applicationContext.refresh()刷新applicationContext

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章