不要再说不会Spring了!Spring第一天,学会进大厂!

Spring第二天,你必须知道容器注册组件的几种方式!学废它吊打面试官!

Spring第三天,详解Bean的生命周期,学会后让面试官无话可说!

今天讲解Spring底层对BeanPostProcessor的使用。

背景:BeanPostProcessor是什么?有什么用?

Spring所有对Bean的处理,BeanPostProcessor接口都贯穿其中,都离不开该接口。BeanPostProcessor接口可以管理bean工厂内所有beandefinition(未实例化)数据,可任意修改属性。

首先来看一下有哪些类实现了BeanPostProcessor接口

可以从上图看出,有这么多的接口及类都实现了BeanPostProcessor接口,今天重点来讲解ApplicationContextAwareProcessorBeanValidationPostProcessor类InitDestoryAnnotationBeanPostProcessor类的原理。其中ApplicationContextAwareProcessor类是为一些Aware类型的接口,注入对应的属性值。此类帮我们组件IOC容器,是一个后置处理器,跟进ApplicationContextAwareProcessor类我们发现,这个后置处理器其实就是判断我们的bean有没有实现ApplicationContextArea接口,并处理相应的逻辑,所有的后置处理器的原理都是如此。

(其他的类和接口在这里暂且不讨论,重点关注ApplicationContextAwareProcessor类是如何帮助组件IOC容器的。ps:单词都很长,一定要注意区分对应的类和接口)

一、ApplicationContextAwareProcessor实现分析

问题:ApplicationContextAwareProcessor如何帮助我们组件IOC容器呢?

回答:只需要实现ApplicationContextAware接口即可。

定义实体类Plane.java类

我们再来分析一下ApplicationContextAwareProcessor类的源码方法

1. 在创建Plane对象,并且还没有初始化之前,先判断是否是实现了ApplicationContextAware接口,如果实现了的话,就调用invokeAwareInterfaces()方法,并给里面注入值。

2. 接下来我们进入invokeAreaInterfaces()方法,判断是哪一个aware,如果是ApplicationContextAware,就将当前的bean转成ApplicationContextAware类型,调用setApplicationContext(),把plane注入到IOC容器中去。

3. 通过debug调用,测试用例打断点来调试:

4. 也可以用debug调用栈来分析。

ps:debug可以打在ApplicationContextAwareProcessor处理器类的PostProcessorsBeforeInitialization()方法里,便于调试,当bean为plane类型时,F5跟进看,最终在InvokeAwareInterfaces()方法里返回我们的IOC容器applicationContext。

二、BeanValidationPostProcess实现分析

该类主要用于数据校验,当创建完对象,给bean赋值后,在Web用的特别多;把页面提交的值进行校验。

通过源码可以从BeanValidationPostProcess类中可以看到87和95行,有postProcessBeforeInitialization() 和 postProcessAfterInitialization() 方法,这两个方法分别在bean初始化之前 和 初始化之后进行校验。

三、InitDestoryAnnotationBeanPostProcessor实现分析

此类用来处理JSR250(JDK提供)规范中@PostConstruct 和 @PreDestory,怎么知道这两注解是前后开始调用的呢?

定义实体类Jeep.java类

以@PostConstruct为例,为什么声明了这个注解之后,就可以找到初始化init方法呢?

继续执行,执行到invokeInitMethods()方法中可以看到,利用反射调用了该方法

从上面的debug调试可以得知,InitDestoryAnnotationBeanPostProcessor来处理了这两个注解是前后开始调用的。

接下来,我们来了解一下@Autowired自动装配

四、@Autowired自动装配

什么是自动装配?自动装配:Spring利用依赖注入(DI),完成对IOC容器中的各个组件的依赖关系赋值。

1. 新建TestController.java 、TestService.java 、 TestDao.java 类分别建立在指定包内,通过配置类,将所有这些Java对象扫描后保存在IOC容器中管理。

2. 新建配置类,扫描并将以上bean都扫描并加载到容器中

3. 针对以上类建立完成后,在TestService.java中,使用@Autowired注入,并把testDao打印出来

4. 新建测试类,比较TestService 拿到testDao与直接从容器拿到的testDao是否为同一个?

结果很明显,打印出来是同一个testDao,地址一样

小结:

@Autowired表示默认优先按类型去容器中找对应的组件,相当于anno.getBean(TestDao.class)去容器中获取id为testDao的bean,并注入到TestService的bean中;

使用方式如下:

