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级别扩展点:

接口:

  • 普通接口:InitializingBean,DisposableBean
  • 各种Aware接口:ApplicationContextAware, BeanNameAware, BeanClassLoaderAware等
  • 特殊的接口:Lifecycle,LifecycleProcessor

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

注解:@PostConstruct,@PreDestroy

2. 容器级别扩展点:

接口: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接口来给多个处理器进行排序,按从小到大的顺序进行处理

3. 工厂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 stopwatchObject retVal = pjp.proceed();// stop stopwatchreturn 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 {@Overridepublic 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需要代理类来实现代理才能实现。

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

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

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

3. @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 {@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {/* some process logic */return bean;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {/* some process logic */return bean;}
}@Configuration
public class MyConfig {@Beanpublic CustomBean customBean() {return new CustomBean(customBeanDependence);}@Beanpublic CustomBeanPostProcessor customBeanPostProcessor() {return new CustomBeanPostProcessor();}
}

好的依赖注入示例

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

spring容器_Spring容器文档阅读要点记录相关推荐

  1. swagger api文档_带有Swagger的Spring Rest API –创建文档

    swagger api文档 使REST API易于使用的真正关键是好的文档. 但是,即使您的文档做得很好,您也需要设置公司流程的权利以正确,及时地发布它. 确保利益相关者按时收到是一回事,但是您也要负 ...

  2. 带有Swagger的Spring Rest API –创建文档

    使REST API易于使用的真正关键是好的文档. 但是,即使您的文档做得不错,您也需要设置公司流程的权利,以正确,及时地发布它. 确保利益相关者按时收到是一回事,但是您也要负责API和文档中的更新. ...

  3. Spring Framework 5.3文档学习(一)

    Spring Framework 5.3文档学习(一) Overview 1.What We Mean by "Spring" 2. History of Spring and t ...

  4. SpringBoot 第十篇: 用spring Restdocs创建API文档

    这篇文章将带你了解如何用spring官方推荐的restdoc去生成api文档.本文创建一个简单的springboot工程,将http接口通过Api文档暴露出来.只需要通过 JUnit单元测试和Spri ...

  5. ExtJS4 API文档阅读(四)——Data

    2019独角兽企业重金招聘Python工程师标准>>> ExtJS4 API文档阅读(四)--Data 数据 Data包负责加载和保存你应用程序中的所有数据,由41个类构成,其中有三 ...

  6. Flink中GroupWindow和OverWindow各自的作用+window体系+文档阅读方式

    GroupWindow和OverWindow各自的作用 Flink Window 作用 完整实例 GroupWindow 对window中的数据按照字段进行分组 完整案例 OverWindow 在整个 ...

  7. Spring Boot API 接口文档 Swagger 入门

    转载自 芋道 Spring Boot API 接口文档 Swagger 入门 摘要: 原创出处 http://www.iocoder.cn/Spring-Boot/Swagger/ 「芋道源码」欢迎转 ...

  8. 在Spring中使用Asciidoctor:使用Spring MVC渲染Asciidoc文档

    Asciidoc是一种基于文本的文档格式,因此如果要将文档提交到版本控制系统中并跟踪不同版本之间的更改,它非常有用. 这使Asciidoc成为编写书籍,技术文档,常见问题解答或用户手册的理想工具. 创 ...

  9. Spring Data JPA - 参考文档 地址

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. Spring Data JPA - 参考文档 文档地址

最新文章

  1. maven生成war包的两种方式
  2. View事件分发机制(源码分析篇)
  3. 2字节十六进制浮点数 qt_Qt中如何实现十六进制“41A4 0000”十六进制转为浮点数20.5呢?...
  4. IOS操作数据库总结
  5. MySQL从入门到精通50讲(八)-MySQL排序分组
  6. php输出1到10的和,php通过排列组合实现1到9数字相加都等于20的方法
  7. QTP工作原理的学习心得
  8. 子div用了float浮动之后,如何撑开父元素,让父元素div自动适应高度的问题
  9. 12.2 asmca fails with 'ORA-00845'
  10. atitit.基于bat cli的插件管理系统.doc
  11. Android多国语言缩写(全)
  12. MySQL8.0无法启动3534的解决方法
  13. HTML页面基本结构浅谈
  14. 【每周论文】Graphene: Packing and Dependency-aware Scheduling for Data-Parallel Clusters(OSDI 2016)
  15. 技术面试中常见的问题以及提升建议
  16. C3AE: Exploring the Limits of Compact Model for Age Estimation
  17. win10系统改win7设置bios方法图文教程
  18. ESP32S3系列--FLASH及PSRAM配置
  19. 数字先锋 | 天翼云牵手中能融合
  20. SQL数据库基本语句

热门文章

  1. 基于JAVA+SpringMVC+Mybatis+MYSQL的药方中医管理系统
  2. XML DOM Object Model in .NET [3/3] - Samples
  3. 1.1.29 加入项目符号后换行文字未对齐
  4. Django学习之十: staticfile 静态文件
  5. apidoc @apiGroup兼容中文
  6. windows常见快捷键
  7. JAVA-5NIO之Selector
  8. 店铺如何用视觉走出差异化?
  9. 所经历的大文件数据导出(后台执行,自动生成)
  10. 【新年假期宅家系列】动漫游戏集中营