我们想将一个类注册到IOC容器中首先要有个配置,一般有基于xml配置文件的方式和注解的方式。
已知Person类
package com.tan.bean;
public class Person {
private String name;
private Integer age;
}
1、xml配置的方式(配置文件为:bean.xml)
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对象,这个对象会添加到容器中
* @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中大量使用了这种方式。
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);
}