public class TestService {  // 默认去容器中找testDao的bean  @Autowired  private TestDao testDao;}

5. 注意事项

5.1 如果容器中找到多个testDao,会加载哪一个testDao呢?

操作步骤

在配置类声明@Bean("testDao2")

并将TestDao加入flag属性和get、set以及toString()方法,用来分辨加载了哪个bean。

如何区分TestService是使用了(@Reponstry的testDao的flag=1)的bean还是(testDao2的flag=2)的bean?

测试步骤如下:

1. 直接使用@Autowired, 将testDao注入到TestService

bean id为testDao,根据id默认去找@Reponsitory注解的testDao

测试结果为:

service...testDao...TestDao [flag=1]

2. 如果一定要使用容器中的testDao2呢?操作如下:

将bean id指向testDao2即可

测试结果为:

service...testDao...TestDao [flag=2]

3. 虽然以上定义了private TestDao testDao2, 但还是想加载bean id为testDao(flag=1)的bean,怎么办?此时可以使用@Autowired和@Qualifier结合来指定注入哪一个bean,

操作如下:

通过使用@Qualifier注解,指定加载testDao

测试结果:

service...testDao...TestDao [flag=1]

4. 如果容器中没有任何一个testDao, 会出现什么状况呢?

操作如下: 注释掉@Repository和@Bean("testDao2")

此时容器启动时这两个bean都不会加载(因为注解被注释啦.......)

测试结果如下:

很明显报错了, 因为@Autowired注解里的属性默认required=true.必须找到bean

那怎么解决呢?

将TestService中@Autowired中required设置为false,指定为非必须,当容器中无此bean,也不会报错。

测试结果如下:

null

5. @Primary注解指定bean如何加载呢?

(注:将以上原注释掉的@Repository和@Bean("testDao2") 恢复,见下图)

重要:为了验证@Qualifier与@Primary两注解的加载顺序,测试如下:

当对于testDao在容器中同时存在多个时, 且@Qualifier与@Primary注解同时存在,会发生什么呢?

见下操作:  打开@Qualifier与@Primary注解.

测试结果:

TestDao [flag=1]

unit....test....TestDao [flag=2]

此时只能说明一点: @Qualifier是根据bean id指定获取testDao, 不受@Primary影响

那么@Primary的功能在哪呢?继续测试.....

测试结果:

5.2 除了@Autowired, 是不是还用过@Resource(JSR250)  和@Inject(JSR330)

将Qualifier和Autowired注释掉(注意: 此时@Primary 还没注释......)

测试结果:

效果也是一样的, 但它不先优先装配@Primary的bean

小结:@Resource和Autowired的区别如下:

@Resource和Autowired一样可以装配bean

@Resource缺点:

① 不能支持@Primary功能

② 不能支持@Autowired(required = false)的功能

当然也可以在TestService里按以下方式指定要注入的Bean

测试结果:

5.3 @Inject自动装配的使用:

注:@Inject与@Autowired的区别如下:

@Inject和Autowired一样可以装配bean, 并支持@Primary功能, 可用于非spring框架.

@Inject缺点: 但不能支持@Autowired(required = false)的功能,需要引入第三方包javax.inject

操作步骤:

1,pom.xml导入javax.inject包

2,使用@Inject注解

结论:@Inject不支持required=false,  但支持primary

Autowired属于spring的, 不能脱离spring,  @Resource和@Inject都是JAVA规范

推荐大家使用@Autowired

这一章节​比较生涩难懂。可以在下方留言进行一起沟通讨论。

(来自享学IT的​总结)

阅读原文:Spring第四天,BeanPostProcessor源码分析,彻底搞懂IOC注入及注解优先级问题!

明天将会介绍一章重头戏,AOP底层介绍,理论实践​。

关注公众号【Java极客思维】

Spring第四天,BeanPostProcessor源码分析,彻底搞懂IOC注入及注解优先级问题!的更多相关文章

  1. Spring Security(四) —— 核心过滤器源码分析

    摘要: 原创出处 https://www.cnkirito.moe/spring-security-4/ 「老徐」欢迎转载,保留摘要,谢谢! 4 过滤器详解 前面的部分,我们关注了Spring Sec ...

  2. java 日志体系(四)log4j 源码分析

    java 日志体系(四)log4j 源码分析 logback.log4j2.jul 都是在 log4j 的基础上扩展的,其实现的逻辑都差不多,下面以 log4j 为例剖析一下日志框架的基本组件. 一. ...

  3. Spring Boot 揭秘与实战 源码分析 - 工作原理剖析

    文章目录 1. EnableAutoConfiguration 帮助我们做了什么 2. 配置参数类 – FreeMarkerProperties 3. 自动配置类 – FreeMarkerAutoCo ...

