springboot常用注解初识

@springBootConfiguration

主配置类的注解,将标注类所在包及其子包下所有的组件扫描到spring容器

如果想改变扫描路径:scanBasePackages=“com.lun”

@ConditionalOnMissingBean(name="tom")

没有tom名字的Bean时,Bean才能生效

@ImportResource

引入配置文件

@ConfigurationProperties

@ConfigurationProperties + @Component

只有在容器中的组件,功能才生效

@Component
@ConfigurationProperties(prefix = "mycar")
public class Car {
    private String branch;
    private String price;
}
# application.yaml
server:
  port: 8888
mycar:
  branch: "雅阁"
  price: "20w"
@RestController
public class myController {
    @Autowired
    Car car;
    @GetMapping("/car")
    public Car getMyCar(){
        return car;
    }
}

@EnableConfigurationProperties

@EnableConfigurationProperties(Car.class) + @ConfigurationProperties

开启car配置的绑定功能;将car添加到spring容器(所以使用时,需要将car注入使用类中-构造器注入或者set注入)

@EnableConfigurationProperties(Car.class)
public class MyConfig {
@ConfigurationProperties(prefix = "mycar")
public class Car {
    private String branch;
    private String price;
}

@ConditionalOnProperty

通过配置文件的属性值来判定configuration/bean是否被注入

内部实现:@Conditionl(OnPropertyCondition.class)

拓展 - SpringBoot读取配置的其他方式

1、通过Environment注入获取配置

 2、通过@PropertySource(value = "db/properties")读取配置文件

@AutoConfigureAfter(A.class)

表示该自动配置类需要在自动配置类A配置完成之后

@Import

注入bean

1、引入其他@Configureration+@Bean,优先于本身的类定义加载

2、指定其他普通的类,优先于本身的类定义加载

3、指定实现ImportSelector,优先于本身的类定义加载

4、指定实现DeferredImportSelector,这样selectImports方法返回的类是最后加载

5、

参考博文: Spring全解系列 - @Import注解 - 知乎

@SpringBootConfiguration 

  

 

@AutoConfigurationPackage:让包中的类以及子包中的类能够被自动扫描到spring容器中。其实就是将主配置类(即@SpringBootApplication标注的类)的所在包及子包里面所有组件扫描加载到Spring容器。因此我们要把DemoApplication放在项目的最高级中(最外层目录)。@SpringBootApplication(scanBasePackages = {""}):如果有scanBasePackages ,就不扫描默认得路径。

所以将bean加入spring容器:放在启动类得包路径下面+@component;@ComponentScan(basePackages = {""})+@component;从META-INF/spring.factories中获取资源;@Configuration+@Bean

@Import(AutoConfigurationImportSelector.class),@Import注解就是给Spring容器中导入一些组件,这里传入了一个组件的选择器:AutoConfigurationImportSelector。ImportSelector有一个方法为:selectImports。将所有需要导入的组件以全类名的方式返回,这些组件就会被添加到容器中。

会给容器中导入非常多的自动配置类(xxxAutoConfiguration);就是给容器中导入这个场景需要的所有组件,并配置好这些组件。有了自动配置类,免去了我们手动编写配置注入功能组件等的工作。

会从META-INF/spring.factories中获取资源,然后通过Properties加载资源。可以知道SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作。以前我们需要自己配置的东西,自动配置类都帮我们完成了。

BeanDefinitionRegistry 

该接口主要是对BeanDefintion的增删查改

BeanDefinitionRegistryPostProcessor

 调用时序

该接口的调用时序是非常靠前的,基本上容器初始化的一些操作就是为了调用这个接口而做准备的。有一个特别关键的实现类,就是ConfigurationClassPostProcessor 类,在这里它作为一个普通的实现类去执行,但也是关键的创建bd的代码。

执行时机:在所有bean定义信息将要被加载,bean实例还未创建时;

它优先于BeanFactoryPostProcessor执行,利用BeanDefinitionRegistryPostProcessor给容器中再额外添加一些组件;

原理

1)IOC创建对象;

2)refresh()-->invokeBeanFactoryPostProcessors(BeanFactory);

