> 文章列表 > 【Java基础】day13

【Java基础】day13

【Java基础】day13

day13

一、Spring Bean 生命周期是怎样的?

详细过程分为以下几个步骤:

初始化 Bean

容器通过获取 BeanDefinition 中的信息进行实例化,这一步仅仅是简单的实例化,并没有进行依赖注入。

实例化的对象被包装在 BeanWrapper 对象中,BeanWrapper 提供了设置对象属性的接口,从而避免了使用反射机制来注入属性。

② 设置对象属性(依赖注入)

实例化后的 Bean 仍是一个原生的状态,然后 Spring 根据 BeanDefinition 中的信息进行依赖注入,并且通过 BeanWrapper 提供的设置属性的接口完成依赖注入。

③ 注入 Aware 接口

Spring 会检测该对象是否实现了 xxxAware 接口,并将相关的 xxxAware 实例注入给 bean。

  • BeanNameAware:通过 Bean 的引用来获取 Bean 的 ID,一般业务中很少使用;
  • BeanFactoryAware:获取 Spring BeanFactory 容器,如发布事件等;
  • ApplicationContextAwaer:获取 Spring ApplicationContext 容器;

④ BeanPostProcessor 前置处理

如果 Spring 实现了 BeanPostProcessor 接口,pring 将调用它们的 postProcessBeforeInitialization(Object bean, String beanName)(预初始化)方法,作用是在 Bean 实例创建成功后对进行增强处理,如对 Bean 进行修改,增加某个功能。

⑤ InitializingBean 接口

InitializingBean 接口只有一个函数:afterPropertiesSet(),作用是在 bean 正式构造完成前增加我们自己自定义的逻辑,与前置处理不同,该函数的入参没有 bean 对象, 因为无法处理 bean 对象本身,只能增加一些额外的逻辑。

⑥ init-method 声明

作用同 InitializingBean 接口。

⑦ BeanPostProcessor 后置处理

如果 Spring 实现了 BeanPostProcessor 接口,Spring 将调用它们的 postProcessAfterInitialization(Object bean, String beanName)(预初始化)方法,作用与 4 的一样,只不过初始化方法声明是在 Bean 初始化前执行的,而这个是在 Bean 初始化后执行的,时机不同。

⑧ Bean 初始化完成

经过以上的工作后,Bean 将一直驻留在应用上下文中给应用使用,直到应用上下文被销毁。

⑨ DispostbleBean 接口

DispostbleBean 接口只有一个函数:destroy() 在 bean 被销毁前执行此函数里的逻辑。

⑩ destroy-method 声明

作用同 DispostbleBean 接口。

简单描述分为以下几个步骤:

  • 实例化:通过反射去推断构造函数进行实例化,主要使用的是 doCreateBean() 方法

    • 一般有静态工厂、实例工厂的方式进行实例化
  • 属性赋值:解析自动装配(byName、byType、Constructor、@Autowired)

    • 是 DI 的体现,将依赖的对象/属性值注入到需要创建的对象中
  • 初始化:

    • 调用 xxxAware 回调方法,这个过程是一个渐进过程,只有实现了 Aware 接口才会去调用,依此如下

      • 调用 BeanNameAware 的 setBeanName() 方法
      • 调用 BeanFactoryAware 的 setBeanFactory() 方法
      • 调用 ApplicationContextAware 的 setApplicationContext() 方法
    • Aware 接口实现之后,调用 BeanPostProcessor 的预初始化方法。调用 InitializingBean 的 afterPropertiesSet() 方法,调用定制的初始化方法,调用 BeanPostProcessor 的后初始化方法。(调用初始化生命周期回调,有三种方式,此处是其一)初始化生命周期回调另外两种方式:① XML 文件中指定 <init-method>;② 用注解 @PostConstructor 实现初始化生命周期回调

    • 如果 Bean 实现了 AOP,会在这一步创建动态代理

  • 销毁

    • Spring 容器关闭的时候进行调用

    • 调用销毁生命周期回调(三种方式)

      • 实现 Disposable 接口的 destroy() 方法
      • XML 配置文件中配置 <destroy-method>
      • 使用注解 @PreDestory 创造销毁前置方法

二、Spring Boot 的自动装配原理是什么?

启动类上标注的 @SpringBootApplication 注解,实际上内部会有一个 @EnableAutoConfiguation 注解。

Spring Boot 通过 @EnableAutoConfiguration 开启自动装配,通过 SpringFactoriesLoader 最终加载 META-INF/spring.factories 中的自动配置类实现自动装配,自动配置类其实就是通过 @Conditional 按需加载的配置类,想要其生效必须引入 spring-boot-starter-xxx 包实现起步依赖。

三、BeanDefinition 中保存的是什么?

BeanDefinition 包含了对 Bean 的所有描述信息,是 Spring IoC 容器保存 Bean 的基本数据结构。同时对外提供了获取/修改 Bean 描述的各种方法。BeanDefinition 包装了需要让 IoC 容器管理的 Bean 对象的数据信息:依赖关系,创建方式,加载方式等。

