Spring 框架相关知识整理

2016-12-03 Spring

整理 Spring 框架原理等知识点。

框架组成结构

image

容器类型

  • BeanFactory
    • 基础容器
    • 默认采用延迟加载
    • 启动快
  • ApplicationContext
    • 高级容器,继承 ListableBeanFactory 接口
    • 提供各种高级特性
      • 通用方式加载资源 ResourceLoader
      • 事件发布 ApplicationEventPublisher
      • 国际化信息支持 MessageSource
      • 继承实现的上下文环境隔离

Bean 的生命周期

  • 实例化 bean 对象
  • 填充属性,注入依赖
  • 调用 xxxAware 接口
    • 调用 BeanNameAware#setBeanName(String name)
    • 调用 BeanFactoryAware#setBeanDactory(BeanFactory beanFactory)
    • 调用 ApplicationContextAware()#setApplicationContext(ApplicationContext ctx)
      • 将上下文环境传入 bean
  • 调用 BeanPostProcessor#postProcessBeforeInitialization(Object bean, String beanName)
    • 创建成功后进行前置增强处理,如修改 bean,增加某些方法
    • @AutoWired @Value @Inject 等注解就是通过 AutowiredAnnotationBeanPostProcessor 进行的注入
    • 此时 bean 已填充了属性,但未执行任何自定义初始化操作
    • The returned bean instance may be a wrapper around the original
  • 调用 @PostConstruct 标注的方法
  • 调用 InitializingBean#afterPropertiesSet()
  • 调用 xml 里的 init-method
  • 调用 BeanPostProcessor#postProcessAfterInitialization(Object bean, String beanName)
    • 在所有初始化操作完成后执行
  • working…
  • 调用 @PreDestroy 标注的方法
  • 调用 DisposableBean#destroy()
  • 调用 xml 里的 destroy-method

大意如图所示:

image

以上就是主要的 bean 生命周期,实际上还有很多的细节没有列出,比如 BeanClassLoaderAware、EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ServletContextAware、DestructionAwareBeanPostProcessor 等等特别多的过程,某种程度上实现了 Spring 框架的良好可拓展性。(参考官网 doc Combining lifecycle mechanisms

初始化循环依赖

Spring 源码中搜索 circular references.

  • 循环依赖的检测

初始化 bean 时设置一个标志位,在系统加载过程中发现此对象被标记为创建中,则说明是循环依赖。

  • 循环依赖的解决

循环依赖的解决基于引用传递,也就是初始化之后,属性设置可以延后。因此:

属性注入的循环依赖可以解决,构造器注入的循环依赖不可以解决,即构造器初始化不可延后,因为无法创建对象。prototype 类型的循环依赖也无法解决。

分析源码 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry,内部有一个 singletonsCurrentlyInCreation 集合保存了正在创建过程中的 bean 们。同时内部定义了 singletonObjectsearlySingletonObjectssingletonFactories 三个缓存(一二三级缓存)。

  • A 创建过程中需要 B,于是 A 将自己放到 singletonFactories,去实例化 B
  • B 首先从 singletonObjects 中获取 A
    • 找不到说明正在创建,继续从 earlySingletonObjects 中获取 A
      • 找不到就从 singletonFactories 里获取 A
        • 找到后则从 singletonFactories 中移除 A,放到 earlySingletonObjects 中
  • B 创建完成,将自己放入 singletonObjects.
  • A 创建时可直接从 singletonFactories 获取 B

这样就解决了循环依赖。

源码里的设计模式

其实 Spring 将常用设计模式都实现了。

  • 工厂,单例,代理
  • 适配器
  • 装饰者
  • 观察者
  • 策略
  • 模板方法
  • builder 模式
  • 责任链

更多设计模式,可访问:java设计模式整理 查看。

Spring AOP

参考官方文档:Aspect Oriented Programming with Spring

面向切面编程就是将交叉业务逻辑封装成切面,利用 AOP 的功能将切面织入到主业务逻辑中。交叉业务逻辑是指通用的、与主业务逻辑无关的代码,如安全检查、事务、日志等,若不使用 AOP 则会出现交叉业务逻辑与主业务逻辑混合在一起,会使主业务逻辑变的混杂不清。

Spring 默认采用 JDK 动态代理机制实现 AOP,当 JDK 动态代理不可用时(代理类无接口)会使用 CGlib 机制。但 Spring 的 AOP 有一定的缺点,第一只能对方法进行切入,不能对接口,字段,静态代码块进行切入(切入接口的某个方法,则该接口下所有实现类的该方法将被切入);第二同类中的互相调用方法将不会使用代理类。因为要使用代理类必须从 Spring 容器中获取 Bean。第三性能不是最好的。

主要概念和使用注解如下:

  • Aspect
    • 切面,共有功能的实现,横切多个对象
  • Join point
    • 拦截点,程序在运行过程中能够插入切面的地点。例如,方法调用、异常抛出或字段修改等。
  • Advice
    • 切面的具体实现,以目标方法为参照点,根据放置的地方不同,可分为 5 种:
    • @Before
      • 前置增强:在目标方法执行前,增加执行逻辑。
    • @AfterReturning
      • 后置增强:在目标方法执行后,增加执行逻辑(若目标方法抛出异常,则不执行增强逻辑)。
    • @AfterThrowing
      • 后置增强:和 @AfterReturning 增强相对应,只会在目标方法抛出异常时执行。
    • @After
      • 后置增强:增强始终会被执行(不管目标方法是否抛出异常)。
    • @Around
      • 环绕增强:在目标方法执行前和执行后,增加执行逻辑。
  • @Pointcut
    • 切点:简单理解就是一个匹配规则,与切点函数组合使用。
    • 一个 Pointcut 对应多个 Join point。
  • Weaving
    • 将切面应用到目标对象从而创建一个新的代理对象的过程。
    • 这个过程可以发生在编译期、类装载期及运行期。

大致使用流程如下:

  • 使用 @Aspect 注解一个 bean

    @Component
    @Aspect
    @Order(0) //设置切面的优先级  0-2147483647 数字越小优先级越高
    public class MyAopBean{
    
  • 指定切点

    @Pointcut("execution(* com.demo.service..*.*(..))")
    public void myPointcut(){}
    
  • 配置 advice

    @Before("myPointcut()")
    public void doBeforeAdvice(JoinPoint joinPoint){
      // 具体的逻辑,比如安全检查、日志记录、耗时检测等等
    }
    
  • Spring 初始化完切面之后,IOC 容器就会为切面相匹配的 bean 创建代理。
  • 当运行对应的切面业务运行时就会被织入对应逻辑

AOP & 动态代理

打开源码的 org.springframework.aop.framework.DefaultAopProxyFactory 类:

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

  @Override
  public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
      Class<?> targetClass = config.getTargetClass();
      if (targetClass == null) {
        throw new AopConfigException("TargetSource cannot determine target class: " +
            "Either an interface or a target is required for proxy creation.");
      }
      if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
        return new JdkDynamicAopProxy(config);
      }
      return new ObjenesisCglibAopProxy(config);
    }
    else {
      return new JdkDynamicAopProxy(config);
    }
  }

