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比较像,但是比较轻量级,使用规则也比较简单。

  1. 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

此外,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

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生命周期
    4. spring bean作用域
      有哪些作用域:
    5. singleton 默认 一个bean factory一个
    6. prototype 原型作用域 每次依赖查找和注入生成新的对象
    7. request 将SpringBean存在Servelet Request上下文中
    8. session 将SpeingBean存在HttpSession中
    9. 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的动态刷新

    1. Spring Bean生命周期
    2. bean元信息配置阶段:beandefinition配置与扩展
  • BeanDefinition配置
    • 面向资源
    • XML配置
    • Properties资源配置(已废弃)
    • groovy
    • 面向注解
    • 面向API
  • BeanDefinition解析
    • 面向资源的BeanDefinition解析
    • BeanDefinitionReader
    • XML解析器-BeanDefinitionParser
    • 面向注解的BeanFinition解析
    • AnnotatedBeanDefinitionReader(ComponentScan)
      1. 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.

      1. beanfinition合并阶段
  • 父子BeanDefinition合并
    • 当前BeanFactory查找
    • 层次性BeanFactory查找
      org.springframework.beans.factory.support.AbstractBeanFactory#getMergedBeanDefinition(java.lang.String)
      合并后,GenericBeanDefinition变成RootBeanDefinition,并且从parent里继承property value并覆盖。
      存放在BeanFactory的字段mergedBeanDefinitions中

      1. bean class加载阶段: bean classloader能够被替换吗
  • ClassLoader类加载
  • Java Security 安全控制
  • ConfigurableBeanFactory临时ClassLoader
    1. 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
      1. 属性复制前阶段:配置后的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
      1. 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

      1. bean初始化前阶段
  • 方法回调 org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization
    1. bean初始化阶段:postconstruct initializingbean
  • @PostConstruct 标注方法
  • 实现InitializingBean接口的afterPropertiesSet()方法
  • 自定义初始化方法
    1. 初始化后阶段:beanpostprocessor
  • BeanPostProcessor#postProcessAfterInitialization
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization

    1. 初始化完成阶段
  • spring 4.1+ SmartInitializingSingleton#afterSingletonInstantiated
    1. 销毁前阶段:destructionawarebeanpostprocessor
  • DestructionAwareBeanPostProcessor#postProcessBeforeDestruction
    beanFactory.addBeanPostProcessor(new MyDestructionAwareBeanPostProcessor());
    beanFactory.destroyBean(user);
    只代表在容器里销毁 不代表在jvm里销毁

    1. 销毁阶段:predestory disposablebean
  • Bean销毁
    • @PreDestory标注方法
    • 实现DisposableBean接口的destory()方法
    • 自定义销毁方法
      1. 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生命周期与方法映射如上
  1. 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
  2. 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
      处理字段注入
      过程大概是:
  3. 在doCreateBean中会先调用applyMergedeanDefinitionPostProcessors,然后执行populateBean;所以会先调用PostProcessMergedBeanDefinition后执行InstantiationAwareBeanPostProcessor的postProcessProperties
  4. 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 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的方法
        总结
      3. 有多少种注入方式
        构造器 setter 字段 方法 接口回调
      4. 偏好构造器还是setter
        均可使用 推荐构造器 setter注入可选依赖
      5. 依赖注入的来源有哪些
      6. 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外部化配置
      4. 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是否如何集成的

已发布

分类

, ,

作者:

标签

评论

发表回复