Java EE 与 Spring核心概念

Part 1 JAVA EE简介

上个世纪Java主要由Sun公司掌控。
Sun制订了一套发展战略:
Java SE作为java发行的标准版,攻占桌面市场;
Java EE作为企业版,攻占企业服务市场;
Java ME作为移动终端版,攻占移动端市场。

在桌面端,微软凭借VB声名大噪,这种全新的交互方式对开发非常友好。
一个ui组件,就是一个类,类就是一个对象的模板。
组件有属性,对象也有,组件有行为,对象也有。面向对象的思想与UI界面天然适配。
基于UI界面的需求,我们可以推导出一个java面向对象机制的基本需求:

  1. 支持组件默认构造方式:默认提供无参构造方法
  2. 支持组件属性赋值+支持属性可扩展:提供对象的属性默认赋值方式+对象的属性增加机制
  3. 支持组件的本地加载与存储:提供序列化与反序列化机制
    ….
    最后这个【东西】,我们叫做【java bean】。
    java bean为了解决桌面应用的问题而诞生,但是sun公司的桌面战略全面失败了,java bean还在。
    此外,sun在java se中还制定了SPI协议、JNDI协议。这里按下不表。

我们可以看下java ee的设计:
file

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下新建文件指定接口实现类的位置,使得服务使用者可以动态加载服务实现。
      file

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)
    * Spring 3.0 获取注解类型Bean实例列表
      * getBeansWithAnnotstion(Class)
    * 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是否如何集成的

已发布

分类

, ,

来自

标签:

评论

发表回复