结合上述源码,Spring AOP 生成代理对象的流程为:

  • 创建容器对象的时候,根据切入点表达式拦截的类,生成代理对象。
  • 如果目标对象有实现接口,使用 jdk 代理。如果目标对象没有实现接口,则使用 cglib 代理。
  • 从容器获取代理后的对象,在运行期植入”切面”类的方法。

事务 & AOP & 动态代理

  • jdk 原生事务操作

    jdk 的事务操作包含在 java.sql.Connection 内部:

    setAutoCommit(boolean autoCommit)
    commit()
    rollback()
    
  • Spring 中的事务操作

    Spring 使用 AOP 实现事务。当扫描到 @Transactional 注解或者 tx 标签对应的类和方法,则会创建对应的 AOP 代理对象;在调用对应对象和方法时进行判断进而调用 AOP 代理对象,也就会执行 AOP 对应的逻辑。

    结合 bean 生命周期可以分析到,生成代理对象的过程就是在 BeanPostProcessor 中进行的,实现了对 bean 的加工增强。

  • Spring 事务相关接口

    Spring 的事务实现主要基础为如下几个接口:

    • PlatformTransactionManager 在此基础进行不同实现
      • DataSourceTransactionManager
      • JpaTransactionManager
      • HibernateTransactionManager
    • TransactionDefinition 定义了事务属性
    • TransactionStatus 代表当前事务状态,对当前事务进行控制
  • Spring 事务的特殊性

    要求声明事务事务的方法必须为 public

    默认自动回滚机制;(官网链接:17.5.3 Rolling back a declarative transaction

    In its default configuration, the Spring Framework’s transaction infrastructure code only marks a transaction for rollback in the case of runtime, unchecked exceptions; that is, when the thrown exception is an instance or subclass of RuntimeException. ( Errors will also - by default - result in a rollback). Checked exceptions that are thrown from a transactional method do not result in rollback in the default configuration.

    默认只把 runtime, unchecked exceptions 标记为回滚,即 RuntimeException 及其子类、Error 默认也导致回滚;Checked exceptions 默认不导致回滚。这些规则和EJB是一样的。因此建议设置 rollBackFor

    当在 @Transactional 标注的类内部,普通方法 A 调用已标注 @Transactional 的事务方法 B 时,事务不生效,原因如下:

    • Spring 检测到类标记了事务注解,会创建代理对象,以及这个类的实例 bean
    • 业务代码执行到 A 时,因为 A 未被标记事务,Spring 会调用类的实例 bean 的 A 方法
    • 实例 bean 的 A 方法内部即便调用了 B 方法,也无法执行事务,因为实例 bean 不具备增强的事务功能

    Spring 的事务机制是通过增强的 AOP 代理实现,当业务代码被标注为声明了事务,Spring 才会调用增强的 AOP 代理 bean 去执行事务方法,复杂的事务隔离级别、传播机制都是建立在增强的 AOP 之上,事务线程通过 ThreadLocal 记录关联。

Search

    Post Directory