一、配置類
我們想將一個類註冊到IOC容器中首先要有個配置,一般有基於xml配置文件的方式和註解的方式。
已知Person類
package com.tan.bean;
public class Person {
private String name;
private Integer age;
}
1、xml配置的方式(配置文件爲:bean.xml)
<bean id="person" class="com.tan.bean.Person"> <property name="name" value="張三"></property> <property name="age" value="18"></property> </bean>
2、基於註解的方式的配置(等於xml的配置文件)
- @Configuration:告訴spring這是一個配置類。
二、註冊方式(給容器中註冊組件的方式)
- 包掃描 + 組件標註註解(@Controller,@Service, @Repository,@Component)
- @Bean[導入的第三方包裏面的組件]
- @Import[快速給容器中導入一個組件]
1)、@Import(要導入到容器中的組件);容器中就會自動註冊這個組件,id默認是全類名
2)、@ImportSelector 返回需要導入的組件的全類名數組
3)、@ImportBeanDefinitionRegistrar 手動將bean註冊到容器中
- 使用spring提供的FactoryBean(工廠bean)
1、包掃描 + 組件標註註解(@Controller,@Service, @Repository,@Component)
@ComponentScan:包掃描
@ComponentScan(value = "com.tan", excludeFilters = {
//@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class}),
//type:按什麼類型排除,以上是按註解去排除
//classes:指定掃描的時候按照什麼規則排除哪些組建
// includeFilters + useDefaultFilters=false 只掃描classes中的組建
//@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookService.class}),
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
})
參數解析
value:指定要掃描的包
包過濾掃描:
excludeFilters = Filter[]:指定掃描的時候按照什麼規則排除哪些組件
includeFilters = Filter[]:指定掃描的時候只需要包含哪些組件,(需要關閉默認全部掃描的設置,useDefaultFilters=false)
@Filter中參數解析:
type:按什麼類型排除
FilterType.ANNOTATION 按照註解進行掃描 FilterType.ASSIGNABLE_TYPE 按照給定的類型 FilterType.ASPECTJ 使用ASPECTJ表達式方式 FilterType.REGEX 使用正則表達式方式 FilterType.CUSTOM 使用自定義規則
classes:指定掃描的時候按照什麼規則排除哪些組件
自定義過濾掃描包
@ComponentScan(value = "com.tan", excludeFilters = {
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
})
自定義掃描規則類實例
package com.tan.config;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import java.io.IOException;
/**
* 自定義包掃描過濾規則
*/
public class MyTypeFilter implements TypeFilter{
/**
* @param metadataReader 讀取到的當前正在掃描的類的信息
* @param metadataReaderFactory 可以獲取到其他任何類信息
* @return
* @throws IOException
*/
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//獲取當前類註解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//獲取當前正在掃描的類的類信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//獲取當前類資源信息(類的路徑)
Resource resource = metadataReader.getResource();
String className = classMetadata.getClassName();
System.out.println("--->" + className);
if (className.contains("er")) { //bean名字中包含er字符的返回true
return true;
}
return false;
}
}
2、@Bean[導入的第三方包裏面的組件]
@Conditional({MacConditional.class})
@Scope("prototype")
@Lazy //懶加載模式,容器在啓動的時候不會創建,當獲取一次的時候會加載,第二次再進來就不會在new
了
@Bean(value = "person02")
public Person person() {
return new Person("張三", 10);
}
@Bean("person"):給容器註冊一個bean (類型爲返回值的類型,id默認用方法名作爲id)。 @Lazy:懶加載模式,容器在啓動的時候不會創建,當獲取一次的時候會加載,第二次再進來就不會在new了。
Scope 註解調整作用域:
- @Scope("singleton"):單實例的(默認是單實例的) ioc容器啓動會調用方法創建對象放到ioc容器中,以後每次獲取就是 直接從容器(map.get())中拿。
- @Scope("prototype"):多實例的,ioc啓動並不會去調用方法創建對象放在容器中,而是在每次獲取bean的時候,纔會 調用方法創建對象,每次獲取的時候都會調一遍。
- @Scope("request"):同一次請求創建一個實例(基於web的)。
- @Scope("session"):同一個session創建一個實例(基於web的)。
@Conditional({Conditional}) :按照一定的條件進行判斷,滿足條件給容器中註冊bean。類中組件統一設置,滿足當前條件,這個類中配置的所有bean註冊才能生效,可以放在類上也可以放在方法上。
Conditional實例:
/**
* @Conditional({Conditional}) 按照一定的條件進行判斷,滿足條件給容器中註冊bean
* 如果系統是mac,放這個bean
* 如果是linux系統就放linus這個bean
*/
@Conditional({MacConditional.class})
@Bean(value = "mac")
public Person person02() {
return new Person("mac", 40);
}
@Conditional({LinuxConditional.class})
@Bean(value = "linus")
public Person person03() {
return new Person("linus", 50);
}
條件過濾類:
/**
* 判斷是否是mac系統
*/
public class MacConditional implements Condition{
/**
*
* @param context 判斷條件,能使用的上下文(環境)
* @param metadata 註釋信息
* @return
*/
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//是否mac系統
//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");
if (property.contains("Mac OS X")) {
return true;
}
return false;
}
}
MacConditional實現了Condition接口,實現了matches方法,方法中的參數類(ConditionContext)可以獲取到ioc容器的beanfactory,可以獲取類加載器,可以獲取當前環境,可以獲取bean定義的註冊類。
LinuxConditional類同理(略)........
3、@Import[快速給容器中導入一個組件]
導入組件,id默認是組件的全類名
@Import({Color.class, Red.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
1)、@Import(要導入到容器中的組件);容器中就會自動註冊這個組件,id默認是全類名。
2)、@ImportSelector 返回需要導入的組件的全類名數組。
3)、@ImportBeanDefinitionRegistrar 手動將bean註冊到容器中。
@ImportSelector註冊方式:
/**
* 自定義邏輯返回需要導入的組件
*/
public class MyImportSelector implements ImportSelector{
/**
* 返回值,就是導入到容器中的組件全類名
* @param importingClassMetadata 當前標註@Import註解的類的所有註解信息
* @return
*/
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.tan.bean.Blue", "com.tan.bean.Yellow"};
}
}
@ImportBeanDefinitionRegistrar手動註冊方式
/**
* 手動註冊
*/
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar{
/**
* @param importingClassMetadata 當前類的註解信息
* @param registry BeanDefinition註冊類
* 把所有需要添加到容器中的bean調用
* BeanDefinitionRegistry.registerBeanDefinition()手工註冊進來
*/
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean definition1 = registry.containsBeanDefinition("com.tan.bean.Red");
boolean definition2 = registry.containsBeanDefinition("com.tan.bean.Blue");
if (definition1 && definition2) {
//指定bean的定義信息(bean的類型,作用域啥的都可以在這指定)
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Black.class);
//註冊一個bean,指定bean名
registry.registerBeanDefinition("black", rootBeanDefinition);
}
}
}
4、使用spring提供的FactoryBean(工廠bean)
@Bean public ColorFactoryBean colorFactoryBean() {
return new ColorFactoryBean();
}
實現類:
/**
* 創建一個spring定義的FactoryBean
*/
public class ColorFactoryBean implements FactoryBean<Color>{
/**
* 返回color對象,這個對象會添加到容器中
* @return
* @throws Exception
*/
public Color getObject() throws Exception {
System.out.println("ColorFactoryBean...getObject...");
return new Color();
}
public Class<?> getObjectType() {
return Color.class;
}
/**
* 是單實例?
* true: 這個bean是單實例,在容器中保存一份
* false: 多實例,每次獲取都會創建一個新的bean
* @return
*/
public boolean isSingleton() {
return false;
}
}
分析:
使用spring提供的FactoryBean(工廠bean), 默認獲取到的是工廠bean調用getObject創建的對象,以上獲取就是Color的bean
要獲取bean本身,我們需要給id前面加一個& (&colorFactoryBean)才能回去。
springboot中大量使用了這種方式。
三、bean的獲取方式
1、基於xml的方式的獲取
/*********基於spring xml配置文件式開發**********/
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
Person person = (Person) applicationContext.getBean("person");
System.out.println(person);
2、基於註解配置文件的方式獲取
/*********基於spring 註解式開發**********/
ApplicationContext applicationContext= new AnnotationConfigApplicationContext(MainConfig.class);
Person person = applicationContext.getBean(Person.class);
System.out.println(person);
//通過類名稱去找bean的名稱
String[] beanNamesForType = applicationContext.getBeanNamesForType(Person.class);
for (String name : beanNamesForType) {
System.out.println(name);
}