通常项目启动时,IoC 容器启动扫描路径,根据配置将需要容器管理的 Bean 信息装配成 BeanDefinition 对象。在 getBean 时通过反射将 BeanDefinition 中的 beanClass 创建、初始化赋值、依赖注入等操作,通过这种方式让 IoC 容器控制 Bean 的创建、初始化、销毁。
用来存储 Bean 的定义信息,用来决定 Bean 的生产方式。

定义信息包括:Bean 的类别、父类名称、BeanClass 名称、Scope 、是否为懒加载、依赖对象、初始化/销毁方法名称;是否为单例、多例、抽象等。通常定义的 Bean 中,只有 singleton、非 abstract、非 lazy 的 Bean 才会在 IoC 容器被创建的时候加载。

BeanDefinition 的部分源代码如下:

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {// 单例、原型标识符String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;        String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;// 标识 Bean 的类别,分别对应 1.用户定义的 Bean 2.来源于配置文件的 Bean 3.Spring 内部的 Beanint ROLE_APPLICATION = 0;int ROLE_SUPPORT = 1;int ROLE_INFRASTRUCTURE = 2;void setParentName(@Nullable String parentName);void setBeanClassName(@Nullable String beanClassName);void setScope(@Nullable String scope);void setLazyInit(boolean lazyInit);boolean isLazyInit();void setDependsOn(@Nullable String... dependsOn);void setAutowireCandidate(boolean autowireCandidate);boolean isAutowireCandidate();void setPrimary(boolean primary);boolean isPrimary();void setFactoryBeanName(@Nullable String factoryBeanName);void setFactoryMethodName(@Nullable String factoryMethodName);ConstructorArgumentValues getConstructorArgumentValues();default boolean hasConstructorArgumentValues() {return !getConstructorArgumentValues().isEmpty();}MutablePropertyValues getPropertyValues();default boolean hasPropertyValues() {return !getPropertyValues().isEmpty();}void setInitMethodName(@Nullable String initMethodName);void setDestroyMethodName(@Nullable String destroyMethodName);void setRole(int role);void setDescription(@Nullable String description);ResolvableType getResolvableType();boolean isSingleton();boolean isPrototype();boolean isAbstract();// 其他的一些方法和属性...
}

四、BeanName 的生成规则?

Bean 的名称一般是通过使用 BeanNameGenerator 接口来实现,其下面存在两个实现类,包括 AnnotationBeanNameGeneratorDefaultBeanNameGenerator

Bean 将自己的全路径类名作为自己的 Bean 名字。如果没有类名,那就看是否有父 Bean;如果有,假设父 Bean 名字为 hehe,那么就用 hehe$child 作为此子 Bean 的名字;如果没有父 Bean,那就看 Bean 的工厂 Bean 的名字。如果有,假设工厂 Bean 名字为 haha,那么 Bean 的名字就是 haha$created;如果没有工厂,那就报错:既没有自己的类名、也没有父 Bean 类名、也没有工厂 Bean 类名。

Caused by: org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Unnamed bean definition specifies neither ‘class’ nor ‘parent’ nor ‘factory-bean’ - can’t generate beanName.

这种情况,通常是存在没有定义的 Bean,多见于配置文件的 Bean 中。

不管最终用的是哪一个的名字,需要对这个名字进行唯一性检查。如果已经有这个名字存在了,那就在名字后面加上 #1#num)格式的字符,这样每个 Bean 的名字就是唯一的了。# 后面的数字,使用一个 counter 来维护,保持数字在相同的前缀名称下不会发生重复。

Reference

https://blog.csdn.net/dgh112233/article/details/102919658

五、JDK 动态代理和 CGLib 动态代理的实现?

JDK 动态代理使用 Java 反射包中的类和接口实现动态代理的功能,主要涉及三个类:InvocationHandlerMethodproxy

  • JDK 动态代理使用 Java 反射技术进行操作,在生成类上更高效。
  • JDK 动态代理只能对接口进行代理,这是因为 JDK 动态代理生成的代理类,其父类是 Proxy,且 Java 不支持类的多继承。

CGLIB Code Generation Library 通过动态生成一个需要被代理类的子类,即被代理类作为父类。该子类重写被代理类的所有不是 final 修饰的方法,并在子类中采用方法拦截的技术拦截父类所有的方法调用,进而织入横切逻辑。在底层实现上,CGLIB 使用字节码处理框架 ASM,该框架用于转换字节码并生成新的类。之后将新的类字节码文件通过类加载器加载到内存中,通过调用目标类实现动态代理。

  • CGLIB 能够代理接口和普通的类,但是被代理的类不能被 final 修饰,且接口中的方法不能使用 final 修饰。
  • CGLIB 使用 ASM 框架直接对字节码进行修改,使用了 FastClass 的特性。在某些情况下,类的方法执行会比较高效。