Spring容器文档阅读要点记录

作者: 沐风之境

Spring容器文档阅读

容器代码位于 org.springframework.beansorg.springframework.context包下面


容器的基本的接口

基本接口:BeanFactory,ApplicationContext 是BeanFactory的子接口,提供了企业级应用的功能,拥有更加高级的功能

平常的业务开发基本上都是层ApplicationContext的的实现类开始,因为其提供了功能强大的实现

常用的容器实现类:

Web类:

  • XmlWebApplicationContext
  • AnnotationConfigWebApplicationContext
  • GroovyWebApplicationContext

非Web类:

  • ClassPathXmlApplicationContext
  • GenericXmlApplicationContext
  • AnnotationConfigApplicationContext
  • GenericGroovyApplicationContext
  • FileSystemXmlApplicationContext

BeanDefinition和BeanDefinitionReader

BeanDefinition

BeanDefinition用于存放Bean的定义元信息

Spring容器的的MetaData定义和具体的元信息的定义形式无关,是完全解耦的。支持XML,注解,Java配置类都可以用于定于Spring容器都元信息。Spring2.5开始支持注解,Spring3.0开始支持基于Java配置类的元信息定义

容器中记录了BeanDefinition用于定义Bean的定义和其它一些信息,包括:

  • 包限定的类
  • Bean的名字
  • 作用域
  • 构造器参数
  • Bean行为定义,包含Scope,lifeCycle callbacls等
  • 依赖的其它Bean
  • 其它一些设置信息

常见的实现类:

  • AnnotatedGenericBeanDefinition:可以读取注解来定义元信息的BeanDefinition

  • RootBeanDefinition:能够单独作为一个BeanDefinition,或者是别的BeanDefinition的父定义,但不能作为一个BeanDefinition的子定义

  • ChildBeanDefinition:不能单独作为一个BeanDefinition,必须要依赖一个父BeanDefinition

  • ConfigurationClassBeanDefinition:是一个为Java的配置类生成的过渡性BeanDefinition,用于确定一个Bean是否需要覆盖另一个Bean的情况

  • ScannedGenericBeanDefinition:和AnnotatedGenericBeanDefinition功能差不多,但是增加了扫描.Class的特性

    BeanDefinitionReader

BeanDefinitionReader用于读取不同种类的容器定义形式的元数据的接口

有实现类:

  • GroovyBeanDefinitionReader:通过Groovy的的方式定义元信息
  • PropertiesBeanDefinitionReader:通过Property格式定义元数据
  • XmlBeanDefinitionReader:常用的通过XML的格式定义元数据

Bean的生命周期

  1. 初始化容器
  2. 读取Bean的定义数据,生成BeanDifinition并缓存到BeanFactory中
  3. 完成初始化容器,回调BeanFactoryPostProcessor接口的实现类的postProcessBeanFactory
  4. 容器开始遍历缓存的BeanDifinition集合并实例化非懒加载的Bean(如果是懒加载,则在主动向容器获取的时候开始Bean生命周期)
  5. 实例化Bean,如果是构造器注入依赖则使用指定的构造器执行实例化操作
  6. 设置对象属性,通过反射调用setXXX()方法将依赖的Bean注入
  7. 检查当前的Bean是否实现了各种,*Aware相关接口。如ApplicationContextAwareBeanNameAwareBeanClassLoaderAware等。如果实现就调用相关方法
  8. 将当前Bean的实例作为参数调用一系列的BeanPostProcessor接口的实现类的postProcessBeforeInitialization前置处理方法
  9. 检查当前Bean的是否实现了InitializingBean接口,如果实现了就调用afterPropertiesSet方法
  10. 检查是否有自定义的init-method或者Bean中有被@PostConstruct注解的方法,有就调用
  11. 将当前Bean的实例作为参数调用一系列的BeanPostProcessor接口的实现类的postProcessAfterInitialization后置处理方法
  12. 注册必要的Destruction相关回调接口
  13. 正常使用中
  14. 当Application关闭的时候,检查Bean是否实现了DisposableBean接口,有就调用destroy方法
  15. 检查Bean是否有配置自定义的 destroy-method方法或者@PreDestroy注解的方法,有就调用。另外Spring的容器会自动调用实现了java.lang.AutoCloseable 或者java.io.Closeable的Bean的接口
  16. Application关闭成功

容器与Bean的生命周期回调扩展点

有以下3类扩展方法,用于不同的生命周期节点和不同的作用域

  1. Bean级别扩展点:

    接口:

  2. 普通接口:InitializingBean,DisposableBean

  3. 各种Aware接口:ApplicationContextAware, BeanNameAware, BeanClassLoaderAware等

  4. 特殊的接口:Lifecycle,LifecycleProcessor

