@Configuration&@Bean给容器中注册组件
pojo:
@Data
public class Person {
private String id;
private String name;
private Integer age;
public Person() {
}
public Person(String id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
}
使用xml配置:
<bean id="person" class="com.lly.pojo.Person">
<property name="id" value="2"></property>
<property name="name" value="lisi"></property>
<property name="age" value="18"></property>
</bean>
使用注解开发:
@Configuration // 告诉Spring这是一个配置类
public class PojoConfig {
@Bean("xxx") //指定这个组件的id为xxx,不指定默认为方法名
public Person person() { //返回值类型为bean组件类型
return new Person("1","zs",1);
}
}
@ComponentScan自动扫描组件&指定规则
xml配置文件扫描组件配置:
<!--扫描所有组件,只要标注了@Controller、@Service、@Repository、@Component-->-->
<context:component-scan base-package="com.lly" use-default-filters="false"/>
//use-default-filters="false"是为了在使用includeFilters(指定扫描的时候只需要包含哪些组件)的时候
取消默认的扫描规则,否则includeFilters会不生效
注解驱动:
@Configuration
@ComponentScan(value = "com.lly",includeFilters = {@ComponentScan.Filter(
type = FilterType.ANNOTATION,classes = Controller.class)},
useDefaultFilters = false)
public class Config {
@Bean
public Person person(){
return new Person("1","zs",18);
}
}
@ComponentScan
value: 指定要扫描的包
@excludeFilters = Filter[] : 指定扫描的时候按照什么规则排除组件(排除哪些类型的组件)
type: 指定排除规则
ANNOTATION,按注解,默认按注解
ASSIGNABLE_TYPE,按给定的类型
ASPECTJ,使用ASPECTJ表达式
REGEX,正则表达式
CUSTOM;自定义规则
classes: 指定排除组件类型
@includeFilters = Filter[] : 指定扫描的时候只需要包含哪些组件,使用这个要禁用掉默认的扫描规则
useDefaultFilters = false
@ComponentScans
这个可以理解为是@ComponentScan的复数形式,里面放的是@ComponentScan数组
即@ComponentScans(@ComponentScan(),@ComponentScan()……)
自定义TypeFilter指定过滤规则
@ComponentScan指定扫描规则的时候,还可以指定自定义类型的规则。而自定义类型规则的实现需要实现TypeFilter接口,在match()方法中写对应的规则
public class MyTypeFilter implements TypeFilter {
/***
*
* @param metadataReader 读取当前正在扫描的类信息
* @param metadataReaderFactory 获取当前其他任何类任何信息
* @return
* @throws IOException
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//获取当前类注解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取当前正在扫描的类的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//获取当前类的资源信息(类路径等信息,在哪个盘等)
Resource resource = metadataReader.getResource();
……
//根据实际需要,编写扫描规则,返回true/false
return false;
}
}
@Scope设置组件的作用域范围
在bean创建的时候,无论我们获取多少次,bean都是同一个对象,因为bean的创建是单实例的,如果需要改为多实例,可以使用@Scope配置
【默认单实例】
/***
* ConfigurableBeanFactory#SCOPE_PROTOTYPE prototype
* ConfigurableBeanFactory#SCOPE_SINGLETON singleton
* org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST request【不常用】
* org.springframework.web.context.WebApplicationContext#SCOPE_SESSION session【不常用】
*
* prototype: 多实例 - ioc容器启动并不创建对象放在容器中
* 而是每次获取的时候才会调用方法创建对象
* singleton: 单实例 【默认值】 - ioc容器启动会调用方法创建对象放进ioc容器中
* 以后每次获取直接从容器(map.get)中拿
* request: 同一次请求创建一个实例
* session: 同一个session创建一个实例
*
*
* @Scope: 调整该组件的作用域范围
*/
@Scope(value = "singleton")
@Lazy设置bean懒加载
- 懒加载:—> 针对单实例
单实例bean: 默认在容器启动的时候创建对象
懒加载: 容器启动后不创建对象。第一次使用(获取)bean时才创建对象,并初始化
@Conditional按条件注册bean
这个注解在看SpringBoot源码的时候会看见的比较多,某些组件在什么样的情况下才进行注册等等操作。
想要实现自己的条件也是可以的,只要实现Condition接口就行,在实现类上编写自己的逻辑
@Conditional()注解里放的就是Conditional 的实现类
public @interface Conditional {
Class<? extends Condition>[] value();
}
@Import给容器快速导入组件
@Import(value = {Book.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
public class Config {
//一般情况下,我们要注册一个组件可能会像下面这样写一个@Bean。
//但其实不用那么麻烦,只要在一个配置类上加入@Import注解,里面写上要注册组件的类型即可,
//如上面的第一个
@Bean
public Person person(){
return new Person("1","zs",18);
}
}
@Import-使用ImportSelector
在上面的例子中,第二个就是使用了ImportSelector。
ImportSelector的实现要实现ImportSelector接口。
public class MyImportSelector implements ImportSelector {
//返回值,就是要导入容器中的组件全类名
//AnnotationMetadata: 当前标注@Import注解的类(就是这个配置类啦)的所有注解信息
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//返回一个String类型数组,数组里的就是你要注册的组件的全类名
return new String[]{"com.lly.pojo.Color"};
}
}
@Import-使用ImportBeanDefinitionRegistrar(5.1.6 ,5.3开始貌似有新增)
例子中的第三个ImportBeanDefinitionRegistrar来注册组件。这个方法应该可以说和底层源码是有关联的。
在之前的Spring源码里面,可以知道组件的注册在开始时就是会调用BeanDefinitionRegistrar来注册的。
要使用ImportBeanDefinitionRegistrar注册组件,就要实现ImportBeanDefinitionRegistrar接口。有两个实现方法,看你需要实现哪一个。
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/***
* @param importingClassMetadata 当前类的注解信息
* @param registry BeanDefinition注册类 :
* 把所有要添加到容器中的bean通过调用 BeanDefinitionRegistry.registerBeanDefinition 手工注册
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//可以写自己需要的业务逻辑
boolean b = registry.containsBeanDefinition("com.lly.pojo.Book");
if (b) {
//这个方法和底层组件注册的调用差不多,参数一是你可以自定义组件的名字
//参数二要一个BeanDefinition类型,BeanDefinition是一个接口,其底下有许多实现类,
//在new一个实现类的时候要传入你要注册的组件的全类名
registry.registerBeanDefinition("Author", new RootBeanDefinition("com.lly.pojo.Author"));
}
}
}
registerBeanDefinition入参类型:
registerBeanDefinition实现类:
使用FactoryBean注册组件
使用FactoryBean注册组件要实现FactoryBean接口,接口传的泛型类型就是你要注册组件的类型
//创建一个Spring定义的工厂Bean
public class MyFactoryBean implements FactoryBean<Book> {
//返回一个Color对象,这个对象会添加到容器中
@Override
public Book getObject() throws Exception {
return new Book();
}
@Override
public Class<?> getObjectType() {
return Book.class;
}
//是单例吗?
// true:单实例,在容器中仅保留一份
// false:多实例,每次获取都会创建一个新的
@Override
public boolean isSingleton() {
return true;
}
}
将FactoryBean的实现类加入到配置类中即可
@Bean
public MyFactoryBean myFactoryBean() {
return new MyFactoryBean();
}
在获取FactoryBean的实现类的类型时,如果直接获取,得到的是你要注册组件的类型,因为会调用FactoryBean的实现类的getObject()方法。
但如果加入了前缀&。就能得到原本的工厂类型,这是因为BeanFactory接口中规定了FactoryBean的前缀
String FACTORY_BEAN_PREFIX = "&";
Object bean1 = context.getBean("myFactoryBean");
System.out.println(bean1.getClass());
Object bean2 = context.getBean("&myFactoryBean");
System.out.println(bean2.getClass());
-----------------------输出----------------
class com.lly.pojo.Book
class com.lly.Config.MyFactoryBean