文章目录
- BeanFactory和ApplicationContext有什么区别?
- spring bean的生命周期
- Spring 中3种依赖注入的方式:
- bean加载过程
- Spring 框架中都用到了哪些设计模式
- Spring框架中单例beans是线程安全的吗?
- @SpringBootApplication 注解
- 自动装配机制
BeanFactory和ApplicationContext有什么区别?
Bean工厂(BeanFactory)是Spring框架最核心的接口,提供了高级Ioc的配置机制.
应用上下文(ApplicationContext)建立在BeanFacotry基础之上,提供了更多面向应用的功能,如果国际化,属性编辑器,事件等等.
beanFactory是spring框架的基础设施,是面向spring本身,ApplicationContext是面向使用Spring框架的开发者,几乎所有场合都会用到ApplicationContext.
https://www.jianshu.com/p/b82ceb084adf
spring bean的生命周期
- Spring启动,查找并加载需要被Spring管理的bean,进行Bean的实例化
- Bean实例化后对将Bean的引入和值注入到Bean的属性中
- 如果Bean实现了BeanNameAware接口的话,Spring将Bean的Id传递给setBeanName()方法
- 如果Bean实现了BeanFactoryAware接口的话,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入
- 如果Bean实现了ApplicationContextAware接口的话,Spring将调用Bean的setApplicationContext()方法,将bean所在应用上下文引用传入进来
- 如果Bean实现了BeanPostProcessor接口,Spring就将调用他们的postProcessBeforeInitialization()方法。Spring 的 AOP 就是利用它实现的。
- 如果Bean 实现了InitializingBean接口,Spring将调用他们的afterPropertiesSet()方法。类似的,如果bean使用init-method声明了初始化方法,该方法也会被调用
- 如果Bean 实现了BeanPostProcessor接口,Spring就将调用他们的postProcessAfterInitialization()方法。
- 此时,Bean已经准备就绪,可以被应用程序使用了。他们将一直驻留在应用上下文中,直到应用上下文被销毁。
- 如果bean实现了DisposableBean接口,Spring将调用它的destory()接口方法,同样,如果bean使用了destory-method 声明销毁方法,该方法也会被调用。
简版:
- Bean 的实例化
- Bean 属性赋值
- Bean 的初始化
- Bean 的使用
- Bean 的销毁
java对象创建实例化和初始化的区别
实例化:在堆中申请内存空间,属性都是默认值
初始化:给对象的属性进行赋值操作或者初始化方法的调用
Spring Bean生命周期 (biancheng.net)
面试官:请你说一下 Bean 的生命周期 - 知乎 (zhihu.com)
java类的初始化和实例化区别 - pu20065226 - 博客园 (cnblogs.com)
Spring 中3种依赖注入的方式:
-
基于 field 注入(属性注入)
-
基于 setter 注入
-
基于 constructor 注入(构造器注入)
spring 新版本推荐使用构造器注入, 如下
private final Svc svc; public HelpService( Svc svc) {this.svc = svc; }
可以使用 lombok的
@RequiredArgsConstructor
或者@AllArgsConstructor
来自动生成构造方法, 虽然这样做也容易 违反单一责任原则@RequiredArgsConstructor // @AllArgsConstructor public class MdmProjectController {private final MdmProjectService mdmProjectService; }
field 注入,就是在bean的变量上使用注解进行依赖注入。本质上是通过反射的方式直接注入到field。这是我平常开发中看的最多也是最熟悉的一种方式,同时,也正是 Spring 团队所不推荐的方式。
源码位置: org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java:621
5.1.7.RELEASE
@Overrideprotected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {//获取要注入的字段Field field = (Field) this.member;Object value;//如果字段的值有缓存if (this.cached) {//从缓存中获取字段值valuevalue = resolvedCachedArgument(beanName, this.cachedFieldValue);}//没有缓存else {//创建一个字段依赖描述符DependencyDescriptor desc = new DependencyDescriptor(field, this.required);desc.setContainingClass(bean.getClass());Set<String> autowiredBeanNames = new LinkedHashSet<>(1);Assert.state(beanFactory != null, "No BeanFactory available");//获取容器中的类型转换器TypeConverter typeConverter = beanFactory.getTypeConverter();try {//核心!获取注入的值value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);}catch (BeansException ex) {throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);}// .... 省略代码if (value != null) {//显式使用JDK的反射机制,设置自动的访问控制权限为允许访问ReflectionUtils.makeAccessible(field);//为字段赋值field.set(bean, value);}}
为什么不推荐使用Field注入?
-
违反单一责任原则
添加新的依赖项非常容易。添加6个、10个甚至12个依赖项没有问题。当使用构造函数注入时,在某一点之后,构造函数参数的数量会变得过高,并且很明显会出现问题。依赖太多通常意味着类有太多的责任。这可能违反了单一职责原则和关注点分离,这表明类需要进一步的检查和重构。当直接注入字段时,没有这样的警告,因为这种方法可以无限扩展。 -
依赖隐藏
使用依赖注入容器意味着类不再负责管理自己的依赖项。获取依赖项的职责是从类中提取的。由其他人现在负责提供依赖项——依赖注入容器或在测试中手动分配它们。当类不再负责获取其依赖项时,它应该使用公共接口(方法或构造函数)清楚地与它们通信。
就是说 类的成员变量(依赖项)被"隐藏"了, 按照 控制翻转 的思想, 成员变量的赋值应该在初始化类时(显示)处理 , 但是由于 field注入是反射做的, 就不是类的主动操作了,
- 依赖注入容器耦合
DI框架的核心思想之一是托管类不应该依赖于所使用的DI容器。换句话说,它应该只是一个普通的POJO,可以独立地实例化它,前提是将所有必需的依赖项传递给它。通过这种方式,您可以在单元测试中实例化它,而不需要启动DI容器,并单独测试它(使用的容器更像是集成测试)。如果没有容器耦合,则可以将该类作为托管或非托管类使用,甚至可以切换到新的DI框架。
就是说, 要用bean应该从容器中取, 不能自己额外去取, 反射操作就是自己去取了
-
不变性
与构造函数不同,Field注入不能用于将依赖项分配给最终字段。 -
会造成循环依赖
2, 3, 4,5 几点都是由于 field方式是反射做的, 才造成了这种情况, 使用Field 注入不会有功能上的问题, 只是违背了spring的设计思想
Spring注解@Autowired源码分析 - 腾讯云开发者社区-腾讯云 (tencent.com)
关于Spring注入方式的几道面试题,你能答上么? - 知乎 (zhihu.com)
IDEA 提示 Field injection is not recommended_Ongoing蜗牛的博客-CSDN博客
科普文: 大白话讲解Spring的@bean注解 - 知乎 (zhihu.com)
一类注解是用于注册Bean:
@Component , @Repository , @ Controller , @Service , @Configration, @bean
等等一类注解是用于使用Bean
@Autowired , @Resource
等等本题中的注入方式, 指的是 bean注入的某个类中, 而
@service
等注解是注入的容器中
bean加载过程
- 根据bean名称 查询缓存中是否已存在(因为是单例)
context.getBean("person", Person.class);
-
确认该bean在工厂中是否已定义,解决循环依赖问题,有定义则完善该bean并返回
-
如果没有就会创建个新的,并放到bean池中
-
根据配置文件(xml.yaml)给bean设置属性(处理循环依赖问题(仅仅是单例的情况下))
-
初始化bean,如果有自定义的bean初始化方法,则会在这里执行
https://segmentfault.com/a/1190000012887776#item-4-3 .
https://www.jianshu.com/p/b82ceb084adf
Spring 框架中都用到了哪些设计模式
代理模式—在AOP和remoting中被用的比较多。
单例模式—在spring配置文件中定义的bean默认为单例模式。
模板方法—用来解决代码重复的问题 比如. RestTemplate, JmsTemplate, JpaTemplate。 前端控制器—Srping提供了DispatcherServlet来对请求进行分发。 视图帮助(View Helper )—Spring提供了一系列的JSP标签,高效宏来辅助将分散的代码整合在视图里。 依赖注入—贯穿于BeanFactory / ApplicationContext接口的核心理念。
工厂模式—BeanFactory用来创建对象的实例。
Builder模式- 自定义配置文件的解析bean是时采用builder模式,一步一步地构建一个beanDefinition
策略模式:Spring 中策略模式使用有多个地方,如 Bean 定义对象的创建以及代理对象的创建等。这里主要看一下代理对象创建的策略模式的实现。 前面已经了解 Spring 的代理方式有两个 Jdk 动态代理和 CGLIB 代理。这两个代理方式的使用正是使用了策略模式。
Spring框架中单例beans是线程安全的吗?
不是,Spring框架中的单例beans不是线程安全的。
springboot 启动过程
启动的流程主要分为两大阶段:
- 初始化
SpringApplication
运行SpringApplication
(在这里就扫描了所有的spring.factories,并缓存在内存中) - 运行
SpringApplication
的过程
其中运行SpringApplication
的过程又可以细分为以下几个部分:
1)SpringApplicationRunListeners
引用启动监控模块,
2)ConfigrableEnvironment
配置环境模块和监听:包括创建配置环境、加载属性配置文件和配置监听
3)ConfigrableApplicationContext
配置应用上下文:包括配置应用上下文对象、配置基本属性和刷新应用上下文, 其中最核心代码refreshContext(context)
刷新上下文, 将通过工程模式产生应用上下文中所需的bean。实现spring-boot-starter-*(mybatis、redis等)自动化配置的关键,包括spring.factories的加载、bean的实例化等核心工作
详解面试官经常问的SpringBoot启动流程机制 - 云+社区 - 腾讯云 (tencent.com)
高级面试题–SpringBoot启动流程解析_hfmbook的博客-CSDN博客_springboot启动流程面试
@SpringBootApplication 注解
@SpringBootApplication
是一个复合注解,包括@ComponentScan
,和@SpringBootConfiguration
,@EnableAutoConfiguration
。
@SpringBootConfiguration
继承自@Configuration
,二者功能也一致,标注当前类是配置类,并会将当前类内声明的一个或多个以@Bean
注解标记的方法的实例纳入到spring
容器中,并且实例名就是方法名。@EnableAutoConfiguration
的作用启动自动的配置,@EnableAutoConfiguration
注解的意思就是Springboot
根据你添加的jar包来配置你项目的默认配置,比如根据spring-boot-starter-web
,来判断你的项目是否需要添加了webmvc
和tomcat
,就会自动的帮你配置web项目中所需要的默认配置。@ComponentScan
,扫描当前包及其子包下被@Component
,@Controller
,@Service
,@Repository
注解标记的类并纳入到spring容器中进行管理。是以前的<context:component-scan>
(以前使用在xml中使用的标签,用来扫描包配置的平行支持)。
所以SpringBootApplication做了三件事, 能够识别并加载@bean的实例 / 开启自动去读配置(自己业务加的那些配置) / 扫描各种类对象并加载
@SpringBootApplication注解分析 - duanxz - 博客园 (cnblogs.com)
自动装配机制
1、main方法中SpringApplication.run(HelloBoot.class,args)的执行流程中有refreshContext(context)。
2、而这个refreshContext(context)内部会解析,配置类上自动装配功能的注解@EnableAutoConfiguration中的,@EnableAutoConfiguration中的,使用@Import引入类AutoConfigurationImportSelector
。
3、AutoConfigurationImportSelector这个类中的方法SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()
会读取jar包中的/项目中的META-INF/spring.factories文件。
4、spring.factories配置了自动装配的类,最后根据配置类的条件,自动装配Bean。
SpringBoot自动装配原理 - 简书 (jianshu.com)
(Spring Boot的自动装配原理及流程)_一碗谦谦粉的博客-CSDN博客_springboot自动装配面试