其它的一些Aware类型接口都可以用于Bean的实现

注解:@PostConstruct,@PreDestroy

  1. 容器级别扩展点:

    接口:BeanPostProcessor,Ordered,BeanFactoryPostProcessor {接口:beanpostprocessor,ordered,beanfactorypostprocessor}

    BeanPostProcessor

BeanPostProcessor接口用于在Spring容器初始化实例后的一些回调方法,可用于自定义Bean初始化业务逻辑。

Spring-Aop的应用:
在AOP编程中,要实现AOP有两种方式,如果是按接口注入的方式,那么可以使用JDK的动态代理生成代理类。如果按类注入的方式,就需要CGLIB来生成字节码注入的方式来生成代理类。而上面两种方式的共同点都是要向使用者隐瞒真实的类型信息,让使用者认为代理类就是被代理类本身。这个过程就可以通过BeanPostProcessor的回调函数进行操作,生成代理类后直接返回该代理类到容器中,容器会认为该代理类是代理类本身。在注入的时候,就会把代理类注入。

BeanFactoryPostProcessor

BeanFactoryPostProcessor接口用于在容器初始化并加载完所有BeanDefinition后初始化实例之前。在这里们可以自定义的去配置Bean的元信息,也就是BeanDefinition。

Ordered

Ordered接口是用于排序BeanPostProcessor和BeanFactoryPostProcessor的实现类的。如果存在多个BeanPostProcessor和BeanFactoryPostProcessor。那么,Spring会使用Ordered接口来给多个处理器进行排序,按从小到大的顺序进行处理

  1. 工厂Bean:

一类特殊的Bean,其作用完全是为了生产目标Bean而存在,们通过容器获取目标Bean的时候,容器会通过工厂Bean来生产目标Bean。

接口:FactoryBean

FactoryBean是一个特殊的接口,如果一个Bean实现了FactoryBean接口。那么该Bean是目标Bean的工厂Bean。们使用容器去getBean时候,容器会去调用当前工厂Bean的getObject()方法,在getObject()中们可以自定义初始化一个Class的实例,返回的对象作为目标Bean本身。在目标Bean初始化逻辑非常复杂的情况下,FactoryBean接口是一个非常有用的接口,它可以帮助们去自定义一个Bean的初始化过程。这个作用和@Bean注解的方法非常相似,区别在于FactoryBean返回的目标Bean没有SpringBean的生命周期,而@Bean返回的目标Bean是拥有完整的Bean生命周期的。@Bean常常用于@Configuration类中,适合在Application层面的配置。但作为库的程序推荐使用FactoryBean接口的方式。以免和库的使用者的配置发生冲突。

使用注意点:

对于getObject()暴露出去的实例最好在FactoryBean类中保存一下引用关系,因为通过FactoryBean的工厂Bean暴露出去的目标Bean是不存在Spring IoC容器Bean的生命周期的,如果要应用程序关闭后,目标Bean需要做一些资源回收的操作只能在工厂Bean中做,工厂Bean是拥有SpringBean的完整生命周期的,如果工厂Bean中没有缓存对目标Bean的引用关系,那么没有办法做到目标Bean的资源回收。


配置文件的读取

需要自定义读取一个properties配置文件的时候的可以使用两种方式:

上面两种方式的文件都放置在Resources目录下面

注解的方式:

@Configuration
@PropertySource(value = "classpath:/my-custom-app.properties", encoding = "UTF-8")
public class MyBeanConfig {
}

AOP编程

Spring的AOP是一个通用的切面编程封装。它封装了结合JDK动态代理和CGLIB的基于类的代理使用方式。根据不同的情况,Spring会自动的选择适合的切面实现方式和提供统一的使用方式,既通过ProxyFactory代理工厂类来生成代理对象。

Spring中如何生成使用AOP编程?有两种方式:一种是结合IoC容器的实现,另一种是基于编程的实现ProxyFactory类通过编程来生成代理类

基于IoC容器的实现:

1.声明Advice类,使用@Aspect

/* 使用@Aspect注解来声明当前的类是一个需要被织入被代理对象方法的类定义。需要将其注入IoC容器才能正确的被Spring框架处理 */
@Aspect
@Component
public class NotVeryUsefulAspect {

}

2.声明Advice方法

可以使用的Advice:

  • @Before:被代理方法被执行的通知
  • @AfterReturning:在被代理方法执行后通知
  • @AfterThrowing:在被代理方法抛出异常后通知
  • @After:在被代理方法执行后,无论是否返回或者抛出异常都通知
  • @Around:环绕通知,带被代理方法的前后都能被通知