3)从容器中获取到所有的BeanDefinitionregistryPostProcessor组件。

        3.1)依次触发所有的postProcessBeanDefinitionRegistry()方法;

        3.2)再来触发postProcessBeanFactory ;

4)再来从容器中找到beanFactoryPostProcessor组件,然后依次触发;

spring中我们常用注解配置以及xml文件来管理我们的bean。其实在spring的生命周期中,bean在实例化之前都是无差别的被当做资源加载进来的,并被封装成一个个Beandefinition。在spring启动时,所有bean实例化的注解和xml文件都会被加载进来,并注册成Beandefinition。准备后续的bean实例化。那么在注册成Beandefinition这步,spring其实提供给了我们不少后门进行操作。常见的后置处理器BeanDefinitionRegistryPostProcessor就是一个比较常见的自定义操作bean的接口。BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的子接口,BeanFactoryPostProcessor的作用是在bean的定义信息已经加载但还没有初始化的时候执行方法postProcessBeanFactory()方法,而BeanDefinitionRegistryPostProcessor是在BeanFactoryPostProcessor的前面执。

模拟springBoot调用

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnnotationTest.class); 

Spring后置处理器

BeanPostProcessor:bean级别的处理,针对某个具体的bean进行处理。接口提供了两个方法,分别是初始化前和初始化后执行方法。

需要注意一点,我们定义一个类实现了BeanPostProcessor,默认是会对整个Spring容器中所有的bean进行处理。

BeanFactoryPostProcessor:BeanFactory级别的处理,是针对整个Bean的工厂进行处理。

此接口只提供了一个方法,方法参数为ConfigurableListableBeanFactory 。

 其中有个方法名为getBeanDefinition的方法,我们可以根据此方法,找到我们定义bean的BeanDefinition对象。然后我们可以对定义的属性进行修改。

@ComponentScan

会将包下@Component注解的类注册进Spring。(通过ConfigurationClassPostProcessor

) 。@Service、@Controller、@Configuration、@Repository也会,因为有@Component注解。

Spring中最!最!最!重要的后置处理器!没有之一!!! - 知乎 (zhihu.com)

SpringBoot中@Import注解的使用方式_java_脚本之家 (jb51.net)

AutowiredAnnotationBeanPostProcessor

这个bean的后置处理器用来处理@Autowired、@Value的注入  

在 AutowiredAnnotationBeanPostProcessor初始化的时, autowiredAnnotationTypes 中添加了三个注解@Autowired@Value、 和通过反射得到的 javax.inject.Inject

getConstructors:只返回公共构造函数

getDeclaredConstructor:返回所有的构造函数 

 Candidate 候选

determineCandidateConstructor:方法是 SmartInstantiationAwareBeanPostProcessor

 接口中的方法。其作用是从 注入bean的所有构造函数中过滤出可以作为构造注入的构造函数列表

1、构造器注入@AutoWired(required=true)有且只能有一个,同时不可有required=false的

2、可以有多个@AutoWired(required=false)修饰的构造函数(如果有无参构造方法,也会一起返回)

3、如果当前没有@Autowired注解,且类里只有一个public有参构造函数,且没有@AutoWired注解,就会返回这个

4、上面都不符合,返回null:比如只有个默认无参构造函数

CommonAnnotationBeanPostProcessor

用来处理如@Resource,@PostConstruct等符合JSR-250规范的注解

refresh()方法

