springboot ImportBeanDefinitionRegistrar注入bean

简介

  • ImportBeanDefinitionRegistrar类只能通过其他类@Import的方式来加载,通常是启动类或配置类
  • 使用@Import,如果括号中的类是ImportBeanDefinitionRegistrar的实现类,则会调用接口方法,将其中要注册的类注册成bean。

使用自定义注解, 将bean注入到springboot容器中

项目结构

项目结构

类代码

自定义注解
/**
 * 自定义注解, 作用同spring @service注解
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyService {
    
    
}
需要被注入的类
/**
 * 被注入的类
 */
@MyService
public class UserService {

    public void print(){
        System.out.println("userService对象注入成功");
    }
}
启动类
@SpringBootApplication
@EnableAsync
@EnableScheduling
//在启动类上, 标记@Import注解, MyImportBeanDefinitionRegistrar类是ImportBeanDefinitionRegistrar子类
@Import({MyImportBeanDefinitionRegistrar.class})
public class Application {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
        UserService bean = context.getBean(UserService.class);
        bean.print();
    }
}

在启动类上标记@Import注解, 将MyImportBeanDefinitionRegistrar类设置为属性, 项目启动时, 会执行MyImportBeanDefinitionRegistrar类中的registerBeanDefinitions()方法

MyImportBeanDefinitionRegistrar
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        //自定义的扫描类MyClassPathBeanDefinitionScanner, 实现了ClassPathBeanDefinitionScanner接口
        // 当前MyClassPathBeanDefinitionScanner已被修改为扫描带有指定注解的类
        MyClassPathBeanDefinitionScanner scanner = new MyClassPathBeanDefinitionScanner(registry, false);
//        scanner.setResourceLoader(resourceLoader);
        scanner.registerFilters();
        scanner.doScan("com.hctrl");
    }
}

在该类中, 自定义了一个扫描器MyClassPathBeanDefinitionScanner, 通过给扫描器设置扫描的注解, 扫描的路径, 就会将该路径下的标有扫描注解的类进行注入

MyClassPathBeanDefinitionScanner
public class MyClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {

    public MyClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
        super(registry, useDefaultFilters);
    }

    /**
     * @addIncludeFilter 将自定义的注解添加到扫描任务中
     * @addExcludeFilter 将带有自定义注解的类 ,不加载到容器中
     */
    protected void registerFilters () {
        /**
         *  addIncludeFilter  满足任意includeFilters会被加载
         *  注入@MyService注解标记的类
         */
        addIncludeFilter(new AnnotationTypeFilter(MyService.class));
        /**
         *  addExcludeFilter 同样的满足任意excludeFilters不会被加载
         *  可以配置过滤
         */
        // addExcludeFilter(new AnnotationTypeFilter(MyService.class));
    }

    /**
     * 重写类扫描包路径加载器,调用父类受保护的扫描方法 doScan
     * @param basePackages
     * @return
     */
    @Override
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        return super.doScan(basePackages);
    }
}

在自定义的扫描器中, 配置的扫描路基和注解
MyClassPathBeanDefinitionScanner类继承了spring提供的ClassPathBeanDefinitionScanner类, 顾名思义, 这个类就是用来扫描类, 将类转化为beanDefinition对象的
让我们进入MyImportBeanDefinitionRegistrar类的doScan方法进行源码追踪

	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		for (String basePackage : basePackages) {
		    // 进入findCandidateComponents方法
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}

在这里插入图片描述
ScannedGenericBeanDefinition对象是BeanDefinition接口的子类, 里面包含大量构建bean的属性
在这里插入图片描述

  private volatile Object beanClass;//bean的类型
    private String scope;//bean的作用范围
    private boolean abstractFlag;//是否是抽象类
    private boolean lazyInit;//是否延迟加载
    private int autowireMode; //自动注入模式
    private int dependencyCheck;//依赖检查,spring 3.0后弃用
    private String[] dependsOn; //表示这个bean,依赖于其它beans先实例化
    private boolean autowireCandidate;//设为false,则容器在自动装配时,将不考虑该bean(即这个bean不会作为其它bean自动装配的候选者),但是这个bean还可以自动装配其它bean
    private boolean primary;//自动装配出现多个bean时,将它作为首选者
    private final Map<String, AutowireCandidateQualifier> qualifiers;//用于记录Qualifier
    private boolean nonPublicAccessAllowed;//允许访问非公开的构造器和方法
    private boolean lenientConstructorResolution;//是否以宽松的模式解析构造函数,默认为true
    private String factoryBeanName;//指定工厂类和工厂方法
    private String factoryMethodName;
    private ConstructorArgumentValues constructorArgumentValues;//记录构造函数注入属性
    private MutablePropertyValues propertyValues;//普通属性集合
    private MethodOverrides methodOverrides; //方法重写的持有者
    private String initMethodName; //初始化方法
    private String destroyMethodName; //销毁方法
    private boolean enforceInitMethod; //是否执行初始化方法
    private boolean enforceDestroyMethod;//是否执行销毁方法
    private boolean synthetic;//是否是用户定义的而不是应用程序本身定义的,创建AOP时为true
    private int role;//定义这个bean的使用,application:用户,infrastructure:完全内部使用,与用户无关;support:某些复杂配置的一部分
    private String description;//bean的描述
    private Resource resource;//这个bean定义的资源。

beanDefinition创建方法

在这里插入图片描述

@Component注解类注入原理

ClassPathBeanDefinitionScanner是springboot提供的, 它默认会扫描和启动类同级目录下的所有文件下的类, 如果类上标记了@Component注解(或者@Component是他们元注解, 比如@Service, @Controller等)就会被注入到容器中
源码解析 :
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
至此, @Component标记的类全部注入完成, 只有就是对beanDefinition对象赋默认值

mybatis 通过ImportBeanDefinitionRegistrar将所有的Mapper对象注入到容器中

前提

创建一个项目, pom.xml包含

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>

首先查看mybatis的自动装配
在这里插入图片描述
在这里插入图片描述
进入到MapperScannerConfigurer类中
在这里插入图片描述
可以看到, 该类实现了BeanDefinitionRegistryPostProcessor接口, 项目启动时会执行postProcessBeanDefinitionRegistry()方法
在这里插入图片描述

可以看出, 该方法的主要作用就是创建一个ClassPathMapperScanner扫描器, 用来扫描@Mapper注解
可以推断, ClassPathMapperScanner应该是ClassPathBeanDefinitionScanner的子类
在这里插入图片描述
之后在类启动时, ClassPathMapperScanner扫描器就会将指定路径下的, 所有标记@Mapper注解的类注入到容器中

这就是Mybatis对象注入的全部流程

== 问题 : @Mapper注解都是标记在接口上, 那么接口什么时候生成的代理对象呢? ==

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值