@Aspect
@Component
public class NotVeryUsefulAspect {
  @Around("execution(* tech.mufeng.app.controller.*.(..)")
  public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
    // start stopwatch
    Object retVal = pjp.proceed();
    // stop stopwatch
    return retVal;
  }
}

3.声明切点常用的几种方式:

  1. execution表达式

  2. @annotation表达式

    基于编程的方式使用ProxyFactory方式

public interface BusinessInterface {
  public String doSomething();
}

public class MyBusinessInterfaceImpl implements BusinessInterface {
  public String doSomething() {
    return "hello";
  }
}


import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class AroundInteceptor implements MethodInterceptor {
  @Override
  public Object invoke(MethodInvocation invocation) throws Throwable {
    System.err.println(invocation.getMethod().getName() + "调用之前");
    Object res = invocation.proceed();
    System.err.println(invocation.getMethod().getName() + "调用之后");
    return res;
  }
}


/* 使用JD动态代理 */
public class TestJdkDynamicProxy {
  public static void mian() {
    ProxyFactory factory = new ProxyFactory();
    factory.addInterface(BusinessInterface.class);
    factory.setTarget(new MyBusinessInterfaceImpl());
    factory.addAdvice(new AroundInteceptor());
    MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy();
    tb.doSomething();
  }
}

/* 使用CBLIB生成代理对象 */
public class TestCBLIBProxy {
  public static void mian() {
    ProxyFactory factory = new ProxyFactory();
    factory.setTarget(new MyBusinessInterfaceImpl());
    factory.addAdvice(new AroundInteceptor());
    MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy();
    tb.doSomething();
  }
}

Spring使用的注意点:

  1. @Scope 注解中的proxyModel。

如果一个类的作用域不是singleton单例的情况下,其作为一个单例的一个依赖的情况下,必须制定代理模式为:INTERFACES或TARGET_CLASS。如果当前类是按接口类型注入的,那就可以使用INTERFACES作为代理模式,Spring会基于该接口生产动态代理类。如果当前类没有接口实现,那就需要指定代理模式为TARGET_CLASS,Spring会使用CGLIB生成代理类。因为作为单例类的依赖类,在单例类完成Bean的初始话后,属性就固定了。所以为了每次都能调用到不同Scope的Bean需要代理类来实现代理才能实现。

  1. 自定义@ComponentScan和@Filter的使用

由于扫描一些自定义的类并把他注册到容器中

@Configuration
@ComponentScan(basePackages = "org.example",
    includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
    excludeFilters = @Filter(Repository.class))
public class AppConfig {
  ...
}

  1. @Bean注解的使用注意点,避免过早实例化

    @Bean注解需要在@Configuration声明的类中使用

@Bean注解需要在@Configuration声明的类中使用,特别需要依赖容器中的其它Bean的时候。需特别注意依赖Bean的注入方式。如果在非@Configuration的类中使用@Bean注解方法,那么这样无法保证当前@Bean返回的目标Bean的依赖关系。需要在当前的类内部进行依赖的保证。所以建议@Bean注解需要在@Configuration声明的类中使用。

使用方法参数的方式注入依赖

因为使用@Configuration的类在容器初始化之后会很早被执行,如果通过@Autowired或者@Value使用注入的话可能会导致一些意想不到的异常,建议通过方法的参数注入依赖Bean。

用@Bean注解静态方法来避免过早实例化

还有要注意的是。如果使用@Bean修饰的方法返回的是自定义的BeanPostProcessor或者BeanFactoryPostPorcessor的话,需要使用静态方法来提升方法运行的优先级,因为@Configuration自己本身就是一个Bean。如果说在@Configuration实例化时候,才实例化自定义的BeanPostProcessor或者BeanFactoryPostPorcessor的话可能会导致一些Bean不能被正确的处理。

坏的依赖注入示例

public class CustomBeanPostProcessor implements BeanPostProcessor {
  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    /* some process logic */
    return bean;
  }

  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    /* some process logic */
    return bean;
  }
}


@Configuration
public class MyConfig {
  @Bean
    public CustomBean customBean() {
    return new CustomBean(customBeanDependence);
    }

  @Bean
  public CustomBeanPostProcessor customBeanPostProcessor() {
    return new CustomBeanPostProcessor();
  } 
}

好的依赖注入示例

@Configuration
public class MyConfig {

  @Bean
  public CustomBean customBean(CustomBeanDependence customDependence) {
    return new CustomBean(customDependence);
  }