是整个Spring容器的核心,在这个方法中进行了bean的实例化、初始化、自动装配、AOP等功能。

 public void refresh() throws BeansException, IllegalStateException {
2    synchronized (this.startupShutdownMonitor) {
3        // Prepare this context for refreshing.
4        // 初始化属性配置文件、检验必须属性以及监听器
5        prepareRefresh();
6        // Tell the subclass to refresh the internal bean factory.
7        // 给beanFactory设置序列化id
8        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
9        // 向beanFactory中注册了两个BeanPostProcessor,以及三个和环境相关的bean
10        // 这两个后置处理器为ApplicationContextAwareProcessor和ApplicationListenerDetector
11        // 前一个后置处理是为实现了ApplicationContextAware接口的类,回调setApplicationContext()方法,
12        // 后一个处理器时用来检测ApplicationListener类的,当某个Bean实现了ApplicationListener接口的bean被创建好后,会被加入到监听器列表中

13        prepareBeanFactory(beanFactory);
14        try {
15            // Allows post-processing of the bean factory in context subclasses.
16            // 空方法,由子类实现
17            postProcessBeanFactory(beanFactory);
18            // 执行所有的BeanFactoryPostProcessor,包括自定义的,以及spring内置的。默认情况下,容器中只有一个BeanFactoryPostProcessor,即:Spring内置的,ConfigurationClassPostProcessor(这个类很重要)
19            // 会先执行实现了BeanDefinitionRegistryPostProcessor接口的类,然后执行BeanFactoryPostProcessor的类
20            // ConfigurationClassPostProcessor类的postProcessorBeanFactory()方法进行了@Configuration类的解析,@ComponentScan的扫描,以及@Import注解的处理
21            // 经过这一步以后,会将所有交由spring管理的bean所对应的BeanDefinition放入到beanFactory的beanDefinitionMap中
22            // 同时ConfigurationClassPostProcessor类的postProcessorBeanFactory()方法执行完后,向容器中添加了一个后置处理器————ImportAwareBeanPostProcessor

23            invokeBeanFactoryPostProcessors(beanFactory);
24            // 注册所有的BeanPostProcessor,因为在方法里面调用了getBean()方法,所以在这一步,实际上已经将所有的BeanPostProcessor实例化了
25            // 为什么要在这一步就将BeanPostProcessor实例化呢?因为后面要实例化bean,而BeanPostProcessor是用来干预bean的创建过程的,所以必须在bean实例化之前就实例化所有的BeanPostProcessor(包括开发人员自己定义的)
26            // 最后再重新注册了ApplicationListenerDetector,这样做的目的是为了将ApplicationListenerDetector放入到后置处理器的最末端

27            registerBeanPostProcessors(beanFactory);
28            // Initialize message source for this context.
29           // 初始化MessageSource,用来做消息国际化。在一般项目中不会用到消息国际化
30            initMessageSource();
31            // Initialize event multicaster for this context.
32            // 初始化事件广播器,如果容器中存在了名字为applicationEventMulticaster的广播器,则使用该广播器
33            // 如果没有,则初始化一个SimpleApplicationEventMulticaster
34            // 事件广播器的用途是,发布事件,并且为所发布的时间找到对应的事件监听器。
35            initApplicationEventMulticaster();
36
37            // Initialize other special beans in specific context subclasses.
38            // 执行其他的初始化操作,例如和SpringMVC整合时,需要初始化一些其他的bean,但是对于纯spring工程来说,onFresh方法是一个空方法
39            onRefresh();
40
41            // Check for listener beans and register them.
42            // 这一步会将自定义的listener的bean名称放入到事件广播器中
43            // 同时还会将早期的ApplicationEvent发布(对于单独的spring工程来说,在此时不会有任何ApplicationEvent发布,但是和springMVC整合时,springMVC会执行onRefresh()方法,在这里会发布事件)
44            registerListeners();
45            // 实例化剩余的非懒加载的单例bean(注意:剩余、非懒加载、单例)
46            // 为什么说是剩余呢?如果开发人员自定义了BeanPosrProcessor,而BeanPostProcessor在前面已经实例化了,所以在这里不会再实例化,因此这里使用剩余一词

47            finishBeanFactoryInitialization(beanFactory);
48            // 结束refresh,主要干了一件事,就是发布一个事件ContextRefreshEvent,通知大家spring容器refresh结束了。
49            finishRefresh();
50        }
51        catch (BeansException ex) {
52            // 出异常后销毁bean
53            destroyBeans();
54            // Reset 'active' flag.
55            cancelRefresh(ex);
56            // Propagate exception to caller.
57            throw ex;
58        }
59        finally {
60           // 在bean的实例化过程中,会缓存很多信息,例如bean的注解信息,但是当单例bean实例化完成后,这些缓存信息已经不会再使用了,所以可以释放这些内存资源了
61            resetCommonCaches();
62        }
63    }
64}

默认使用的ApplicationContext

