一、使用@Configuration
和@Bean
给容器中注册组件
/**
* 配置类,使用@Configuration注解标识这是一个配置类
*/
@Configuration
public class MainConfig {
/*@Bean注解:给容器中注册一个bean
* class类型为返回值的类型
* id默认是方法名
* */
@Bean(value = "person")
public Person person(){
return new Person(1,"lisi");
}
}
测试配置类
@Test
public void testConfiguration(){
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
//使用class类型获取bean
Person person = context.getBean(Person.class);
//使用id获取bean
Person person1 = (Person) context.getBean("person");
System.out.println(person);
System.out.println(person1);
//容器中都是同一个bean
System.out.println(person1 == person);
}
输出结果:
二、@ComponentScan
自动扫描组件
@Configuration
@ComponentScan(value = {"bean","controller","dao","service"})
public class MainConfig {
/*@Bean注解:给容器中注册一个bean
* class类型为返回值的类型
* id默认是方法名
* */
@Bean(value = "person")
public Person person(){
return new Person(1,"lisi");
}
}
测试扫描到了容器中哪些组件
@Test
public void testComponentScan(){
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String beanName : beanDefinitionNames){
System.out.println(beanName);
}
}
输出结果:
还可以使用@Filter
注解指定排除规则
@Configuration
@ComponentScan(value = "spring.annotation",excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class})
})
public class MainConfig {
/*@Bean注解:给容器中注册一个bean
* class类型为返回值的类型
* id默认是方法名
* */
@Bean(value = "person")
public Person person(){
return new Person(1,"lisi");
}
}
输出结果:
三、自定义TypeFilter
指定过滤规则
public enum FilterType {
ANNOTATION,
ASSIGNABLE_TYPE,
ASPECTJ,
REGEX,
CUSTOM;
private FilterType() {
}
}
//按照注解过滤
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class})}
//按照指定的类型过滤
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {BookService.class})}
还可以自定义过滤规则,首先必须创建TypeFilter
接口的实现类
/** Filter candidates using a given custom
* {@link org.springframework.core.type.filter.TypeFilter} implementation.
*/
CUSTOM
创建MyTypeFilter
自定义过滤规则类
public class MyTypeFilter implements TypeFilter {
/**
*
* @param metadataReader 读取到的当前正在扫描的类的信息
* @param metadataReaderFactory 可以获取到其它任何类的信息
* @return Boolean 返回true或false代表是否匹配成功
* @throws IOException
*/
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//获取当前扫描类的注解信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取当前扫描类的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//获取当前扫描类的资源信息
Resource resource = metadataReader.getResource();
//匹配全类名中包含“er”的类
if (className.contains("er")){
return true;
}
return false;
}
}
在@ComponentScan
扫描组件中指定自定义的过滤规则
@ComponentScan(value = "spring.annotation",includeFilters = {
@ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyTypeFilter.class})
},useDefaultFilters = false)
输出结果:
四、@Scope
设置组件作用域
容器中bean
默认是单实例的
@Test
public void testScope(){
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
//容器中bean默认是单实例的
Object person = context.getBean("person");
Object person1 = context.getBean("person");
System.out.println(person == person1); //true
}
可以使用@Scope
来指定作用域
ConfigurableBeanFactory#SCOPE_PROTOTYPE //多实例
ConfigurableBeanFactory#SCOPE_SINGLETON //单实例,默认值
org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST //同一次请求创建一个实例
org.springframework.web.context.WebApplicationContext#SCOPE_SESSION //同一个Session创建一个实例
设置为多实例
@Configuration
public class MainConfig2 {
@Bean
@Scope(value = "prototype")
public Person person(){
return new Person(2,"张三");
}
}
测试
@Test
public void testScope(){
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
//容器中bean默认是单实例的
Object person = context.getBean("person");
Object person1 = context.getBean("person");
System.out.println("person == person1 : " + (person == person1));
}
输出结果:
注意:
- 作用域是单实例,ioc容器启动会调用方法创建对象放到ioc容器中,以后每次获取就直接从ioc容器中拿。
- 作用域是多实例,ioc容器启动时不会调用方法创建对象,而是在每次获取对象的时候,才会调用方法创建对象。
五、@Lazy
懒加载
针对于单实例bean,默认在容器启动的时候就创建好对象。而懒加载就是使得在容器启动的时候暂时不创建对象,等到第一次获取bean的时候,再创建对象并进行初始化。
@Configuration
public class MainConfig2 {
@Bean
@Lazy
public Person person(){
return new Person(2,"张三");
}
}
六、@Conditional
按照条件注册bean
所有的bean都会默认自动加载到容器中
@Test
public void testCondition(){
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String beanName : beanDefinitionNames){
System.out.println(beanName);
}
}
输出结果:
可以在方法上加上@Conditional
来按照条件将bean加载到容器,首先要创建实现Condition
接口的实现类
P1Condition
类是当容器中存在p2这个bean的时候,就不会创建p1
public class P1Condition implements Condition {
/**
*
* @param context 判断条件能使用的上下文(环境)
* @param metadata 注释信息
* @return 是否匹配
*/
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//可以获取到ioc使用的beanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//可以获取类加载器
ClassLoader classLoader = context.getClassLoader();
//可以获取当前环境信息
Environment environment = context.getEnvironment();
//可以获取bean定义的注册类
BeanDefinitionRegistry registry = context.getRegistry();
String[] beanDefinitionNames = registry.getBeanDefinitionNames();
for (String beanName : beanDefinitionNames){
if ("p2".equals(beanName)){ //容器中存在p2这个bean的时候,就不会创建p1
return false;
}
}
return true;
}
}
P2Condition
类是当容器中存在p1这个bean的时候,就不会创建p2
public class P2Condition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
BeanDefinitionRegistry registry = context.getRegistry();
String[] beanDefinitionNames = registry.getBeanDefinitionNames();
for (String beanName : beanDefinitionNames) {
if ("p1".equals(beanName)) { //容器中存在p1这个bean的时候,就不会创建p2
return false;
}
}
return true;
}
}
使用条件注解限制p1和p2同时加载到容器中
@Configuration
public class MainConfig2 {
@Bean
//@Scope(value = "prototype")
@Lazy
public Person person(){
return new Person(2,"张三");
}
@Conditional({P1Condition.class})
@Bean
public Person p1(){
return new Person(1,"person1");
}
@Conditional({P2Condition.class})
@Bean
public Person p2(){
return new Person(1,"person2");
}
}
测试
@Test
public void testCondition(){
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String beanName : beanDefinitionNames){
System.out.println(beanName);
}
输出结果:
bean加载顺序与配置类中定义的方法的先后顺序有关
七、@import
给容器中快速导入一个组件
给配置类加上@import注解,可以快速到日一个组件到ioc容器中
@Configuration
@Import({Man.class}) //可以导入多个
public class MainConfig2 {
}
查看ioc容器中所有的bean
@Test
public void testImport(){
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String beanName : beanDefinitionNames){
System.out.println(beanName);
}
}
输出结果:
还可以通过创建ImportSelector
接口的实现类,来自定义需要加载的组件
/**
* 自定义逻辑。返回需要的组件
*/
public class MyImportSelector implements ImportSelector{
/**
*
* @param importingClassMetadata 当前标注@Import注解的类的所有注解信息
* @return 返回值就是要导入到容器中的组件的全类名
*/
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"spring.annotation.bean.Boy","spring.annotation.bean.Woman"};
}
}
配置类中导入自定义的组件选择器
@Configuration
@Import({Man.class, MyImportSelector.class})
public class MainConfig2 {}
输出结果:
还可以通过创建ImportBeanDefinitionRegistrar
接口的实现类来手动注册bean到容器中
public class MyImportBeanDefinitionRegistar implements ImportBeanDefinitionRegistrar {
/**
*
* @param importingClassMetadata 当前类的注解信息
* @param registry BeanDefinition注册类,
* 可以调用BeanDefinitionRegistry类里面的registerBeanDefinition方法
* 把所有需要bean手动注册到容器中
*/
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean man = registry.containsBeanDefinition("spring.annotation.bean.Man");
boolean boy = registry.containsBeanDefinition("spring.annotation.bean.Boy");
if (man && boy){ //如果容器中同时又man和boy两个bean就注册girl这个bean到容器中
registry.registerBeanDefinition("girl",new RootBeanDefinition(Girl.class));
}
}
}
配置类
@Configuration
@Import({Man.class, MyImportSelector.class, MyImportBeanDefinitionRegistar.class})
public class MainConfig2 {
输出结果:
给容器中注册组件的方式:
- 包扫描+组件标注注解(
@Controller/@Service/@Repository/@Component
)。 - 使用
@Bean
导入第三方包里面的组件,或者自己写的。 - 使用@Import快速导入一个组件,包括使用
ImportSelector
属性来自定义需要导入的组件的全类名数组;还有使用ImportBeanDefinitionRegistrar
可以手动注册bean到容器中。
八、@FactoryBean注册组件
使用Spring提供的FactoryBean
来注册组件
public class PersonFactoryBean implements FactoryBean<Person>{
/**
*
* @return 返回一个Person对象,对象会添加到容器中
* @throws Exception
*/
public Person getObject() throws Exception {
System.out.println("PersonFactoryBean...getObject");
return new Person();
}
/**
*
* @return 返回对象的类型
*/
public Class<?> getObjectType() {
return Person.class;
}
/**
*
* @return 是否是单实例
*/
public boolean isSingleton() {
return true;
}
}
配置类中加载PersonFactoryBean
到容器中
@Bean
public PersonFactoryBean personFactoryBean(){
return new PersonFactoryBean();
}
测试
@Test
public void testFactoryBean(){
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String beanName : beanDefinitionNames){
System.out.println(beanName);
}
//工厂bean实际上获取的是调用getObject方法创建的对象
Object personFactoryBean = context.getBean("personFactoryBean");
System.out.println("personFactoryBean的类型: " + personFactoryBean.getClass());
//使用 & 符号可以获取工厂bean类型
Object bean = context.getBean("&personFactoryBean");
System.out.println("personFactoryBean的类型: " + bean.getClass());
}
输出结果: