文章目录
2018-12-14 Spring复习小结
Spring 概述
Spring优点:
方便解耦,简化开发
支持AOP编程
声明式事务 (待看)
方便程序的测试 (待看)
集成多种框架
通过封装简化了java EE一些API的使用 JDBC, JavaMail,远程调用等
优秀的源码可通过阅读源码提高水平
核心模块
Core框架按照所属功能可分为五个模块:
IoC: 对象解耦,由IoC容器通过配置负责依赖类之间的创建,拼接,管理,获取等工作。核心接口是BeanFactory,Context模块拓展了国际化,Bean生命周期控制,框架事件体系,资源加载透明化等功能。此外该模块还提供了企业级服务支持,如邮件服务,任务调度,JNDI获取,EJB集成,远程访问等。
AOP 待看
数据访问集成 JDBC, Mybatis(待看),声明式事务(待看)
Web及远程操作 各种web工具, 如通过Listener或Servlet初始化Spring容器,将Spring容器注册到Web容器中。
Web及远程访问 Spring MVC, structs等
Spring 4.0新特性
支持java SE 8 java EE 7
核心容器增强
- 添加泛型依赖注入(子类成员变量注入泛型的类型)
- Map,List依赖注入,@order
- @Lazy
- GGLIB代理类增强(待看)
Lambda表达式(需要看个例子)
增加Groovy DSL定义Bean
全面支持REST风格web开发
Java 8.0的时间日期API
重复注解支持(@Scheduled @PropertySource)
Optional<>避免非空检查
IoC 容器
DI注入方法
构造函数注入,setter方法注入,静态工厂方法
依赖配置方法
xml, 注释, java配置类
Java反射机制
可以通过java反射类(ClassLoader,Class,Constructor, Method)获取类的构造函数和方法,以间接地方式创建类对象及调用对象的各项方法。Spring对类的管理正是借由java反射机制实现的。
BeanFactory ; ApplicationContext ; WebApplicationContext
BeanFactory的类体系结构:
ApplicationContext类体系结构
主要实现类:ClassPathXmlApplicationContext, FIleSystemXmlApplicationContext
其他重要功能接口:
ApplicationEventPublisher, 实现了该事件监听接口的Bean可以接收到容器事件,并对事件进行相应处理。在ApplicationContext的抽象实现类AbstractApplicationContext中存在一个ApplicationEventMulticaster,他负责保存所有的监听器,以便在容器产生context事件时通知这些事件的监听者。
MessageSource为应用提供国际化功能
ResourcePatternResolver 装载Spring配置文件
LifeCycle: 该接口提供了start(), stop()功能,ApplicationContext会将start/stop信息传给容器中所有实现了该接口的Bean,达到管理和控制JMX,任务调度等目的
WebApplicationContext类体系结构
父子容器
子容器可以访问父容器的bean,父容器不能访问子容器的bean。
子容器可以有一个和父容器id相同的bean
例如: Spring MVC中,展现层bean位于子容器中, 业务层和持久层bean位于父容器中。这样展现层bean可饮用可以引用业务层和持久层的beans,反之不行。
bean生命周期
Factorybean中bean生命周期
需要注意的点:
- 如果容器注册了org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor 接口,实例化前会先调用接口的 postProcessBeforeInstantiation()方法。实例化后会调用postProcessAfterInstantiation()(此处可在bean马上要初始化和刚初始化后的时候做一些操作,AOP,动态代理等)
- 如果scope=‘prototype’, bean会返回给调用者(调用时初始化bean并交给调用者),调用者负责bean后续生命周期的管理
- 如果scope=‘singleton’(默认), bean会放到Spring IoC容器的缓存池中(容器初始化后bean就已经初始化完成),并将bean的引用返回给调用者(调用时返回给调用者该bean的引用),spring继续对这些bean进行后续的生命管理。容器关闭时,如果bean实现了disposableBean()接口则调用该接口的destroy()方法,可以在此写释放资源,记录日志等操作。也可以通过的destroy-method属性指定Bean的销毁方法
可以将Bean生命周期内的方法归为四类
Bean自身的方法:如调用Bean构造函数实例化Bean、调用Setter方法设置属性值、通过的init-method和destory-method所指定的方法
Bean级生命周期的方法:BeanNameAware,BeanFactoryAware, InitializationBean和DisposableBean,这些接口方法直接由Bean实现
容器级生命周期接口方法: 上图中带* 有InstantiationAwareBeanPostProcessor 和BeanPostProcessor这两个接口实现,一般称他们的实现类为后处理器类,处理器接口一般不由Bean本身实现,他们独立于Bean,
实现类以容器附加装置的形式注册到Spring容器中,当Spring创建任何对象时,这些后处理器类都会发生作用,所以这些后处理器类的影响是全局的。容器可以注册多个后处理器,按顺序调用
工厂后处理器接口:包括ApsectJWeavingEnabler, CustomerAutoWireConfigurer, ConfigurationClassPostProcessor 等,也是容器级的,在应用上下文装配配置文件后立即调用。
####ApplicationContext中bean生命周期
ApplicationContext中的Bean生命周期类似于BeanFactory 不同的是如果Bean实现了org.springframework.context.ApplicationContextAware接口,则会增加一个调用该接口方法setApplicationContext()的步骤。
两者的另一个不同之处在于: AC会利用java反射机制自动识别出配置文件中定义的BeanPostProcessor, InstantiationAwareBeanPostProcessor, BeanFactoryPostProcessor, 并自动注册到应用上下文中。而后者需要通过add方法手动注册。在AC中只需要在配置文件中通过bean定义后处理器即可。
装配Bean
容器成功启动的三个条件
- Spring框架的类包已经放在应用程序的类路径下。
- 应用程序为Spring提供了完备的Bean配置信息
- bean的类都已经放在应用程序的类路径下
Spring在容器初始化时会读取bean的所有配置信息(不同途径),容器内生成一张bean配置注册表,然后根据这张表实例化bean,装配好bean之间的依赖。Bean的配置信息为meta-data,由四部分组成:bean的实现类,属性信息,依赖关系,行为配置(生命周期范围及生命周期内的各过程回掉函数等)。bean的meta-data在容器内对应BeanDefinition形成的bean注册表。目前为止Spring支持基于xml,基于注解,基于java配置类,基于Groovy等动态语言。
基于scheme格式
基于xml的文件格式采用scheme格式,让不同的配置有了自己的命名空间
依赖注入的类型和配置
setter方法
利用java反射机制,检查bean对应的类中是否有setter方法,并根据配置把对应的依赖注入进去。setter的命名规范为:setXxx(),而变量的命名,前两个字母要么全部大写,要么全部小写。全部小写会减少错误概率。
构造函数注入
类型匹配,索引匹配,联合两种方法,自身类型反射
构造函数注入有可能造成循环依赖(死锁),遇到这种情况需要改为setter注入
工厂方法注入
工厂方法是常用的设计模式,也是控制反转和单实例设计的主要实现方法。Spring框架中常用工厂方法。工厂类负责创建一个或多个目标类实例,工厂类通常用接口或抽象类变量的形式返回目标类实例。
- 非静态工厂类
非静态工厂类必须先实例化才能通过factory-method制造对象。需要先配置工厂类bean,在通过factory-bean引用工厂类实例 - 静态工厂类
静态工厂类不需实例化就可以通过工厂方法制造对象,只需factory-bean配置工厂类方法即可。
自动注入
@Autowired可以对类成员,类方法进行标注, @Qualifier限制注入bean的名称
注入参数
- 字面值
- 引用其他bean (bean,local, parent)
- 内部bean
- null
- 级联 (为类内的bean的属性提供注入值)
- 集合和集合合并(继承父bean的集合值)
- depends-on属性可以使某bean在另一bean实例化前先实例化
- 使用idref可让容器在启动时检查引用的正确性
简化配置
p命名空间
自动装配bean
方法注入
如果希望有一个某类中有一个get方法, 每次调用均可以从容其中返回一个新的bean,有一个方法是实现Spring的接口ApplicationContextAware或BeanFactoryAware,如此这般使用ctx.getBean()返回prototype的bean即可。这种配置一般是希望一个singleton Bean获取多个prototype Bean.
其他方法:
lookup方法注入
定义一个接口,声明一个getXxx方法,配置一下该接口,通过lookup-method标签为该方法提供动态实现,再在类中实现该接口,效果和上面的方法等同. 此法需要用到CGLib包
方法替换
用别的bean的方法替换掉这个bean的同名方法。需要配置replaced-method和replacer
bean的继承关系
有多个bean配置大量重复时,可定义抽象bean,再定义子类bean继承抽象bean。
bean的作用域
- singleton:容器中仅存一个实例,容器启动时实例化并存放在容器缓存池,也可以设置lazy-init
- prototype:每次从容器中调用bean时均返回一个新的实例,调用时实例化
- request:每次http请求调用bean时会创建一个新的bean,请求处理完毕后会销毁这个bean
- session:同一个http session共享一个bean,不同的http session使用不同的bean。session结束后,实例被销毁
- globalSession:一个全局session共享一个bean,一般用于Portlet
使用后三种需要配置web容器,Spring容器需要引入RequestContextListener(它实现了ServletRequestContext接口),该监听器监听HTTP请求事件,因此Spring容器能够掌握Web容器中的请求。另,ContextLoaderListener实现了ServletContextListener接口,负责掌握web容器启动和关闭的事件。
如果需要跨作用域的依赖,比如讲作用域为request的bean注入到singleton的bean中,可以使用aop动态代理技术。在配置文件中添加aop:scoped-proxy后,注入的是request bean的动态代理对象,Spring在其中加入一段逻辑判断当前是在哪个线程里,根据线程找到HTTP request,再在这个request域中找到对应的bean注入进去。
Factory Bean
可以通过定制bean的实例化简化配置过程(不需要一大堆setter,自定义getBean)
基于注解
@Component,@Repository,@Service,@Controller: 把POJO转换为容器管理的bean,配置文件中需要生命context命名空间,并定义扫描类包以应用注解的bean,可以使用resource-pattern来过滤需扫描的类包。
@Lazy @Autowired都不会立刻注入属性值,@Lazy需要在两边同时标注
@Scope表明作用域
@PostConstruct, @PreDestroy 前后处理方法
基于java类
@Configuration 标注为定义类
@Bean 标注定义Bean
可以使用@Configuration类启动容器或向容器中注册(注册定义在其中的bean),只需使用.class,获取bean也可以使用@Bean类的.class属性。也可以在xml中配置被扫描,@Configuration类中也可以引用XML配置信息,这一切都十分灵活。
基于Groovy DSL
Groovy是一种基于JVM的动态语言,结合了python,ruby等动态语言的特性。Groovy相比其他配置方法更加灵活,因为里面可以加入大量的的逻辑。配置示例:
技术内幕
Spring容器的构成类和内部流程
Spring的AbstractApplicationContext的refresh()方法定义了Spring容器在加载配置文件后的各项处理过程。代码清单如下:
- 根据配置文件实例化BeanFactory, Spring将配置文件信息装入容器的Bean定义注册表(BeanDefinitionRegistry)。此时仅仅获取了BeanFactory的实例,Bean还没有初始化(remind:BeanFactory负责制造bean)
- 调用工场后处理器:根据反射机制,从BeanFactoryRegistry中找到所有实现了BeanFactoryPostProcessor接口的Bean,并调用其postProcessBeanFactory()接口方法
- 注册Bean后处理器根据反射机制,从BeanFactoryRegistry中找到所有实现了BeanPostProcessor接口的Bean,并将他们注册到容器Bean后处理器的注册表中。
- 初始化消息源:初始化容器的国际化消息资源
- 初始化ApplicationContextEventMulticaster
- 初始化其他特殊的bean
- 注册事件监听器
- 初始化所有的singleton beans(lazy除外),放入Spring缓存池
- 发布上下文刷新事件。事件广播器将事件广播到每个注册的事件监听器中。
BeanDefinition
这个类包括了所有定义bean的metadata,类成员和xml配置文件中的标签可说是一一对应的。BeanDefinition会被注册到BeanDefinitionRegistry中,后续的操作会从 BeanDefinitionRegistry中读取信息。一般情况下BeanDefinition只会在容器启动时被加载或解析,除非容器刷新或重启,这些信息不会发生变化。
创建最终的BeanDefinition有两个步骤
- 利用BeanDefinitionReader读取承载配置信息的Resource。这里面可能是半成品,因为配置文件中可能有占位符变量引用外部文件属性
- 利用容器中注册的BeanFactoryPostProcessor进行后处理。
####实例化bean
和bean实例化有关的类有:
InstantiationStrategy接口 负责实例化bean,相当于new,并不参与Bean属性设置工作。最常用的实例化策略是SimpleInstantiationStrategy,该策略利用三方法(setter,构造,工厂)创建bean实例CglibSubclassingInstantiationStrategy扩展了该策略,利用Cglib库为Bean动态生成子类,在子类中生成方法注入的逻辑,然后用这个动态生成的子类创建bean的实例。
BeanWrapper 设置属性
容器事件
事件体系中的重要概念:
- 事件源:事件的产生者任何一个EventObject都必须有一个事件源
- 事件监听器注册表: 注册事件监听器,一旦有事件发布的时候,就会根据注册表的信息通知所有注册过的监听器。在配置文件中配置监听器你,容器启动时会自动注册该监听器。
- 事件广播器:负责把事件通知给事件监听器。
Spring的事件类结构
Spring框架定义的事件类型如下:
有4个容器事件(启动,刷新,停止,关闭)
两个Web应用相关的事件,当一个http请求被处理后,产生该事件。只有在web.xml中定义了DispatcherServlet时才会产生该事件。它拥有两个子类,分别代表Servlet及Portlet的请求事件。
事件监听器接口
Spring的事件监听器接口都继承自ApplicationListener接口,结构如下:
ApplicationListener接口只定义了一个方法:onApplicationEvent(E event), 该方法接受ApplicationEvent事件对象,在该方法中编写事件的相应处理逻辑。
SmartApplicationListener接口定义了两个方法:
- boolean supportsEventType(Class <? entends ApplicationEvent> eventType): 指定监听器支持那种类型的容器事件
- boolean supportsSourceType(Class<?> sourceType) 指定监听器仅对何种事件源对象做出相应
GenericApplicationListener接口增强了对泛型事件类型的支持。supportsEventType()方法的参数不再仅局限于ApplicationEvent的子类,而是采用可解析类型ResolvableType。泛型的实际类型信息包括获取类级,字段级别,方法返回值,构造器参数及数组组件类型的泛型信息都可以获得。定义的方法:
- boolean supportsEventType(ResolvableType eventType): 指定监听器支持那种类型的容器事件
- boolean supportsSourceType(Class<?> sourceType) 指定监听器仅对何种事件源对象做出相应
事件广播器
继承关系如下:
事件体系的实现
AbstractApplicationContext中完成了事件体系的搭建。AbstractApplicationContext拥有一个applicationEventMulticaster成员变量,提供了容器监听器的注册表。AbstractApplicationContext在refresh()这个容器启动方法中通过以下3个步骤搭建了事件的基础设施
//initialize ApplicationEventMulticaster()
initApplicationEvnetMulticaster();
...
//注册事件监听器(在其他地方可定义多个监听器,此时注册)
registerListeners();
...
//完成刷新并发布容器刷新事件
finishRefresh();