AnnotationConfigServletWebServerApplicationContext。构造函数中会创建 AnnotatedBeanDefinitionReader。而在 AnnotatedBeanDefinitionReader 构造函数中会调用 AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);,该方法将一些必要Bean(如ConfigurationClassPostProcessorAutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor 等)注入到了容器中。

 GenericApplicationContext中有初始化的BeanFactory

这个类里,BeanDefinitionRegistry对BeanDefinition的增删改查,其实是在父类GenericApplicationContext,父类其实是调用的DefaultListableBeanFactory。

InstantiationAwareBeanPostProcessor

// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.

接口的主要作用在于目标对象的实例化过程中需要处理的事情,包括实例化对象的前后过程以及实例的属性设置 。

postProcessBeforeInstantiation方法是最先执行的方法,它在目标对象实例化之前调用,该方法的返回值类型是Object,我们可以返回任何类型的值。由于这个时候目标对象还未实例化,所以这个返回值可以用来代替原本该生成的目标对象的实例(比如代理对象)。如果该方法的返回值代替原本该生成的目标对象,后续只有postProcessAfterInitialization方法会调用,其它方法不再调用(创建对象结束);否则按照正常的流程走。

postProcessAfterInstantiation方法在目标对象实例化之后调用,这个时候对象已经被实例化,但是该实例的属性还未被设置,都是null。默认返回true,如果返回false,则不需要进行后面的依赖注入。

AnnotatedBeanDefinition

AnnotatedBeanDefinition 是 BeanDefinition 子接口之一,该接口扩展了 BeanDefinition 的功能,其用来操作注解元数据。一般情况下,通过注解方式得到的 Bean(@Component、@Bean) 

该接口可以返回两个元数据的类:

  • AnnotationMetadata:主要对 Bean 的注解信息进行操作,如:获取当前 Bean 标注的所有注解、判断是否包含指定注解。

  • MethodMetadata:方法的元数据类。提供获取方法名称、此方法所属类的全类名、是否是抽象方法、判断是否是静态方法、判断是否是final方法等。

ScannedGenericBeanDefinition

该类继承自 GenericBeanDefinition ,并实现了 AnnotatedBeanDefinition 接口。这个 BeanDefinition 用来描述标注 @Component 注解的 Bean,其派生注解如 @Service、@Controller 也同理。

ConfigurationClassBeanDefinition

该类继承自 RootBeanDefinition ,并实现了 AnnotatedBeanDefinition 接口。这个 BeanDefinition 用来描述在标注 @Configuration 注解的类中,通过 @Bean 注解实例化的 Bean。

其功能特点如下:

1、如果 @Bean 注解没有指定 Bean 的名字,默认会用方法的名字命名 Bean。

2、标注 @Configuration 注解的类会成为一个工厂类,而标注 @Bean 注解的方法会成为工厂方法,通过工厂方法实例化 Bean,而不是直接通过构造方法初始化。

3、标注 @Bean 注解的类会使用构造方法自动装配??

此bean会有如下重要属性:

通过factory-method属性创建对象

一种静态(方法必须静态),一种动态。

   <!--生成对象的工厂-->
    <bean id="stuFactory" class="com.dragon.study.study20190618.spring.springFactoryMethod.StuFactory"/>
    <!--动态获取对象-->
    <bean id="dynamicStu" factory-bean="stuFactory" factory-method="getDynamicStu">
        <!--传入getDynamicStu方法的参数-->
        <constructor-arg value="11"/>
    </bean>
public class StuFactory {
    static Map<String,Stu> stuMap = new HashMap<>();
    static{
        //初始化
        Stream.iterate(1,n->n+1).limit(5).map(String::valueOf).forEach(t-> stuMap.put(t, new Stu(t)));
    }

    //动态创建类
    public Stu getDynamicStu(String stuId){
        return new Stu(stuId);
    }
}

Spring(四)核心容器 - BeanDefinition 解析 - 龙四丶 - 博客园

 

springboot的启动类,就是放在AnnotatedGenericBeanDefinition,内含BeanClass和metadata。在main方法->SpringApplication.run(MultipleApplication.class, args) -> prepareContext -> Set<Object> sources = getAllSources() -> load(context, sources.toArray(new Object[0])) -> 注册启动类的AnnotatedGenericBeanDefinition。

启动类,必须加上@Configuration--好像也不一定。。。

ConfigurationClassPostProcessor

