Part 1 JAVA EE简介
上个世纪Java主要由Sun公司掌控。
Sun制订了一套发展战略:
Java SE作为java发行的标准版,攻占桌面市场;
Java EE作为企业版,攻占企业服务市场;
Java ME作为移动终端版,攻占移动端市场。
在桌面端,微软凭借VB声名大噪,这种全新的交互方式对开发非常友好。
一个ui组件,就是一个类,类就是一个对象的模板。
组件有属性,对象也有,组件有行为,对象也有。面向对象的思想与UI界面天然适配。
基于UI界面的需求,我们可以推导出一个java面向对象机制的基本需求:
- 支持组件默认构造方式:默认提供无参构造方法
- 支持组件属性赋值+支持属性可扩展:提供对象的属性默认赋值方式+对象的属性增加机制
- 支持组件的本地加载与存储:提供序列化与反序列化机制
….
最后这个【东西】,我们叫做【java bean】。
java bean为了解决桌面应用的问题而诞生,但是sun公司的桌面战略全面失败了,java bean还在。
此外,sun在java se中还制定了SPI协议、JNDI协议。这里按下不表。
我们可以看下java ee的设计:
applet后来被ajax干掉了。
顺带干掉了模板引擎
web container:
sun制定了servlet标准,tomcat和jetty是其中最广泛使用的实现。
EJB:
enterprise java bean
sun制定规范的时候,把分布式事务等一些企业级应用的协议塞了进去。最终导致EJB庞大复杂,使用困难。
后来一本书横空出世《Expert One-on-One J2EE Development without EJB》
提出把ejb干掉,所有事情放在web container里解决的想法。
再后来一位热心读者找到了作者。把书里的示例代码扩充成了一个框架,就是Spring。
Spring站在了Java EE的肩膀上。
Spring的很多理念也被逐步吸收进JavaEE中,例如Spring推出了IOC,JAVAEE转头推出来DI的概念,本质就是换了个名字,做的事情是差不多的。
但是Java EE在制定web服务标准上,有一套。
例如Servlet、JPA、响应式编程、JDBC。
网络编程两个方向,业务上层不断寻找更合理的表达业务的模型,便于业务体系扩展下的代码维护;底层服务则不断尝试榨干机器的性能。
响应式编程相比传统的编程方式,理论上,对于机器IO的优化上,具有天然优势。
不过我没试过,感兴趣的同学可以尝试一下。
再后来,Sun公司因为光制定标准,赚不到钱,倒闭了,被甲骨文收购了。
甲骨文觉得一个商业公司整天研究协议和约定,也没意义,就捐给了apache基金会。
但是商标名还在Sun那里,于是基金会给它改了个名字,改叫jakarta雅加达 EE,所以从Java EE 9开始,
引入的新的包名就叫jakarta.xxx了,重获新生。对了,前面提到的,被Spring唾弃的EJB,现在还在里面活得好好的,甚至还不断推陈出新。
另一方面,Spring在2012年开始,启动了另一个项目——SpringBoot。
此时此刻,更广泛的技术大环境下,K8s+docker容器技术代表的云原生平台技术来势汹汹,Spring技术栈集体对于云应用微服务的探索SpringCloud在云平台技术面前,多少有点多余。
天生云原生友好的java框架——quarkus开始发力……
是Spring老树开新花,还是Quarkus笑到最后,亦或者是EJB卷土重来,不管如何,Java EE定义的协议都会一直保留。
Part 2 Spring Bean + IOC
- IOC定义与职责
1.1 定义
IOC是一种编程风格,将开发自己创建对象的过程委托给框架来完成。
控制程序的主动权从开发转交给程序本身(框架或者IOC容器),所以叫做控制反转。
在撰写一般性的业务代码时,我们时常会在一个服务的实例里调用另一个服务,假设服务A是调用方,服务B是被调用方,那么在二者发生关系时,服务A作为发起操作的一方,实际承担了资源管理者的职责,负责创建服务B实例、调用,甚至包括事后服务B其他生命周期事件,例如销毁、资源回收,也是在服务A内部来完成。在实现的时候,A的代码和B的实现就会有耦合的地方。通常来说,耦合是难以避免的,如果想要让两个业务模块尽量解耦,只能引入第三方的机制,比如让A去依赖C,而C是相对通用的一层,就可以增加系统的可扩展性,避免业务变动的时候,要反复修改A的实现。
一般我们会引入一些设计模式,比如工厂方法等等,但是这些也还是需要自己开发的。Spring面对这种一定会存在的问题,提出的解决方案是将资源的控制权,由业务类转交给应用容器,而把依赖关系配置化(XML或者注解的机制),配置化的东西,与业务实现关系不大,表现起来,就是资源控制权和流程的控制权变化了。原本是A去创建管理B,现在在框架应用里,A和B是平等的两个模块,他们之间也许存在业务关联,但是框架并不关心,框架知道A在哪里,B在哪里,如果A需要B,可以在配置里告诉框架应用,那么框架就会帮A创建好,同样如果谁需要A,框架应用也会准备好一个服务A的实例,资源的控制,交由框架来做了。
IOC的演变历史里,一开始人们把这个模式叫做好莱坞模式:"不要来找我们,我们来找你"(Don’t call us, we’ll call you)。后来经过Spring作者的推进以及martin flower的总结归纳,把这种模式总结为IOC和DI。对于这个模式,我们也可以这么说"不要来找我们,也不要自己去找别人,我们会来找你,我们也会替你找别人"。所以IOC的职责有两个:1. 依赖查找 2. 依赖注入。
IOC是我们的目的,DI是实现这个目的的手段,特指Spring框架应用管理所有服务实例,处理服务间依赖关系的手段。
有了上面的抽象,Spring把这个控制服务生命周期的应用,叫做Spring IoC Container,后面简称是Spring容器,而每一个需要被容器管理的业务组件或者业务模块,叫做Spring Bean。
这些概念偏理论一些。
1.2 职责
根据前面的定义,我们可以总结IOC容器的通用职责:- 依赖处理:
- 依赖查找
- 依赖注入
- 生命周期管理
- 容器自身的生命周期
- 托管的资源(Spring Beans 或者其他资源)的生命周期
- 配置
- 容器
- 外部化配置
- 托管的资源
1.3 历史与演变
Spring不是唯一实现了IOC的框架,IOC容器的实现还包含: - Java SE
- java beans (GUI应用蓬勃发展时代的遗留产物)
- java serviceLoader SPI
- JNDI java naming and directory interface
- Java EE
- EJB(enterprise java beans) 在2004年,ejb 3.0就已经实践了依赖注入和依赖查找的思想
- servlet
- 开源:
- apache avaion
- picoContainer (Spring Framework IOC机制的灵感来源)
- Goodle Guice (在国外和Spring一样,也很流行)
- Spring Framework
SPI机制:
服务提供者和服务使用者约定服务的接口,通过在resource目录META-INF下新建文件指定接口实现类的位置,使得服务使用者可以动态加载服务实现。
JNDI:
JNDI即Java Naming and Directory Interface(JAVA命名和目录接口),java命名目的就是为了记录一些不方便记录的内容,就像人的名字或DNS中的域名与IP的关系。
JND里有两个核心接口:
- bind
-
lookup
简单写一个jndi的demo:
public class Pet implements Remote, Serializable {private static final long serialVersionUID = 1L;
public int id;
public String name;@Override
public String toString() {
return id+","+name;
}
}
public class JndiDemo {
public static void bindPet() throws Exception{
LocateRegistry.createRegistry(6000);
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
System.setProperty(Context.PROVIDER_URL, "rmi://localhost:6000");
InitialContext ctx = new InitialContext();
Pet pet = new Pet();
pet.id=1;
pet.name = "tiger";
ctx.bind("pet", pet);
ctx.close();
}
public static void findPet() throws NamingException {
InitialContext initialContext = new InitialContext();
Pet pet = (Pet)initialContext.lookup("pet");
System.out.println(pet.toString());
initialContext.close();
}
public static void main(String[] args) throws Exception {
bindPet();
findPet();
}
}
JNDI隐藏了依赖查找的细节,但是没有完全隐藏,需要程序员调用接口手动进行依赖查找。
查找的范围,或者说依赖的提供者,主要是J2EE服务的开发商。例如,数据库连接池\Java log服务,就使用了JNDI。
去年log4j的0day漏洞,起因就是在使用JNDI的过程中,存在注入的风险,给不怀好意的人留下了远程执行代码的后门。
EJB+servlet的实现依赖J2SE制定的规范。
Goodle Guice和Spring比较像,但是比较轻量级,使用规则也比较简单。
-
Spring Bean基础
前面提到,JavaBean由简单到复杂,衍生出了EJB,人们觉得EJB太复杂,返璞归真,于是,SpringBean的概念就诞生了,本身在概念含义上,他和JavaBean还是很像的
2.1 Bean Definition
包含了Bean的元配置信息:- 名称
- 行为配置元素:作用域、自动绑定的模式、生命周期回调
- 其他Bean的引用:例如合作者或者依赖
- 配置设置:例如Bean属性Properties
构建bean definition的方式: - BeanDefinitionBuilder
- AbstractBeanDefinition以及他的派生类
// 1. bean definition builder
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
// 通过属性设置
beanDefinitionBuilder.addPropertyValue("name", "djf")
.addPropertyValue("age", 23)
.addPropertyValue("id", 1);
// 获取bean definition实例
AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
// 2. 通过AbstractBeanDefinition派生
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(User.class);
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.addPropertyValue("name", "jeff");
propertyValues.add("id", 2);
genericBeanDefinition.setPropertyValues(propertyValues);java
bean definition里还保存了bean的唯一识别符,一般是id或者name,此外还有别名。
Spring一个容器内,bean的名称是唯一的,由beanNameGenerator生成,Spring内部目前有三种生成方式 - DefaultBeanNameGenerator since 2.0.3
- AnnotationBeanNameGenerator Spring 2.5之后才引入了注解
- FullyQualifiedAnnotationBeanNameGenerator since 5.2.3
通常为了节约内存的开销,都是单例。默认的名称生成器,委托给 org.springframework.beans.factory.support.BeanDefinitionReaderUtils#generateBeanName(org.springframework.beans.factory.config.BeanDefinition, org.springframework.beans.factory.support.BeanDefinitionRegistry)来进行名称生成。
public static String generateBeanName(
BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)
throws BeanDefinitionStoreException {
String generatedBeanName = definition.getBeanClassName();
if (generatedBeanName == null) {
if (definition.getParentName() != null) {
generatedBeanName = definition.getParentName() + "$child";
}
else if (definition.getFactoryBeanName() != null) {
generatedBeanName = definition.getFactoryBeanName() + "$created";
}
}
if (!StringUtils.hasText(generatedBeanName)) {
throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " +
"’class’ nor ‘parent’ nor ‘factory-bean’ – can’t generate bean name");
}if (isInnerBean) {
// Inner bean: generate identity hashcode suffix.
return generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
}// Top-level bean: use plain class name with unique suffix if necessary.
return uniqueBeanName(generatedBeanName, registry);
}
还是很好懂的。
AnnotationBeanNameGenerator支持@Component注解的类,或者@Repository这种,注解本身被Component注解过的。
也支持JSR330里的标准注解 jakarta.annotation.ManagedBean和jakarta.inject.Named
此外,bean还有别名,一般用于更具有场景化的命名方法上。
别名的配置方式上,可以通过xml来配置
2.2 BeanDefinition的注册
- xml配置元信息
- java注解配置元信息
- @Bean
- @Component
- @Import
- Java Api配置元信息
- 命名方式 BeanDefinitionRegistry#registryBeanDefinition(String, BeanDefinition)
- 非命名方式 BeanDefinitionReaderUtil#registryBeanDefinitionWithGeneratedName(AbstractBeanDefinition, BeanDefinitionRegistry)
- 配置类方式 AnnotatedBeanDefinitionReader#registry(Class…)
Bean的实例化方法:
- 常规方式
- 构造器: xml java注解 JAVA api
- 静态工厂方法: xml java api
- Bean实例方法: xml java api 本质和静态工厂方法没啥区别 用的不多
- FactoryBean : xml java注解 和JAVA api
- 特殊方式
- 通过ServiceLoaderFactoryBean : xml java注解和Java api
- 通过AutowireCapableBeanFactory::createBean(class, int, boolean)
- 通过beanDefinitionRegistry::registryBeanDefinition(string, BeanDefinition)
Bean的初始化方法
- @PostConstruct标注方法
- 实现InitializingBean接口的afterPropertiesSet方法
- 自定义初始化方法:
- XML配置
- Java注解 @Bean initMethod
- Java API: AbstractBeanDefinition::setInitMethodName(string)
思考:假设这三种方法都在同一个Bean中定义,那么这些方法的执行顺序是怎样的?- PostConstruct 2. InitializingBean afterPropertiesSet 3. 自定义
自定义初始化方法其实是放在beandefinition里,所以可以直接调Spring的api
- PostConstruct 2. InitializingBean afterPropertiesSet 3. 自定义
Bean的延迟初始化:
- xml: lazy-init
- java注解 @Lazy
、
思考:某个bean被定义为延迟初始化的时候,那么Spring容器返回的对象和非延迟的对象存在怎样的差异?
非延迟:在应用上下文启动时完成
延迟:依赖查找触发了初始化
Bean的销毁方法: - @PreDestory方法
- 实现DIsposableBean接口的desctory()方法
- 自定义销毁方法: destory
假设同时定义,执行顺序:
bean如何垃圾回收:- 关闭spring容器
- 执行GC
- SpringBean覆盖的finalize方法被回调:已经被标记为弃用了
思考1: 如何注册一个SpringBean
通过BeanDefinition和外部单体对象来注册
思考2:什么是Spring Bean Definition
scope\role\parent name\lazy init\depend on依赖\autowire candidate是否是一个自动装配的\primary\ctor args\description
思考3: Spring容器是怎么管理注册Bean的
IOC配置元信息读取和解析、依赖查找和注入、Bean生命周期 - spring bean作用域
有哪些作用域: - singleton 默认 一个bean factory一个
- prototype 原型作用域 每次依赖查找和注入生成新的对象
- request 将SpringBean存在Servelet Request上下文中
- session 将SpeingBean存在HttpSession中
- application 存储在ServeletContext中
singleton真的在当前应用唯一吗
在java里,单例模式通常是classloader维度的。在Spring里,则是beanfactory维度是唯一的。
原型模式:哪些情况会创建新的实例
- 注意事项:
- Spring容器没有办法管理prototype Bean的完整生命周期,也没有办法记录实例的存在。销毁回调方法将不会执行,可以利用BeanPostProcessor进行清扫工作(其实不太合理)。
- singleton和prototype都会执行初始化回调,但是只有singleton会执行销毁回调
request作用域
session:有哪些局限
application:是否真的有必要
自定义bean的作用域:设计bean作用域应该注意哪些原则
- 实现Scope接口
-
注册Scope
参考SimpleThreadlocalScope自定义一个ThreadlocalScope:
public class ThreadlocalScope implements Scope {public static final String SCOPE_NAME = "thread-local";
private final NamedThreadLocal<Map<String, Object>> threadLocal = new NamedThreadLocal<>("thread-local-scope"){
@Override
protected Map<String, Object> initialValue() {
return new HashMap<>();
}
};@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
Map<String, Object> context = getContext();
Object o = context.get(name);
if (o == null) {
Object object = objectFactory.getObject();
context.put(name, object);
}
return context.get(name);
}private Map<String, Object> getContext() {
return threadLocal.get();
}@Override
public Object remove(String name) {
Map<String, Object> context = getContext();
return context.remove(name);
}@Override
public void registerDestructionCallback(String name, Runnable callback) {}
@Override
public Object resolveContextualObject(String key) {
Map<String, Object> context = getContext();
return context.get(key);
}@Override
public String getConversationId() {
Thread thread = Thread.currentThread();
return String.valueOf(thread.getId());
}
}
补充资料:spring cloud的refresh scope如何控制bean的动态刷新- Spring Bean生命周期
- bean元信息配置阶段:beandefinition配置与扩展
- BeanDefinition配置
- 面向资源
- XML配置
- Properties资源配置(已废弃)
- groovy
- 面向注解
- 面向API
- BeanDefinition解析
- 面向资源的BeanDefinition解析
- BeanDefinitionReader
- XML解析器-BeanDefinitionParser
- 面向注解的BeanFinition解析
- AnnotatedBeanDefinitionReader(ComponentScan)
- beandefinition与单体bean注册
- BeanDefinition 注册
- BeanDefinitionRegistry
- 唯一真正进行注册工作的实现类:DefaultListableBeanFactory
数据结构:
beanDefinitionMap: ConcurrentHashMap Map of bean definition objects, keyed by bean name.
beanDefinitionNames: ArrayList List of bean definition names, in registration order.- beanfinition合并阶段
- 父子BeanDefinition合并
- 当前BeanFactory查找
- 层次性BeanFactory查找
org.springframework.beans.factory.support.AbstractBeanFactory#getMergedBeanDefinition(java.lang.String)
合并后,GenericBeanDefinition变成RootBeanDefinition,并且从parent里继承property value并覆盖。
存放在BeanFactory的字段mergedBeanDefinitions中- bean class加载阶段: bean classloader能够被替换吗
- ClassLoader类加载
- Java Security 安全控制
- ConfigurableBeanFactory临时ClassLoader
- bean实例化阶段:bean的实例化能否被绕开、是通过java反射创建的吗 实例化后是否一定被使用
- 实例化前阶段
- 非主流生命周期 – Bean实例化前阶段
- org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
可以在这个环节创建bean的代理,跳过之后所有的环节
- 实例化 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
- 传统实例化方式:实例化策略InstantiationStrategy
- 构造器依赖注入
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#determineConstructorsFromBeanPostProcessors
从SmartInstantiationAwareBeanPostProcessor中获取推荐的构造方法,默认是null
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#instantiateBean
默认的实例化bean的方法
默认使用org.springframework.beans.factory.support.CglibSubclassingInstantiationStrategy的实现
org.springframework.beans.factory.support.SimpleInstantiationStrategy#instantiate(org.springframework.beans.factory.support.RootBeanDefinition, java.lang.String, org.springframework.beans.factory.BeanFactory)
如果有构造参数,会调用方法org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#autowireConstructor进行实例化
- 实例化后阶段 Bean属性赋值(populate)判断
- org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation
- 属性复制前阶段:配置后的propertyValues还有机会修改吗
- org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation
- Bean属性元信息
- PropertyValues
- Bean属性赋值前回调
- Spring 1.2 – 5.0 org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessPropertyValues
- Spring 5.1 – org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessProperties
- aware接口回调阶段:众多aware接口回调的顺序是安排的?
隶属于Bean的初始化阶段
- aware接口回调阶段:众多aware接口回调的顺序是安排的?
- Spring Aware接口
- BeanNameAware
- BeanClassLoaderAware
- BeanFactoryAware
- EnvironmentAware
- EmbeddedValueResolverAware
- ResourceLoaderAware
- ApplicationEventPublisherAware
- MessageSourceAware
- ApplicationContextAware
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods
application生命周期
org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces- bean初始化前阶段
- 方法回调 org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization
- bean初始化阶段:postconstruct initializingbean
- @PostConstruct 标注方法
- 实现InitializingBean接口的afterPropertiesSet()方法
- 自定义初始化方法
- 初始化后阶段:beanpostprocessor
- BeanPostProcessor#postProcessAfterInitialization
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization- 初始化完成阶段
- spring 4.1+ SmartInitializingSingleton#afterSingletonInstantiated
- 销毁前阶段:destructionawarebeanpostprocessor
- DestructionAwareBeanPostProcessor#postProcessBeforeDestruction
beanFactory.addBeanPostProcessor(new MyDestructionAwareBeanPostProcessor());
beanFactory.destroyBean(user);
只代表在容器里销毁 不代表在jvm里销毁- 销毁阶段:predestory disposablebean
- Bean销毁
- @PreDestory标注方法
- 实现DisposableBean接口的destory()方法
- 自定义销毁方法
- bean垃圾收集 GC :何时需要GC SpringBean
- Bean垃圾回收GC
- 关闭Spring容器(应用上下文)
- 执行GC
- SpringBean覆盖的finalize被回调
思考:
BeanFactoryPostProcessor与BeanPostProcessor的区别
BeanFactoryPostProcessor是Spring BeanFactory(实际是ConfigurableListableBeanFactory)的后置处理器,用于扩展BeanFactory,或者通过BeanFactory进行依赖查找和依赖注入
BeanFactoryPostProcessor必须有Spring ApplicationContext执行,BeanFactory无法与其直接交互
而BeanPostProcessor则直接与BeanFactory关联,属于N对1的关系
BeanFactory是怎样处理Bean生命周期的?
BeanFactory的默认实现是DefaultListableBeanFactory 其中Bean生命周期与方法映射如上
- Spring依赖查找
依赖查找的前世今生:- 单一类型依赖查找: JNDI JavaBeans
- 集合类型依赖查找: java.beans.beancontext.BeanContext
- 层次性依赖查找: java.beans.beancontext.BeanContext
单一类型依赖查找接口-BeanFactory - 根据Bean名称查找
- getBean
- 根据Bean类型查找
- Bean实时查找
- Spring 3.0 getBean(class)
- Spring 4.1 覆盖默认参数 getBean(class, object…)
- Spring 5.1 Bean延迟查找
- getBeanProvider(class)
- getBeanProvider(resolvableType)
- 根据Bean名称+类型查找 getBean(name,class)
@Configuration注解对于application来说不是必须的
集合类型的依赖查找接口 - 集合类型依赖查找接口 – ListableBeanFactory 包括并不限于map list等
- 根据Bean类型(当前类型以及它的子类型都包含在内、isAssignableFrom)查找
- 获取同类型Bean名称列表
- getBeanNamesForType(Class)
- Spring 4.2 getBeanNamesForType(ResolvableType)
- 获取同类型Bean实例列表
- getBeanOfType(Class)以及重载方法
- 通过注解类型查找
- Spring 3.0 获取注解类型Bean名称列表
- getBeanNamesForAnnotation(Class<? extends Annotation>)
- Spring 3.0 获取注解类型Bean实例列表
- getBeansWithAnnotstion(Class<? extends Annotation>)
- Spring 3.0 获取指定名称+注解类型的Bean列表
过程: - getBeanNamesForType从BeanDefinition中获取,只会看Bean的定义,不会涉及Bean的初始化
- getBean则会触发Bean的初始化,提前把一些Bean初始化,但是初始化过程并不完整,可能会导致一些未知的错误;建议判断的时候先通过名称去判断,再通过类型去判断,会更好一些
层次类型的依赖查找: - 层次性依赖查找接口 HierarchicalBeanFactory
- 双亲BeanFactory: getParentBeanFactory()
- 层次性查找
- 根据Bean名称
- 基于containsLocalBean方法实现b
- 根据Bean类型查找实例列表
- 单一类型:BeanFactoryUtils#beanOfType
- 集合类型:BeanFactoryUtils#beanOfTypeIncludingAncestors
- 根据Java注解查找名称列表
- BeanFactoryUtils#beanNamesForTypeIncludingAncestors
延迟依赖查找
- BeanFactoryUtils#beanNamesForTypeIncludingAncestors
- bean延迟依赖查找接口
- ObjectFactory
- ObjectProvider
- Spring5对java 8特性扩展
- 函数式接口
- getIfAvailable(supplier)
- ifAvailable(Consumer)
- Stream扩展 – stream()
安全依赖查找
内建可查找的依赖
- environment 外部化配置以及profiles
- systemProperties Java系统属性
- systemEnvironment 操作系统环境变量
- messageSource 国际化文案
- lifecycleProcessor lifecycle bean处理器
- applicationEventMultiCaster Spring事件广播器
注解驱动Spring应用上下文内建可查找的依赖 - internalConfigurationAnnotationProcessor ConfigurationClassPostProcessor对象 处理Spring配置类
- internalAutowiredAnnotationProcessor AutowiredAnnotationBeanPostProcessor对象 处理@autowired以及@value注解
- internalCommonAnnatationProcessor CommontAnnotationBeanPostProcessor对象 处理jsr-250注解 如@postConstruct等
- internalEventListenerProcessor EventListenerMethodProcessor 处理标记了@EventListener的Spring事件监听方法
- internalEventListenerFactory DefaultEventListenerFactory对象 @EventListener事件监听方法适配为applicationListener
- internalPersistenceAnnatationProcessor PersistenceAnnatationBeanPostProcessor对象 处理JPA注解场景
具体有哪些内建的依赖可以看 org.springframework.context.annotation.AnnotationConfigUtils
依赖查找中的异常:bean找不到、不唯一、创建失败 - NoSuchBeanDefinition
- NoUniqueBeanDefinition
- BeanInstantiation
- BeanCreation
- BeanDefinitionStore
-
Spring依赖注入
有哪些模式和类型- 手动模式-配置或者变成的方式,提前安排注入规则
- xml
- java注解配置元信息
- API配置元信息
- 自动模式-实现方提供依赖自动关联的方式,按照内建的注入规则
- Autowiring自动绑定
- 依赖注入类型
- setter
- 构造器
- 字段(不推荐,例如resource并不是spring的api是通用api,不纯粹)
- 方法
- 接口回调
自动绑定:有哪些自动绑定的模式和使用场景、限制和不足 - autowiring modes
- no 默认值 没有激活 需要手动指定依赖注入对象
- byName 根据被注入属性的名称作为Bean名称进行依赖查找
- ByType
- constructor 根据bytype用于构造器参数
org.springframework.beans.factory.annotation.Autowire
autowiring的限制和不足
限制: - 会被字段注入或者构造器注入覆盖,不能绑定简单的类型,例如strings或arrays
- 不够精确 byname bytype
- 无法 确定在上下文中是否存在
- 多个Bean命中时需要用@qualifier primary进行标记
setter的不足,为什么官方推荐构造器注入 - 实现方法
- 手动模式
- xml
- java注解配置元信息
- API配置
- 自动
- byName
- byType
setter注入的原理是什么?
beanDefinitionBuilder.addPropertyReference
字段注入 - 手动方式
- java注解配置元信息
- Autowired (会忽略static静态字段或者静态方法)
- Resource
- Inject (可选)
方法注入是什么 - 手动方式
- java注解配置元信息
- Autowired (会忽略static静态字段或者静态方法)
- Resource
- Inject (可选)
- @Bean
接口回调注入 - Aware系列接口回调
- BeanFactoryAware
- ApplicationContextAware
- EnvironmentAware
- …
依赖注入类型选择 - 注入类型
- 低依赖:构造器注入
- 多依赖:setter注入泵
- 便利性:字段注入
- 声明类:方法注入
基础类型注入
基础类型 - 原生类型primitive int boolean
- 标量类型 number character enum
- 常规类型 Object String TimeZone
- Spring类型 resource inputSource Formatter
集合类型的注入
集合类型 - 数组类型Array 原生类型 、标量类型、常规类型、Spring类型
- 集合类型Collection
- collection List Set
- map Properties
xml配置:字符转集合类型,中间涉及类型转换
限定注入 - 使用注解@Qualifier限定
- 通过Bean名称限定
- 通过分组限定
- 基于注解@Qualifier扩展限定
- 自定义注解-如Sping Cloud @LoadBalanced
@Autowired
@Qualifier
private CollectionqualifiedUsers;
@Autowired
private User user;@Bean
@Qualifier
public User getUser1() {
User user = new User();
user.setId(1L);
return user;
}@Bean
@Qualifier
public User getUser2() {
User user = new User();
user.setId(1L);
return user;
}
延迟依赖注入- 使用API ObjectFactory延迟注入
- 单一类型
- 集合类型
- 使用API ObjectProvider延迟注入(推荐)
- 单一类型
- 集合类型
依赖处理的过程 - 基础知识
- 入口:org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency
- 依赖描述符:DependencyDescriptor
- 自动绑定候选对象处理器:AutowireCandidateResolver
依赖处理是依赖注入的一个环节,通过解析或者反射将对象插入
autowired 的规则和原理 - 元信息解析
- 依赖查找
- 依赖注入(字段 方法)
在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean中调用
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties
处理字段注入
过程大概是:
- 在doCreateBean中会先调用applyMergedeanDefinitionPostProcessors,然后执行populateBean;所以会先调用PostProcessMergedBeanDefinition后执行InstantiationAwareBeanPostProcessor的postProcessProperties
-
postProcessProperties有两个步骤
(1)findAutowiringMetadata查找注入元数据,没有缓存就创建,具体就是前一部分依赖处理的内容。最终会返回InjectionMetadata,里面包括待注入的InjectedElement信息(field、method)等等
(2) 执行InjectionMetadata的inject方法,具体为AutowiredFieldElement和AutowiredMethodElement的Inject方法;AutowiredFieldElement inject具体流程:(2.1.1)DependencyDescriptor的创建 (2.1.2)调用beanFactory的resolveDependency获取带注入的bean (2.1.2.1)resolveDependency根据具体类型返回候选bean的集合或primary 的bean (2.1.3)利用反射设置field
在构建BeanDefinition时,是需要知道所有元数据信息的,所以会执行一次findAutowiringMetadata;
所以postProcessProperties里再执行的时候,实际是拿的缓存信息,一般不需要重新再执行一次。
jsr330 inject和autowired的联系- @Inject注入过程
-
如果JSR330存在于ClassPath中,复用AutowiredAnnotationBeanPostProcessor实现
需要在pom中添加jsr330的依赖
查找自动注入的注解
private MergedAnnotation<?> findAutowiredAnnotation(AccessibleObject ao) {
MergedAnnotations annotations = MergedAnnotations.from(ao);
Iterator var3 = this.autowiredAnnotationTypes.iterator();MergedAnnotation annotation;
do {
if (!var3.hasNext()) {
return null;
}Class extends Annotation> type = (Class)var3.next(); annotation = annotations.get(type);
} while(!annotation.isPresent());
return annotation;
构造时,确认是否包含inject注解
try {
this.autowiredAnnotationTypes.add(ClassUtils.forName("jakarta.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
this.logger.trace("’jakarta.inject.Inject’ annotation found and supported for autowiring");
} catch (ClassNotFoundException var3) {
}
java通用注入原理 @ejb @resource
- CommonAnnotationBeanPostProcessor
- 注入注解
- javax.xml.ws.WebServiceRef
- javax.ejb.EJB
- javax.annotation.Resource
- 生命周期注解
- javax.annotstion.PostConstruct
- javax.annotation.PreDestory
所以通用注解和autowire注解走的是不同的postprocessor
他们之间存在优先级的差异,并且通用注解还包含生命周期的处理。
比如common和autowired执行时,common优先级比autowired要高,order值更小即common是order=integer.max_value-3 autowired=integer.max_value-2 common的invoke方法在autowired invoke方法之前执行。
自定义依赖注入注解:如何最简化实现自定义依赖注解
- 基于AutowiredAnnotationBeanPostProcessor实现
- 自定义实现
- 生命周期处理
- InstantiationAwareBeanPostProcessor
- MergedBeanDefinitionPostProcessor
- 元数据
- InjectedElement
- InjectionMetadata
- @Autowired注解可以作为元注解,自定义一个被标记的注解即可
- 或者完全自定义,需要定制beanPostProcessor
例子1:
@Autowired
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAutowired {
boolean required() default true;
}
例子2
@Bean(name = AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)
public static AutowiredAnnotationBeanPostProcessor beanPostProcessor() {
AutowiredAnnotationBeanPostProcessor beanPostProcessor = new AutowiredAnnotationBeanPostProcessor();
// @autowired + 新的注解
Set<Class<? extends Annotation>> classSet =
new LinkedHashSet<>(asList(Autowired.class, MyAutowired.class));
beanPostProcessor.setAutowiredAnnotationTypes(classSet);
return beanPostProcessor;
}
注意 需要是static的方法
总结 - 有多少种注入方式
构造器 setter 字段 方法 接口回调 - 偏好构造器还是setter
均可使用 推荐构造器 setter注入可选依赖 - 依赖注入的来源有哪些
- Spring依赖来源
容器自建、自定义spring bean、还有其他吗
- 查找来源
- spring beandefinition :
- xml
- @bean
- BeanDefinitionBuilder
- 单例对象
- API实现 registersingleton
- Spring内建的BeanDefinition
- xxxBeanPostProcessor
- xxProcessor
依赖注入和依赖查找的来源不同?
- 依赖注入的来源
- Spring BeanDefinition
- 单例对象
- 非Spring容器管理对象 registerResolveDependency 他们可以被注入进来,但是无法被依赖查找到
- beanFactory
- resourceLoader
- ApplicationEventPublisher
- ApplicationContext
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
prepareBeanFactory
所以Spring依赖查找和依赖注入的范围并不完全一致
spring管理对象和游离对象
来源
SpringBean对象
生命周期管理
配置元信息
使用场景
Spring BeanDefinition
是
是
有
依赖查找、依赖注入
单例对象(registerSingleton)
是
否
否
依赖查找、依赖注入
Resolvable Dependency
否
否
无
依赖注入
Spring bean definition作为依赖来源、单例对象、非spring容器管理对象、外部化配置
- 要素
- 元数据 BeanDefinition
- 注册 BeanDefinitionReigistry#registerBeanDefinition
- 类型:延迟 非延迟
- 顺序 Bean生命周期顺序按照注册顺序
单例对象作为依赖来源
- 要素
- 来源:外部普通java对象 不一定是pojo
- 注册: SingletonBeanRegistry#registerSingleton
- 限制
- 无生命周期管理
- 无法实现延迟初始化bean
单例对象就是spring直接管理对象本身,需要的时候直接获取,普通对象的话spring管理的是对象的定义元信息,需要的时候是根据元信息创建这个对象返回
非spring容器管理对象作为依赖来源
- 要素
- 注册 ConfigurableListableBeanFactoey#registerResolvableDependency
-
限制
- 无生命周期管理
- 无法实现延迟初始化
- 无法通过依赖查找
且只能用于byType的依赖注入
public class ResolvableDependencySourceDemo {
@Autowired
private String value;@PostConstruct
public void init() {
System.out.println(value);
}public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
annotationConfigApplicationContext.register(ResolvableDependencySourceDemo.class);annotationConfigApplicationContext.addBeanFactoryPostProcessor(beanFactory -> { beanFactory.registerResolvableDependency(String.class, "hello,world"); }); annotationConfigApplicationContext.refresh(); annotationConfigApplicationContext.close();
}
}
如何理解resolvabledependency @value如何注入外部化配置的
外部化配置作为依赖来源 - 要素
- 类型 非常规Spring对象依赖来源
- 限制
- 无生命周期管理
- 无法实现延迟初始化
- 无法通过依赖查找
org.springframework.beans.factory.support.DefaultListableBeanFactory#getAutowireCandidateResolver
org.springframework.beans.factory.support.AutowireCandidateResolver#getSuggestedValue
思考- 依赖注入和查找的来源是否相同
显然不同 - 单例对象能否在ioc容器启动后注册
可以。
单例对象和BeanDefinition不同,BeanDefinition会被ConfigurableListableFactory#freezeConfiguration方法影响,从而冻结注册,但是单例对象不存在这个限制
严格来说,这个冻结操作其实不是严格的限制,也可以注册bd,但是在启动完成后再注册bd已经没有意义了,不如直接注册单例对象 - Spring注入的来源
bd 单例对象 resolable dependency @Value外部化配置 - Spring应用上下文生命周期
启动准备阶段
- 依赖注入和查找的来源是否相同
- org.springframework.context.support.AbstractApplicationContext#prepareRefresh 方法
- 启动时间 – startupDate
- 状态标识 – closed(false) active(true)
- 初始化PropertySource – initPropertySources() 到这里的时候 environment还没生成,一个hook方法 用于子类进行扩展的
- 检验environment中必须属性 getEnvironment().validateRequiredProperties(); 在AbstractApplicationContext中,到这一步才生成了environment对象,但是其实在其他派生类里,都是在前一步,甚至更早的时候,就生成了。
- 初始化事件监听器集合 初始化earlyApplicationListeners,在refresh操作之前,可能存在一些别的事件,这些事件交给earlyApplicationListeners处理
- 初始化早期Spring事件集合 earlyApplicationEvents
beanfactory创建阶段
- org.springframework.context.support.AbstractApplicationContext#obtainFreshBeanFactory方法
- 刷新Spring应用上下文底层BeanFactory – refreshBeanFactory AbstractApplicationContext里面实际没有实现,默认实现在org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory
- 销毁或者关闭BeanFactory 如果已存在的话
- 创建BeanFactory – createBeanFactory
- 设置BeanFactory id
- 设置"是否允许BeanDefinition重复定义" – customizeBeanFactory(DefaultListableBeanFactory)
- 设置"是否允许循环引用(依赖)" – customizeBeanFactory(DefaultListableBeanFactory)
- 加载BeanDefinition – loadBeanDefinitions(defaultListableBeanFactory)方法;默认空实现,子类的实现有xml和注解两种
- 关联新建BeanFactory到Spring应用上下文
- 返回Spring应用上下文底层BeanFactory – getBeanFactory()
关注点:
loadBeanDefinitions的方式,子类有所区分
beanfactory的实现,内部唯一的实现:DefaultListableBeanFactory
beanfactory准备阶段
- org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory方法
- 关联classloader: classloader是从外部添加进来的,所以可以通过添加自己的,实现一些类隔离的特殊需求
- 设置Bean表达式处理器: 负责SpringEL的处理
- 添加PropertyEditorRegister实现 – ResourceEditorRegister,可以关联多种实现,用于类型转换
- 添加Aware回调接口BeanPostProcessor实现 – ApplicationContextAwareProcessor
- 忽略Aware回调接口作为依赖注入接口 ignoreDependencyInterface
- 注册ResolvableDependency对象 – BeanFactory\ResourceLoader\ApplicatrionEventPublisher以及ApplicationContext
- 注册ApplicationListenerDetector对象
- 注册LoadTimeWeaverAwareProcessor对象
- 注册单例对象- Environment\java system properties以及OS环境变量
ignoreDependencyInterface的作用可以参考:
https://www.jianshu.com/p/3c7e0608ff1f
https://segmentfault.com/a/1190000039159709
beanfactory后置处理阶段
- org.springframework.context.support.AbstractApplicationContext#postProcessBeanFactory
- 由子类覆盖
- org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors
- 调用BeanFactoryPostProcessor或者BeanDefinitionRegistry后置处理方法
- 注册LoadTimeWeaverWaareProcessor对象
第二种方法.AbstractApplicationContext#invokeBeanFactoryPostProcessors – 一部分当前应用上下文所关联的,另一部分是当前上下文所关联的BeanFactory注册的BeanDefinition Bean对象 也就是一部分来源框架自定义,一部分来源开发者自定义- 处理当前上下文的BeanDefinitionRegistryPostProcessor-框架内部
- 处理beanFactory中的BeanDefinitionRegistryPostProcessor-开发者自定义,优先处理@PriorityOrdered,@Ordered,常规顺序
- 处理当前上下文的BeanFactoryPostProcessor
- 处理beanFactory中的BeanFactoryPostProcessor-if else后完成处理
到这里beanFactory的初始化操作基本完成。
beanfactory注册beanprocessor阶段
- org.springframework.context.support.AbstractApplicationContext#registerBeanPostProcessors方法
- 注册PriorityOrdered类型的BeanPostProcessor Beans
- 注册Ordered类型的BeanPostProcessor Beans
- 注册普通BeanPostProcessor Beans
- 注册MergedBeanDefinitionPostProcessor Beans
- 注册ApplicationListenerDetector对象
- 获取所有 BeanPostProcessor 类型的 beanName
- 添加 BeanPostProcessor – BeanPostProcessorChecker,用于打印日志(所有 BeanPostProcessor 还没有全部实例化就有 Bean 初始化完成)
- 获取所有 BeanPostProcessor 实现类(依赖查找),添加至 BeanFactory 容器中(顺序:PriorityOrdered > Ordered > 无)
- 注意,第 3 步添加的 BeanPostProcessor 如果是 MergedBeanDefinitionPostProcessor 类型,会再次添加(先移除在添加,也就是将顺序往后挪)
- 重新添加 BeanPostProcessor – ApplicationListenerDetector,目的将其移至最后,因为这个后置处理器用于探测 ApplicationListener 类型的 Bean,需要保证 Bean 完全初始化,放置最后比较合适
上述第 4 步里,我的理解 MergedBeanDefinitionPostProcessor 主要是依赖注入的实现,需要保证当前 Spring Bean 的相关初始化工作已完成,然后再进行依赖注入。
通常MergedBeanDefinitionPostProcessor需要BeanDefinition的元信息充分准备。
初始化内建bean messagesource spring事件广播器
- org.springframework.context.support.AbstractApplicationContext#initMessageSource
- Spring国际化相关的操作
初始化内建bean spring事件广播器
- Spring国际化相关的操作
- org.springframework.context.support.AbstractApplicationContext#initApplicationEventMulticaster
应用上下文刷新阶段 - org.springframework.context.support.AbstractApplicationContext#onRefresh
- 子类覆盖方法
- org.springframework.web.context.support.AbstractRefreshableWebApplicationContext#onRefresh
- org.springframework.web.context.support.GenericWebApplicationContext#onRefresh
- org.springframework.web.context.support.StaticWebApplicationContext#onRefresh
这个方法作为一个扩展点
事件监听器注册阶段
- org.springframework.context.support.AbstractApplicationContext#registerListeners
- 添加当前应用上下文所关联的applicationListener Beans
- 添加BeanFactory所注册ApplicationListener Beans
- 广播早期Spring事件
beanfactory初始化完成阶段
- org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization
- BeanFactory关联ConversionService Bean 如果存在
- 添加StringValueResolver对象
- 依赖查找LoadTimeWeaverAwareBean
- BeanFactory临时ClassLoader置为null
- BeanFactory冻结配置
- BeanFactory冻结化非延迟单例Beans
应用上下文刷新完成阶段
Last step: publish corresponding event.
- org.springframework.context.support.AbstractApplicationContext#finishRefresh
- 清除ResourceLoader缓存 – clearResourceCaches
- 初始化LifecycleProcessor对象 – initLifecycleProcessor
- 调用getLifecycleProcessor().onRefresh();
- 发布Spring应用上下文已刷新事件 – ContextRefreshedEvent
在 Spring 中还提供了 Lifecycle 接口, Lifecycle 中包含start/stop方法,实现此接口后Spring会保证在启动的时候调用其start方法开始生命周期,并在Spring关闭的时候调用 stop方法来结束生命周期,通常用来配置后台程序,在启动后一直运行(如对 MQ 进行轮询等)。而ApplicationContext的初始化最后正是保证了这一功能的实现。
spring应用上下文启动阶段
侠义的启动
- org.springframework.context.support.AbstractApplicationContext#start
- 启动LifecycleProcessor
- 发布Spring应用上下文已启动事件 – ContextStartedEvent
停止阶段
- org.springframework.context.support.AbstractApplicationContext#stop
- 停止LifecycleProcessor
- 发布Spring应用上下文已停止事件 – ContextStoppededEvent
关闭阶段
- org.springframework.context.support.AbstractApplicationContext#close
- 状态标识 active(false) closed(true)
- 发布Spring应用上下文已停止事件 – ContextStoppededEvent
- 关闭LifecycleProcessor
- 销毁Spring Beans
- 关闭BeanFactory
- 回调onClose
- 移除shutdown Hook线程(如果曾注册)
为什么说objectfactory提供的是延迟依赖查找
依赖查找注入的bean会被缓存吗
@bean的处理流程是怎样的
beanfactory是如何处理循环依赖的
mybatis与springframework是否如何集成的
发表回复
要发表评论,您必须先登录。