  4. Spring Boot 揭秘与实战 源码分析 - 开箱即用,内藏玄机

    文章目录 1. 开箱即用,内藏玄机 2. 总结 3. 源代码 Spring Boot提供了很多”开箱即用“的依赖模块,那么,Spring Boot 如何巧妙的做到开箱即用,自动配置的呢? 开箱即用,内 ...

  5. Spring Environment(二)源码分析

    Spring Environment(二)源码分析 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) Spring Envi ...

  6. Java 集合系列(四)—— ListIterator 源码分析

    以脑图的形式来展示Java集合知识,让零碎知识点形成体系 Iterator 对比   Iterator(迭代器)是一种设计模式,是一个对象,用于遍历集合中的所有元素.  Iterator 包含四个方法 ...

  7. 【Java入门提高篇】Day21 Java容器类详解(四)ArrayList源码分析

    今天要介绍的是List接口中最常用的实现类——ArrayList,本篇的源码分析基于JDK8,如果有不一致的地方,可先切换到JDK8后再进行操作. 本篇的内容主要包括这几块: 1.源码结构介绍 2.源 ...

  8. Java并发包源码学习之AQS框架(四)AbstractQueuedSynchronizer源码分析

    经过前面几篇文章的铺垫,今天我们终于要看看AQS的庐山真面目了,建议第一次看AbstractQueuedSynchronizer 类源码的朋友可以先看下我前面几篇文章: <Java并发包源码学习 ...

  9. 并发编程(四)—— ThreadLocal源码分析及内存泄露预防

    今天我们一起探讨下ThreadLocal的实现原理和源码分析.首先,本文先谈一下对ThreadLocal的理解,然后根据ThreadLocal类的源码分析了其实现原理和使用需要注意的地方,最后给出了两 ...

随机推荐

  1. spring boot:用redis+redisson实现分布式锁(redisson3.11.1/spring boot 2.2)

    一,为什么要使用分布式锁? 如果在并发时锁定代码的执行,java中用synchronized锁保证了线程的原子性和可见性 但java锁只在单机上有效,如果是多台服务器上的并发访问,则需要使用分布式锁, ...

  2. Ubuntu服务安装

    一.ifconfig命令安装 sudo apt install net-tools 二.ssh服务安装 sudo apt-get install openssh-server netstat -ltn ...

  3. 一文秒懂!Python字符串格式化之format方法详解

    format是字符串内嵌的一个方法,用于格式化字符串.以大括号{}来标明被替换的字符串,一定程度上与%目的一致.但在某些方面更加的方便 1.基本用法 1.按照{}的顺序依次匹配括号中的值 s = &q ...

  4. 关于设置Vscode缩进,保存代码任然缩进无效解决方式

    在Vscode按F1,运行命令,输入Formatter config 把内容更改为以下代码 { "onSave": true, "javascript": { ...

  5. apktool重新打包添加签名

    一.生成apk apktool b 反编译后项目目录 -o 新apk名称.apk 二.生成签名 keytool -genkeypair -alias 新apk名称.apk -keyalg RSA -v ...

  6. Django (学习第一部 基础操作)

    django 1 django 文件相关信息 2 Python创建django 3 命令行创建django 4 Django 必会三板斧 5 静态文件配置 6 request对象方法 7 pychar ...

  7. sqlServer数据库中的日期转换

    今天开发过程中涉及到 sqlServer数据库数据同步至mysql数据,所以对日期格式转换需求,查到了一些关于sqlServer 的日期转换内容: 一般存入数据库中的时间格式为yyyy-mm-ddhh ...

  8. 4G DTU是什么 可以应用于哪些行业?

    4G是什么? 4G是移动电话网络通过蜂窝塔传输的信号的名称,蜂窝塔连接到更宽的互联网.这些是当今智能手机使用的信号,当您外出时,可以通过手机上网,因此他们不依赖电缆或光纤,也就是说无线网. 使用合适的 ...

  9. ASP.NET Core Authentication系列(四)基于Cookie实现多应用间单点登录(SSO)

    前言 本系列前三篇文章分别从ASP.NET Core认证的三个重要概念,到如何实现最简单的登录.注销和认证,再到如何配置Cookie 选项,来介绍如何使用ASP.NET Core认证.感兴趣的可以了解 ...

  10. PASS模型-第一周个人总结

    目录 PASS模型-第一周个人报告 1.个人任务 2.个人工作内容 2.1 登陆界面 2.2 信息采集 2.3 视觉搜索 3.个人小结 3.1 收获 3.2 优化 PASS模型-第一周个人报告 博客班 ...