1、首先,获取启动类的BeanDefinition,找到启动类上的ComponentScan注解,扫描basePackage下的@component标注的类,生成ScannedGenericBeanDefinition。遍历扫描到的bean,其实是递归遍历扫描到的类,有没有被@ComponentScan、@Import等注解标注。再扫描启动类上有没有加@Import注解,找到@Import注解里的类,实现ImportBeanDefinitionRegistrar接口的,就会生成ImportBeanDefinitionRegistrar对象,实现ImportSelector接口的会先生成ImportSelector对象,再执行它的selectImports方法,遍历方法返回的类,同样递归遍历,看这些类有没有被@ComponentScan、@Import等注解标注。或者@Import里面的是个普通类,同样看这些类有没有被@ComponentScan、@Import等注解标注、里面方法有没有被@Bean注解标注。再查看主启动类上方法有没有被@Bean注解标注。

parser.parse(candidates):通过@ComponentScan注解扫描的类才会加入到BeanDefinitionMap中/ 通过其他注解(例如@Import、@Bean)的方式,在parse()方法这一步并不会将其解析为BeanDefinition放入到BeanDefinitionMap中,而是先解析成ConfigurationClass类.真正放入到map中是在下面的this.reader.loadBeanDefinitions()方法中实现的

createBeanInstance

1、如果RootBeanDefinition 中存在 factoryMethodName 属性,或者在配置文件中配置了factory-method,Spring会尝试使用 instantiateUsingFactoryMethod 方法,根据RootBeanDefinition 中的配置生成bean实例。需要注意的是,如果一个类中中的方法被 @Bean注解修饰,那么Spring则会将其封装成一个 ConfigurationClassBeanDefinition。此时 factoryMethodName 也被赋值。所以也会调用instantiateUsingFactoryMethod 方法通过反射完成方法的调用,并将结果注入Spring容器中。

  2、构造函数筛选

	// 调用 SmartInstantiationAwareBeanPostProcessor#determineCandidateConstructors 方法来筛选构造函数。
	Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
	// 有候选构造函数 || 构造方式为构造注入 || 有构造函数入参 || 用于构造函数或工厂方法调用的显式参数args 不为null
	// 则调用 autowireConstructor 方法
	if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
			mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
		return autowireConstructor(beanName, mbd, ctors, args);
	}

	// Preferred constructors for default construction?
	// 如果有 确定用于默认构造的首选构造函数(如果有)。如有必要,构造函数参数将自动装配。
	// RootBeanDefinition 的实现是 null 。这里仅在 GenericApplicationContext.ClassDerivedBeanDefinition 中有过解析
	ctors = mbd.getPreferredConstructors();
	if (ctors != null) {
		return autowireConstructor(beanName, mbd, ctors, null);
	}
	// No special handling: simply use no-arg constructor.
	// 使用默认方式进行bean 构造
	return instantiateBean(beanName, mbd);

ctors != null : determineConstructorsFromBeanPostProcessors 方法筛选出的候选构造函数不为null。对于默认的Spring来说,实际返回的是 AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors 方法的返回值。而其作用简单来说就是返回被 @Autowired 注解修饰的构造函数。

mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR :这里的配置是通过xml 来配置bean时指定的装配方式。

mbd.hasConstructorArgumentValues() :这里表示是否给当前bean定义了构造函数入参。通过xml配置bean的时候可以通过 <constructor-arg> 标签指定构造函数入参。

属性注入

Spring 默认是通过 AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues 的实现来完成的属性的注入。AutowiredAnnotationBeanPostProcessor 中完成了@Autowired、@Value 注解的自动注入功能。大概逻辑是,获取被 @Autowired 修饰的 属性或者方法,如果是属性,则通过getBean 获取bean并注入,如果是方法,则获取方法参数后,invoke 方法(调用该方法,因为我们一般写的都是set方法,给属性注入赋值)。

Bean的初始化

 顺序:PostConstruct(在CommonAnnotationBeanPostProcessor实现)>实现InitializingBean接口>调用 init-method 指定的方法

SpringBoot启动时自动执行的方式:

实现接口CommandLineRunnerApplicationRunner接口时,可以通过在类上添加@Order注解来设定运行顺序。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值