  /* 使用static来提升执行的优先级,在实例化MyConfig之前就执行自定义BeanPostProcessor的类 */
  @Bean
  public static CustomBeanPostProcessor customBeanPostProcessor() {
    return new CustomBeanPostProcessor();
  } 
}

原文创作:沐风之境

原文链接:https://www.cnblogs.com/mufeng3421/p/13715762.html

更多推荐

更多
  • AWS自动化机器学习-十一、MLSDLC 的持续集成、部署和训练 技术要求,编纂持续集成阶段,管理持续部署阶段,管理持续训练,延伸,构建集成工件,构建测试工件,构建生产工件,自动化持续集成流程,回顾构建阶段,回顾测试阶段,审查部署和维护阶段,回顾应用用户体验,创建新的鲍鱼调查数据,回顾持续训练流程,清
    Apache CN

  • AWS自动化机器学习-六、使用 AWS 步骤函数自动化机器学习过程 技术要求,介绍 AWS 步骤功能,使用 Step 函数 Data Science SDK for CI/CD,建立 CI/CD 渠道资源,创建状态机,解决状态机的复杂性,更新开发环境,创建管道工件库,构建管道应用构件,部署 CI/CD
    Apache CN

  • AWS自动化机器学习-第三部分:优化以源代码为中心的自动化机器学习方法 本节将向您介绍整体 CI/CD 流程的局限性,以及如何将 ML 从业者的角色进一步整合到管道构建流程中。本节还将介绍这种角色集成如何简化自动化过程,并通过向您介绍 AWS Step 函数向您展示一种优化的方法。本节包括以下章节:
    Apache CN

  • AWS自动化机器学习-一、AWS 上的自动化机器学习入门 技术要求,洗钱流程概述,洗钱过程的复杂性,端到端 ML 流程示例,AWS 如何使 ML 开发和部署过程更容易自动化,介绍 ACME 渔业物流,ML 的情况,从数据中获得洞察力,建立正确的模型,训练模型,评估训练好的模型,探索可能的后续步
    Apache CN

  • AWS自动化机器学习-二、使用 SageMaker 自动驾驶器自动化机器学习模型开发 技术要求,介绍 AWS AI 和 ML 前景,SageMaker 自动驾驶器概述,利用 SageMaker 自动驾驶器克服自动化挑战,使用 SageMaker SDK 自动化 ML 实验,SageMaker Studio 入门,准备实验
    Apache CN

  • AWS自动化机器学习-四、机器学习的持续集成和持续交(CI/CD) 四、机器学习的持续集成和持续交CI/CD技术要求,介绍 CI/CD 方法,通过 CI/CD 实现 ML 自动化,在 AWS 上创建 CI/CD 管道,介绍 CI/CD 的 CI 部分,介绍 CI/CD 的 CD 部分,结束循环,采取以部
    Apache CN

  • AWS自动化机器学习-九、使用 Amazon Managed Workflows 为 Apache AirFlow 构建 ML 工作流 技术要求,开发以数据为中心的工作流程,创建合成鲍鱼调查数据,执行以数据为中心的工作流程,构建和单元测试数据 ETL 工件,构建气流 DAG,清理, 在前面的年龄计算器示例中,我们了解了如何通过 ML 从业者和开发人员团队之间的跨职能
    Apache CN

  • AWS自动化机器学习-七、使用 AWS 步骤函数构建 ML 工作流 技术要求,构建状态机工作流,执行集成测试,监控管道进度,设置服务权限,创建 ML 工作流程, 在本章中,我们将从第六章中的 [处继续,使用 AWS 步骤函数自动化机器学习过程。您将从那一章中回忆起,我们正在努力实现的主要目标是简化
    Apache CN

  • AWS自动化机器学习-八、使用 Apache Airflow 实现机器学习过程的自动化 技术要求,介绍阿帕奇气流,介绍亚马逊 MWAA,利用气流处理鲍鱼数据集,配置 MWAA 系统的先决条件,配置 MWAA 环境, 当建立一个 ML 模型时,有一个所有 ML 从业者都知道的基本原则;也就是说,最大似然模型只有在数据被训练时
    Apache CN

  • AWS自动化机器学习-五、自动化 ML 模型的持续部署 技术要求,部署 CI/CD 管道,构建 ML 模型工件,执行自动化 ML 模型部署,整理管道结构,创建 CDK 应用,部署管道应用,查看建模文件,审查申请文件,查看模型服务文件,查看容器构建文件,提交 ML 工件,清理, 在 [第 4
    Apache CN

  • 近期文章

    更多
    文章目录

      推荐作者

      更多