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
1. 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比较像,但是比较轻量级,使用规则也比较简单。
## 2. 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
![file](https://jeffdingzone.com/wp-content/uploads/2023/01/image-1674561098069.png)
此外,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中定义,那么这些方法的执行顺序是怎样的?
1. PostConstruct 2. InitializingBean afterPropertiesSet 3. 自定义
自定义初始化方法其实是放在beandefinition里,所以可以直接调Spring的api
![file](https://jeffdingzone.com/wp-content/uploads/2023/01/image-1674561117678.png)
Bean的延迟初始化:
* xml: lazy-init
* java注解 @Lazy
思考:某个bean被定义为延迟初始化的时候,那么Spring容器返回的对象和非延迟的对象存在怎样的差异?
非延迟:在应用上下文启动时完成
延迟:依赖查找触发了初始化
Bean的销毁方法:
* @PreDestory方法
* 实现DIsposableBean接口的desctory()方法
* 自定义销毁方法: destory
假设同时定义,执行顺序:
bean如何垃圾回收:
1. 关闭spring容器
2. 执行GC
3. 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生命周期
3. spring bean作用域
有哪些作用域:
1. singleton 默认 一个bean factory一个
2. prototype 原型作用域 每次依赖查找和注入生成新的对象
3. request 将SpringBean存在Servelet Request上下文中
4. session 将SpeingBean存在HttpSession中
5. 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的动态刷新
4. Spring Bean生命周期
1. bean元信息配置阶段:beandefinition配置与扩展
* BeanDefinition配置
* 面向资源
* XML配置
* Properties资源配置(已废弃)
* groovy
* 面向注解
* 面向API
* BeanDefinition解析
* 面向资源的BeanDefinition解析
* BeanDefinitionReader
* XML解析器-BeanDefinitionParser
* 面向注解的BeanFinition解析
* AnnotatedBeanDefinitionReader(ComponentScan)
2. 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.
3. beanfinition合并阶段
* 父子BeanDefinition合并
* 当前BeanFactory查找
* 层次性BeanFactory查找
org.springframework.beans.factory.support.AbstractBeanFactory#getMergedBeanDefinition(java.lang.String)
合并后,GenericBeanDefinition变成RootBeanDefinition,并且从parent里继承property value并覆盖。
存放在BeanFactory的字段mergedBeanDefinitions中
4. bean class加载阶段: bean classloader能够被替换吗
* ClassLoader类加载
* Java Security 安全控制
* ConfigurableBeanFactory临时ClassLoader
5. 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
6. 属性复制前阶段:配置后的propertyValues还有机会修改吗
* 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
7. aware接口回调阶段:众多aware接口回调的顺序是安排的?
隶属于Bean的初始化阶段
* 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
8. bean初始化前阶段
* 方法回调 org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization
9. bean初始化阶段:postconstruct initializingbean
* @PostConstruct 标注方法
* 实现InitializingBean接口的afterPropertiesSet()方法
* 自定义初始化方法
10. 初始化后阶段:beanpostprocessor
* BeanPostProcessor#postProcessAfterInitialization
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization
11. 初始化完成阶段
* spring 4.1+ SmartInitializingSingleton#afterSingletonInstantiated
12. 销毁前阶段:destructionawarebeanpostprocessor
* DestructionAwareBeanPostProcessor#postProcessBeforeDestruction
beanFactory.addBeanPostProcessor(new MyDestructionAwareBeanPostProcessor());
beanFactory.destroyBean(user);
只代表在容器里销毁 不代表在jvm里销毁
13. 销毁阶段:predestory disposablebean
* Bean销毁
* @PreDestory标注方法
* 实现DisposableBean接口的destory()方法
* 自定义销毁方法
14. 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生命周期与方法映射如上
## 5. 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
延迟依赖查找
* 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
6. 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 Collection qualifiedUsers;
@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
处理字段注入
过程大概是:
1. 在doCreateBean中会先调用applyMergedeanDefinitionPostProcessors,然后执行populateBean;所以会先调用PostProcessMergedBeanDefinition后执行InstantiationAwareBeanPostProcessor的postProcessProperties
2. 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
1. @Autowired注解可以作为元注解,自定义一个被标记的注解即可
2. 或者完全自定义,需要定制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的方法
总结
1. 有多少种注入方式
构造器 setter 字段 方法 接口回调
2. 偏好构造器还是setter
均可使用 推荐构造器 setter注入可选依赖
3. 依赖注入的来源有哪些
7. 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
思考
1. 依赖注入和查找的来源是否相同
显然不同
2. 单例对象能否在ioc容器启动后注册
可以。
单例对象和BeanDefinition不同,BeanDefinition会被ConfigurableListableFactory#freezeConfiguration方法影响,从而冻结注册,但是单例对象不存在这个限制
严格来说,这个冻结操作其实不是严格的限制,也可以注册bd,但是在启动完成后再注册bd已经没有意义了,不如直接注册单例对象
3. Spring注入的来源
bd 单例对象 resolable dependency @Value外部化配置
8. 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对象 也就是一部分来源框架自定义,一部分来源开发者自定义
1. 处理当前上下文的BeanDefinitionRegistryPostProcessor-框架内部
2. 处理beanFactory中的BeanDefinitionRegistryPostProcessor-开发者自定义,优先处理@PriorityOrdered,@Ordered,常规顺序
3. 处理当前上下文的BeanFactoryPostProcessor
4. 处理beanFactory中的BeanFactoryPostProcessor-if else后完成处理
到这里beanFactory的初始化操作基本完成。
beanfactory注册beanprocessor阶段
* org.springframework.context.support.AbstractApplicationContext#registerBeanPostProcessors方法
* 注册PriorityOrdered类型的BeanPostProcessor Beans
* 注册Ordered类型的BeanPostProcessor Beans
* 注册普通BeanPostProcessor Beans
* 注册MergedBeanDefinitionPostProcessor Beans
* 注册ApplicationListenerDetector对象
1. 获取所有 BeanPostProcessor 类型的 beanName
2. 添加 BeanPostProcessor - BeanPostProcessorChecker,用于打印日志(所有 BeanPostProcessor 还没有全部实例化就有 Bean 初始化完成)
3. 获取所有 BeanPostProcessor 实现类(依赖查找),添加至 BeanFactory 容器中(顺序:PriorityOrdered > Ordered > 无)
4. 注意,第 3 步添加的 BeanPostProcessor 如果是 MergedBeanDefinitionPostProcessor 类型,会再次添加(先移除在添加,也就是将顺序往后挪)
5. 重新添加 BeanPostProcessor - ApplicationListenerDetector,目的将其移至最后,因为这个后置处理器用于探测 ApplicationListener 类型的 Bean,需要保证 Bean 完全初始化,放置最后比较合适
上述第 4 步里,我的理解 MergedBeanDefinitionPostProcessor 主要是依赖注入的实现,需要保证当前 Spring Bean 的相关初始化工作已完成,然后再进行依赖注入。
通常MergedBeanDefinitionPostProcessor需要BeanDefinition的元信息充分准备。
初始化内建bean messagesource spring事件广播器
* org.springframework.context.support.AbstractApplicationContext#initMessageSource
* Spring国际化相关的操作
初始化内建bean 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是否如何集成的
发表回复
要发表评论,您必须先登录。