Administrator
发布于 2022-12-07 / 44 阅读
0
0

Spring注解驱动---组件注册

@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入参类型:

image-1670500586502

registerBeanDefinition实现类:

image-1670499727689

使用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

评论