为了账号安全,请及时绑定邮箱和手机立即绑定

Spring原理剖析

标签:
Spring

Spring模块

Core(核心容器)

说明

核心容器提供 Spring 框架的基本功能。核心容器的主要组件是BeanFactory,它是工厂模式的实现

BeanFactory使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开

IOC(控制反转模式 )

不创建对象,但是描述创建它们的方式。在代码中不直接与对象和服务连接,但在配置文件中描述哪一个组件需要哪一项服务。容器(在 Spring 框架中是 IOC 容器) 负责将这些联系在一起,保持应用程序对象依赖松散耦合

控制权应用代码转到外部容器控制权的转移,是所谓反转

注入方式

服务需要实现专门的接口,通过接口,由对象提供这些服务,可以从对象查询依赖性

通过 JavaBean 的属性(例如 setter 方法)分配依赖性(常用)

依赖性以构造函数的形式提供,不以 JavaBean 属性的形式公开

org.springframework.beans包,这个包通常不是由用户直接使用,而是由服务器将其用作其他多数功能的底层中介

BeanFactory 接口

它是工厂设计模式的实现,允许通过名称创建和检索对象。BeanFactory也可以管理对象之间的关系

Bean 是被消极加载的,这意味在需要 bean 之前,bean 本身不会被初始化

支持两种对象模型

单例

原型

Bean工厂继承关系(BeanFactory )

ListableBeanFactory

HierarchicalBeanFactory

AutowireCapableBeanFactory

DefaultListableBeanFactory

Bean定义:完整的描述了在 Spring 的配置文件中你定义的  节点中所有的信息,包括各种子节点,在 Spring 的内部他就被转化成 BeanDefinition 对象

Bean解析类

BeanDefinitionReader

BeanDefinitionDocumentReader

XmlBeanDefinitionReader

创建BeanFactory

Ioc 容器实际上就是 Context 组件结合其他两个(Core和BeanFactory)组件共同构建了一个 Bean 关系网

构建的入口就在 AbstractApplicationContext 类的 refresh 方法中

Spring Context(上下文)

说明

Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息

Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能

组件

ApplicationContext

ConfigurableApplicationContext

表示该 Context 是可修改的,也就是在构建 Context 中用户可以动态添加或修改已有的配置信息

下面又有多个子类,其中最经常使用的是可更新的 Context,即 AbstractRefreshableApplicationContext 类

WebApplicationContext

为 web 准备的 Context 他可以直接访问到 ServletContext

Resource

所有的资源都被可以通过 InputStream 这个类来获取,所以也屏蔽了资源的提供者

ResourceLoader 接口负责资源的统一加载

Context 是把资源的加载、解析和描述工作委托给了 ResourcePatternResolver 类来完成,他相当于一个接头人,他把资源的加载、解析和资源的定义整合在一起便于其他组件使用

Spring AOP

说明

通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP

Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中

什么是AOP

AOP(Aspect Orient Programming),也就是面向切面编程

面向对象编程(OOP)是从静态角度考虑程序结构, 

面向切面编程(AOP)是从动态角度考虑程序运行过程 

AOP的作用

处理一些具有横切性质的系统性服务,如事务管理、安全检查、缓存、对象池管理等

AOP的实现原理

AOP 实际上是由目标类的代理类实现的。AOP 代理其实是由AOP 框架动态生成的一个对象,该对象可作为目标对象使用

AOP 代理包含了目标对象的全部方法,但AOP 代理中的方法与目标对象的方法存在差异,AOP 方法在特定切入点添加了增强处理,并回调了目标对象的方法

AOP实现

静态AOP

机制:静态织入

原理:在编译期,切面直接以字节码的形式编译目标字节码文件中

优点:对系统无性能影响

缺点:灵活性不够

动态AOP

机制:动态代理

原理:在运行期,目标类加载后,为接口动态生成代理类,将切面植入到代理类中

优点:相对于静态AOP更加灵活

缺点:切入的关注点需要实现接口。对系统有一点性能影响

代表:JDK动态代理

接口 + InvocationHandler + 目标对象 = 代理

动态字节码生成

机制:在运行期,目标类加载后,动态构建字节码文件生成目标类的子类,将切面逻辑加入到子类

原理:没有接口也可以织入

优点:扩展类的实例方法为final时,则无法进行织入

代表:Cglib动态代理(依赖ASM

接口或类 + MethodInterceptor + 目标对象 = 代理

自定义加载器

机制:在运行期,目标加载前,将切面逻辑加到目标字节码里

原理:可以对绝大部分类进行织入

优点:代码中如果使用了其他类加载器,则这些类将不会被织入

代表:Javassist

字节码转换

机制:在运行期,所有类加载器加载字节码,前进行拦截

原理:可以对所有类进行织入

代表:Javassit +Instrumentation

Spring对AOP的支持

Spring 中AOP 代理由Spring 的IoC 容器负责生成、管理,其依赖关系也由IoC 容器负责管理。因此,AOP 代理可以直接使用容器中的其他Bean 实例作为目标,这种关系可由IoC 容器的依赖注入提供

Spring 默认使用Java 动态代理来创建AOP 代理, 这样就可以为任何接口实例创建代理了。当需要代理的类不是代理接口的时候, Spring 自动会切换为使用CGLIB 代理,也可

强制使用CGLIB

AOP 编程

定义普通业务组件

定义切入点,一个切入点可能横切多个业务组件

定义增强处理,增强处理就是在AOP 框架为普通业务组件织入的处理动作

所以进行AOP 编程的关键就是定义切入点和定义增强处理。一旦定义了合适的切入点和增强处理,AOP 框架将会自动生成AOP 代理,即:代理对象的方法 = 增强处理 + 被代理对象的方法

Spring中AOP的实现

基于Annotation 的“零配置”方式:使用@Aspect、@Pointcut 等Annotation 来标注切入点和增强处理

首先启用Spring 对@AspectJ 切面配置的支持

定义切面Bean

当启动了@AspectJ 支持后,只要在Spring 容器中配置一个带@Aspect 注释的Bean, Spring 将会自动识别该Bean 并作为切面处理

定义@Before 增强处理

定义@AfterReturning 增强处理

定义@AfterThrowing 增强处理

定义@After 增强处理

After 增强处理与AfterReturning 增强处理有点相似,但也有区别

AfterReturning 增强处理处理只有在目标方法成功完成后才会被织入

After增强处理不管目标方法如何结束(保存成功完成和遇到异常中止两种情况),它都会被织入

@Around 增强处理

访问目标方法的参数

定义切入点

所谓切入点,其实质就是为一个切入点表达式起一个名称,从而允许在多个增强处理中重用该名称

一个切入点表达式

一个包含名字和任意参数的方法签名

基于XML 配置文件的管理方式:使用Spring 配置文件来定义切入点和增强点

使用Spring ProxyFactoryBean创建代理

使用 ProxyFactoryBean 来创建 AOP 代理的最重要的优点之一是 IoC 可以管理通知切入点。 这是一个非常的强大的功能,能够实现其他 AOP 框架很难实现的特定的方法。例如,一个通知本身可以引用应用对象(除了目标对象,它在任何 AOP 框架中都可以引用应用对象),这完全得益于依赖注入所提供的可插入性

ProxyFactoryBean的proxyInterfaces属性,指明要代理的接口

ProxyFactoryBean的target属性,指明要代理的目标类 ,这个目标类实现了上面proxyInterfaces属性指定的接口

ProxyFactoryBean的interceptorNames属性,指明要在代理的目标类中插入的Adivce

ProxyFactoryBean还有一个proxyTargetClass属性,如果这个属性被设定为“true”,说明 ProxyFactoryBean要代理的不是接口类,而是要使用CGLIB方式来进行代理,后面会详细讲解使用CGLIB方式来进行代理

IntroductionIntercepter

影响了目标物件的行为定义,直接增 加了目标物件的职责

Spring DAO

说明

JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构

Spring ORM

说明

Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map

所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构

Spring Web 模块

说明

Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成

Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作

Spring MVC 框架

说明

MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现

通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI

Spring事务

概述

事务首先是一系列操作组成的工作单元,该工作单元内的操作是不可分割的,即要么所有操作都做,要么所有操作都不做

ACID

原子性(Atomicity)

即事务是不可分割的最小工作单元,事务内的操作要么全做,要么全不做

一致性(Consistency)

在事务执行前数据库的数据处于正确的状态,而事务执行完成后数据库的数据还是处于正确的状态,即数据完整性约束没有被破坏

如银行转帐,A转帐给B,必须保证A的钱一定转给B,一定不会出现A的钱转了但B没收到,否则数据库的数据就处于不一致(不正确)的状态

隔离性(Isolation)

并发事务执行之间无影响,在一个事务内部的操作对其他事务是不产生影响,这需要事务隔离级别来指定隔离性

持久性(Durability)

事务一旦执行成功,它对数据库的数据的改变必须是永久的,不会因比如遇到系统故障或断电造成数据不一致或丢失

常见问题

丢失更新、

两个事务同时更新一行数据,最后一个事务的更新会覆盖掉第一个事务的更新,从而导致第一个事务更新的数据丢失,这是由于没有加锁造成的

脏读

一个事务看到了另一个事务未提交的更新数据

不可重复读、

在同一事务中,多次读取同一数据却返回不同的结果;也就是有其他事务更改了这些数据

不可重复读的重点是修改

幻读

一个事务在执行过程中读取到了另一个事务已提交的插入数据;即在第一个事务开始时读取到一批数据,但此后另一个事务又插入了新数据并提交,此时第一个事务又读取这批数据但发现多了一条,即好像发生幻觉一样

幻读的重点在于新增或者删除

隔离级别

未提交读(Read Uncommitted)

最低隔离级别,一个事务能读取到别的事务未提交的更新数据,很不安全,可能出现丢失更新、脏读、不可重复读、幻读

提交读(Read Committed)

一个事务能读取到别的事务提交的更新数据,不能看到未提交的更新数据,不可能可能出现丢失更新、脏读,但可能出现不可重复读、幻读

可重复读(Repeatable Read)

保证同一事务中先后执行的多次查询将返回同一结果,不受其他事务影响,可能出现丢失更新、脏读、不可重复读,但可能出现幻读

序列化(Serializable)

最高隔离级别,不允许事务并发执行,而必须串行化执行,最安全,不可能出现更新、脏读、不可重复读、幻读

隔离级别越高,数据库事务并发执行性能越差,能处理的操作越少。因此在实际项目开发中为了考虑并发性能一般使用提交读隔离级别,它能避免丢失更新和脏读,尽管不可重复读和幻读不能避免,但可以在可能出现的场合使用悲观锁乐观锁来解决这些问题

数据库系统的角度来看,锁分为以下三种类型

独占锁(Exclusive Lock)

独占锁锁定的资源只允许进行锁定操作的程序使用,其它任何对它的操作均不会被接受。执行数据更新命令,即INSERT、 UPDATE或DELETE 命令时,SQL Server 会自动使用独占锁。但当对象上有其它锁存在时,无法对其加独占锁。独占锁一直到事务结束才能被释放

共享锁(Shared Lock)

共享锁锁定的资源可以被其它用户读取,但其它用户不能修改它。在SELECT 命令执行时,SQL Server 通常会对对象进行共享锁锁定。通常加共享锁的数据页被读取完毕后,共享锁就会立即被释放

更新锁(Update Lock)

更新锁是为了防止死锁而设立的。当SQL Server 准备更新数据时,它首先对数据对象作更新锁锁定,这样数据将不能被修改,但可以读取。等到SQL Server 确定要进行更新数据操作时,它会自动将更新锁换为独占锁。但当对象上有其它锁存在时,无法对其作更新锁锁定

程序员的角度看,锁分为以下两种类型

悲观锁(Pessimistic Lock)

悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)

乐观锁(Optimistic Lock)

相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受

而乐观锁机制在一定程度上解决了这个问题。乐观锁,大多是基于数据版本( Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如 果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据

数据库事务类型

本地事务

就是普通事务,能保证单台数据库上的操作的ACID,被限定在一台数据库上

分布式事务

涉及两个或多个数据库源的事务,即跨越多台同类或异类数据库的事务(由每台数据库的本地事务组成的),分布式事务旨在保证这些本地事务的所有操作的ACID,使事务可以跨越多台数据库

Java事务类型

JDBC事务:就是数据库事务类型中的本地事务,通过Connection对象的控制来管理事务

JTA事务:JTA指Java事务API(Java Transaction API),是Java EE数据库事务规范, JTA只提供了事务管理接口,由应用程序服务器厂商(如WebSphere Application Server)提供实现,JTA事务比JDBC更强大,支持分布式事务

Java EE事务类型

本地事务:使用JDBC编程实现事务

全局事务:由应用程序服务器提供,使用JTA事务

编程实现

声明式事务: 通过注解或XML配置文件指定事务信息

编程式事务:通过编写代码实现事务

Spring事务使用

说明

结构

DataSource

JdbcDataSource

SessionFactory

EntityManager

TransactionManager

DataSourceTransactionManager

HibernateTransactionManager

JpaTransactionManager

代理机制

Bean和代理

每个Bean有一个代理

所有Bean共享一个代理基类

使用拦截器

使用TX标签配置的拦截器

全注解配置

无论哪种配置方式,一般变化的只是代理机制这部分

5种实现方式

每个Bean都有一个代理

每个Bean用TransactionProxyFactoryBean代理

所有Bean共享一个代理基类

所有Bean都集成TransactionProxyFactoryBean

使用拦截器

拦截器:TransactionInterceptor

根据beanName匹配后进行自动代理:BeanNameAutoProxyCreator

使用tx标签配置的拦截器



全注解

Transactional

Spring扩展

BeanPostProcessor

BeanPostProcessor是Spring容器的一个扩展点,可以进行自定义的实例化、初始化、依赖装配、依赖检查等流程,即可以覆盖默认的实例化,也可以增强初始化、依赖注入、依赖检查等流程

BeanFactory <- AbstractBeanFactory <-AbstractAutowireCapableBeanFactory <- DefaultListableBeanFactory:AbstractAutowireCapableBeanFactory.createBean()负责Bean的初始化到消亡周期

resolveBeanClass(mbd, beanName);解析Bean class,若class配置错误将抛出CannotLoadBeanClassException

mbd.prepareMethodOverrides();准备和验证配置的方法注入,若验证失败抛出BeanDefinitionValidationException

Object bean = resolveBeforeInstantiation(beanName, mbd)

执行InstantiationAwareBeanPostProcessor的实例化的预处理回调方法postProcessBeforeInstantiation(自定义的实例化,如创建代理)

执行InstantiationAwareBeanPostProcessor的实例化的后处理回调方法postProcessAfterInitialization(如依赖注入),如果3.1处返回的Bean不为null才执行

如果3处的扩展点返回的bean不为空,直接返回该bean,后续流程不需要执行

Object beanInstance = doCreateBean(beanName, mbd, args);执行spring的创建bean实例的流程

createBeanInstance(beanName, mbd, args);实例化Bean

instantiateUsingFactoryMethod 工厂方法实例化

构造器实例化

如果之前已经解析过构造器

autowireConstructor:有参调用autowireConstructor实例化

instantiateBean:无参调用instantiateBean实例化

如果之前没有解析过构造器

通过SmartInstantiationAwareBeanPostProcessor 的determineCandidateConstructors 回调方法解析构造器,第二个BeanPostProcessor扩展点,返回第一个解析成功(返回值不为null)的构造器组

autowireConstructor:如果(6.2.2.1返回的不为null,且是有参构造器,调用autowireConstructor实例化

instantiateBean: 否则调用无参构造器实例化

applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName):执行Bean定义的合并

执行MergedBeanDefinitionPostProcessorpostProcessMergedBeanDefinition回调方法,进行bean定义的合并

addSingletonFactory(beanName, new ObjectFactory())

SmartInstantiationAwareBeanPostProcessorgetEarlyBeanReference,当存在循环依赖时,通过该回调方法获取及早暴露的Bean实例

populateBean(beanName, mbd, instanceWrapper);装配Bean依赖

InstantiationAwareBeanPostProcessorpostProcessAfterInstantiation;第五个BeanPostProcessor扩展点,在实例化Bean之后,所有其他装配逻辑之前执行,如果false将阻止其他的InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation的执行和从(9.2到(9.5的执行,通常返回true

autowireByName、autowireByType:根据名字和类型进行自动装配

InstantiationAwareBeanPostProcessorpostProcessPropertyValues,完成其他定制的一些依赖注入,如

AutowiredAnnotationBeanPostProcessor执行@Autowired注解注入

CommonAnnotationBeanPostProcessor执行@Resource等注解的注入

PersistenceAnnotationBeanPostProcessor执行@ PersistenceContext等JPA注解的注入

RequiredAnnotationBeanPostProcessor执行@Required注解的检查等等

checkDependencies:依赖检查

applyPropertyValues:应用明确的setter属性注入

initializeBean(beanName, exposedObject, mbd);执行初始化Bean流程

invokeAwareMethods(BeanNameAware、BeanClassLoaderAware、BeanFactoryAware):调用一些Aware标识接口注入如BeanName、BeanFactory

BeanPostProcessorpostProcessBeforeInitialization:在调用初始化之前完成一些定制的初始化任务,如:

BeanValidationPostProcessor完成JSR-303 @Valid注解Bean验证

InitDestroyAnnotationBeanPostProcessor完成@PostConstruct注解的初始化方法调用

ApplicationContextAwareProcessor完成一些Aware接口的注入(如EnvironmentAware、ResourceLoaderAware、ApplicationContextAware),其返回值将替代原始的Bean对象

invokeInitMethods : 调用初始化方法

InitializingBean的afterPropertiesSet:调用InitializingBean的afterPropertiesSet回调方法

通过xml指定的自定义init-method:调用通过xml配置的自定义init-method

BeanPostProcessorpostProcessAfterInitialization

AspectJAwareAdvisorAutoProxyCreator(完成xml风格的AOP配置()的目标对象包装到AOP代理对象)

AnnotationAwareAspectJAutoProxyCreator(完成@Aspectj注解风格( @Aspect)的AOP配置的目标对象包装到AOP代理对象),其返回值将替代原始的Bean对象

registerDisposableBeanIfNecessary(beanName, bean, mbd) :注册Bean的销毁方法(只有非原型Bean可注册)

单例Bean的销毁流程

DestructionAwareBeanPostProcessor的postProcessBeforeDestruction : 如InitDestroyAnnotationBeanPostProcessor完成@PreDestroy注解的销毁方法注册和调用

DisposableBean的destroy:注册/调用DisposableBean的destroy销毁方法

通过xml指定的自定义destroy-method: 注册/调用通过XML指定的destroy-method销毁方法

Scope的registerDestructionCallback:注册自定义的Scope的销毁回调方法,如RequestScope、SessionScope等

Spring内置的BeanPostProcessor

ApplicationContextAwareProcessor

容器启动时会自动注册

注入那些实现ApplicationContextAware、MessageSourceAware、ResourceLoaderAware、EnvironmentAware、

EmbeddedValueResolverAware、ApplicationEventPublisherAware标识接口的Bean需要的相应实例

在postProcessBeforeInitialization回调方法中进行实施

CommonAnnotationBeanPostProcessor

CommonAnnotationBeanPostProcessor继承InitDestroyAnnotationBeanPostProcessor,当在配置文件有或会自动注册

提供对JSR-250规范注解的支持@javax.annotation.Resource、@javax.annotation.PostConstruct和@javax.annotation.PreDestroy等的支持、

通过@Resource注解进行依赖注入

postProcessPropertyValues:通过此回调进行@Resource注解的依赖注入

用于执行@PostConstruct 和@PreDestroy 注解的初始化和销毁方法的扩展点

postProcessBeforeInitialization()将会调用bean的@PostConstruct方法

postProcessBeforeDestruction()将会调用单例 Bean的@PreDestroy方法(此回调方法会在容器销毁时调用)

AutowiredAnnotationBeanPostProcessor

当在配置文件有或会自动注册

提供对JSR-330规范注解的支持和Spring自带注解的支持

Spring自带注解的依赖注入支持,@Autowired和@Value

determineCandidateConstructors:决定候选构造器

postProcessPropertyValues:进行依赖注入

对JSR-330规范注解的依赖注入支持,@Inject

RequiredAnnotationBeanPostProcessor

当在配置文件有或会自动注册

提供对@Required注解的方法进行依赖检查支持

postProcessPropertyValues:如果检测到没有进行依赖注入时抛出BeanInitializationException异常

PersistenceAnnotationBeanPostProcessor

当在配置文件有或会自动注册

通过对JPA @javax.persistence.PersistenceUnit和@ javax.persistence.PersistenceContext注解进行依赖注入的支持

postProcessPropertyValues: 根据@PersistenceUnit/@PersistenceContext进行EntityManagerFactory和EntityManager的支持

AbstractAutoProxyCreator

AspectJAwareAdvisorAutoProxyCreator和AnnotationAwareAspectJAutoProxyCreator都是继承AbstractAutoProxyCreator

AspectJAwareAdvisorAutoProxyCreator提供对()声明式AOP的支持、

AnnotationAwareAspectJAutoProxyCreator提供对()注解式(@AspectJ)AOP的支持

当使用配置时自动注册AspectJAwareAdvisorAutoProxyCreator

使用时会自动注册AnnotationAwareAspectJAutoProxyCreator

predictBeanType:预测Bean的类型,如果目标对象被AOP代理对象包装,此处将返回AOP代理对象的类型

postProcessBeforeInstantiation:配置TargetSourceCreator进行自定义TargetSource创建时,会创建代理对象并中断默认Spring创建流程

getEarlyBeanReference和postProcessAfterInitialization是二者选一的,而且单例Bean目标对象只能被增强一次,而原型Bean目标对象可能被包装多次

BeanValidationPostProcessor

默认不自动注册,Spring3.0开始支持

提供对JSR-303验证规范支持

根据afterInitialization是false/true决定调用postProcessBeforeInitialization或postProcessAfterInitialization来通过JSR-303规范验证Bean,默认false

BeanPostProcessor如何注册

如ApplicationContextAwareProcessor会在ApplicationContext容器启动时自动注册,而CommonAnnotationBeanPostProcessor和AutowiredAnnotationBeanPostProcessor会在当你使用或配置时自动注册

只要将BeanPostProcessor注册到容器中,Spring会在启动时自动获取并注册

BeanPostProcessor的执行顺序

如果使用BeanFactory实现,非ApplicationContext实现,BeanPostProcessor执行顺序就是添加顺序

如果使用的是AbstractApplicationContext(实现了ApplicationContext)的实现,则通过如下规则指定顺序

PriorityOrdered(继承了Ordered),实现了该接口的BeanPostProcessor会在第一个顺序注册,标识高优先级顺序,即比实现Ordered的具有更高的优先级

Ordered,实现了该接口的BeanPostProcessor会第二个顺序注册

Spring自定义功能标签

XML通常通过DTD、XSD定义,但DTD的表达能力较弱,XSD定义则能力比较强,能够定义类型,出现次数等。自定义标签需要XSD支持,在实现时使用Namespace扩展来支持自定义标签

工作过程

Spring通过XML解析程序将其解析为DOM树,通过NamespaceHandler指定对应的Namespace的BeanDefinitionParser将其转换成BeanDefinition

再通过Spring自身的功能对BeanDefinition实例化对象

制作定义标签

编写XSD文件

META-INF/spring.schemas:配置XSD文件

在解析XML文件时将XSD重定向到本地文件,避免在解析XML文件时需要上网下载XSD文件。通过现实org.xml.sax.EntityResolver接口来实现该功能

META-INF/spring.handlers

指定NamespaceHandler(实现org.springframework.beans.factory.xml.NamespaceHandler)接口,或使用org.springframework.beans.factory.xml.NamespaceHandlerSupport的子类

NamespaceHandler实现中注册标签和解析器

实现配置 BeanDefinitionParser 

解析XML,实例构造想要的Bean为BeanDefinition对象

配置自定义标签并获取对应的Bean

Spring自定义注解

使用Spring扩展机制

Spring PropertyPlaceholderConfigurer配置扩展

通过扩展PropertyPlaceholderConfigurer类自定义Spring取配置值的行为

如通过扩展PropertyPlaceholderConfigurer,内部封装调用ZooKeeper动态配置获取,从而把ZooKeeper的Name Service集成到现有的Spring容器中

Spring设计模式

单例模式

Spring的Bean默认是单例的(Singleton)

工厂模式

简单工厂模式(Simple Factory) 

简单工厂模式又称静态工厂方法模式。重命名上就可以看出这个模式一定很简单。它存在的目的很简单:定义一个用于创建对象的接口

在简单工厂模式中,一个工厂类处于对产品类实例化调用的中心位置上,它决定哪一个产品类应当被实例化, 如同一个交通警察站在来往的车辆流中,决定放行那一个方向的车辆向那一个方向流动一样

组成

工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑。在Java中它往往由一个具体类实现

抽象产品角色:它一般是具体产品继承的父类或者实现的接口。在Java中由接口或者抽象类来实现

具体产品角色:工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现

工厂方法模式(Factory Method) 

工厂方法模式是简单工厂模式的进一步抽象化和推广,工厂方法模式里不再只由一个工厂类决定那一个产品类应当被实例化,这个决定被交给抽象工厂的子类去做

组成

抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现

具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象

抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现

具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现

工厂方法模式使用继承自抽象工厂角色的多个子类来代替简单工厂模式中的“上帝类”。正如上面所说,这样便分担了对象承受的压力;而且这样使得结构变得灵活 起来——当有新的产品(即暴发户的汽车)产生时,只要按照抽象产品角色、抽象工厂角色提供的合同来生成,那么就可以被客户使用,而不必去修改任何已有的代 码。可以看出工厂角色的结构也是符合开闭原则的

可以看出工厂方法的加入,使得对象的数量成倍增长。当产品种类非常多时,会出现大量的与之对应的工厂对象,这不是我们所希望的。因为如果不能避免这种情 况,可以考虑使用简单工厂模式与工厂方法模式相结合的方式来减少工厂类:即对于产品树上类似的种类(一般是树的叶子中互为兄弟的)使用简单工厂模式来实现

简单工厂和工厂方法模式的比较

工厂方法模式和简单工厂模式在定义上的不同是很明显的。工厂方法模式的核心是一个抽象工厂类,而不像简单工厂模式, 把核心放在一个实类上。工厂方法模式可以允许很多实的工厂类从抽象工厂类继承下来, 从而可以在实际上成为多个简单工厂模式的综合,从而推广了简单工厂模式

反过来讲,简单工厂模式是由工厂方法模式退化而来。设想如果我们非常确定一个系统只需要一个实的工厂类, 那么就不妨把抽象工厂类合并到实的工厂类中去。而这样一来,我们就退化到简单工厂模式了

应用场景

当一个类不知道它所必须创建的对象的类的时候

当一个类希望由它的子类来指定它所创建的对象的时候

当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候

抽象工厂模式(Abstract Factory) 

应用场景

一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的

这个系统有多于一个的产品族,而系统只消费其中某一产品族

同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来

系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现

总结

简单工厂模式是由一个具体的类去创建其他类的实例,父类是相同的,父类是具体的

工厂方法模式是有一个抽象的父类定义公共接口,子类负责生成具体的对象,这样做的目的是将类的实例化操作延迟到子类中完成

抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无须指定他们具体的类。它针对的是有多个产品的等级结构。而工厂方法模式针对的是一个产品的等级结构

论是简单工厂模式,工厂方法模式,还是抽象工厂模式,他们都属于工厂模式,在形式和特点上也是极为相似的,他们的最终目的都是为了解耦。在使用时,我们不必去在意这个模式到底工厂方法模式还是抽象工厂模式,因为他们之间的演变常常是令人琢磨不透的。经常你会发现,明明使用的工厂方法模式,当新需求来临,稍加修改,加入了一个新方法后,由于类中的产品构成了不同等级结构中的产品族,它就变成抽象工厂模式了;而对于抽象工厂模式,当减少一个方法使的提供的产品不再构成产品族之后,它就演变成了工厂方法模式

代理模式

对其他对象提供一种代理以控制对这个对象的访问

代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用

代理模式的思想是为了提供额外的处理或者不同的操作而在实际对象与调用者之间插入一个代理对象

代理模式的三个角色

抽象角色:声明真实对象和代理对象的共同接口

代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装

真实角色:代理角色所代表的真实对象,是我们最终要引用的对象

Spring动态代理

JDK动态代理(JdkDynamicAopProxy )

JDK动态代理只能针对实现了接口的类生成代理

接口 + InvocationHandler + 目标对象 = 代理

Cglib动态代理(Cglib2AopProxy )

CGLIB(CODE GENERLIZE LIBRARY)代理是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的所有方法,所以该类或方法不能声明称final的

如果目标对象没有实现接口,则默认会采用CGLIB代理

接口或类 + MethodInterceptor + 目标对象 = 代理

Facade门面(外观)模式

概述

外观模式,我们通过外观的包装,使应用程序只能看到外观对象,而不会看到具体的细节对象,这样无疑会降低应用程序的复杂度,并且提高了程序的可维护性

问题

为了降低复杂性,常常将系统划分为若干个子系统。但是如何做到各个系统之间的通信和相互依赖关系达到最小呢?

解决方案

为子系统中的一组接口提供一个一致的界面, Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。引入外观角色之后,用户只需要直接与外观角色交互,用户与子系统之间的复杂关系由外观角色来实现,从而降低了系统的耦合度

适用性

当你要为一个复杂子系统提供一个简单接口时,子系统往往因为不断演化而变得越来越复杂。大多数模式使用时都会产生更多更小的类

这使得子系统更具可重用性,也更容易对子系统进行定制,但这也给那些不需要定制子系统的用户带来一些使用上的困难。Facade可以提供一个简单的缺省视图

这一视图对大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过Facade层

客户程序与抽象类的实现部分之间存在着很大的依赖性。引入 Facade将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性和可移植性

当你需要构建一个层次结构的子系统时,使用 Facade模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,你可以让它们仅通过Facade进行通讯,从而简化了它们之间的依赖关系

构建模式的组成

外观角色(Facade):是模式的核心,他被客户Client角色调用,知道各个子系统的功能。同时根据客户角色已有的需求预订了几种功能组合

子系统角色(Subsystem classes):实现子系统的功能,并处理由Facade对象指派的任务。对子系统而言,Facade和Client角色是未知的,没有Facade的任何相关信息;即没有指向Facade的实例

客户角色(client):调用Facade角色获得完成相应的功能

效果优点

优点

对客户屏蔽子系统组件减少了客户处理的对象数目使得子系统使用起来更加容易。通过引入外观模式,客户代码将变得很简单,与之关联的对象也很少

实现了子系统与客户之间的松耦合关系,这使得子系统的组件变化不会影响到调用它的客户类,只需要调整外观类即可

降低了大型软件系统中的编译依赖性,并简化了系统在不同平台之间的移植过程,因为编译一个子系统一般不需要编译所有其他的子系统。一个子系统的修改对其他子系统没有任何影响,而且子系统内部变化也不会影响到外观对象

只是提供了一个访问子系统的统一入口,并不影响用户直接使用子系统类

缺点

不能很好地限制客户使用子系统类,如果对客户访问子系统类做太多的限制则减少了可变性和灵活性

在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”

与其他相关模式对比

抽象工厂模式

Abstract Factory式可以与Facade模式一起使用以提供一个接口,这一接口可用来以一种子系统独立的方式创建子系统对象。 Abstract Factory也可以代替Facade模式隐藏那些与平台相关的类

中介模式

Mediator模式与Facade模式的相似之处是,它抽象了一些已有的类的功能。然而,Mediator的目的是对同事之间的任意通讯进行抽象,通常集中不属于任何单个对象的功能

Mediator的同事对象知道中介者并与它通信,而不是直接与其他同类对象通信。相对而言,Facade模式仅对子系统对象的接口进行抽象,从而使它们更容易使用;它并不定义新功能,子系统也不知道Facade的存在

Adapter模式

适配器模式是将一个接口通过适配来间接转换为另一个接口

外观模式的话,其主要是提供一个整洁的一致的接口给客户端

总结

根据“单一职责原则”,在软件中将一个系统划分为若干个子系统有利于降低整个系统的复杂性,一个常见的设计目标是使子系统间的通信和相互依赖关系达到最小,而达到该目标的途径之一就是引入一个外观对象,它为子系统的访问提供了一个简单而单一的入口

外观模式是“迪米特法则”的体现,通过引入一个新的外观类可以降低原有系统的复杂度,外观类充当了客户类与子系统类之间的“第三者”,同时降低客户类与子系统类的耦合度。外观模式就是实现代码重构以便达到“迪米特法则”要求的一个强有力的武器

外观模式要求一个子系统的外部与其内部的通信通过一个统一的外观对象进行,外观类将客户端与子系统的内部复杂性分隔开,使得客户端只需要与外观对象打交道,而不需要与子系统内部的很多对象打交道

外观模式从很大程度上提高了客户端使用的便捷性,使得客户端无须关心子系统的工作细节,通过外观角色即可调用相关功能

不要试图通过外观类为子系统增加新行为 ,不要通过继承一个外观类在子系统中加入新的行为,这种做法是错误的。外观模式的用意是为子系统提供一个集中化和简化的沟通渠道,而不是向子系统加入新的行为,新的行为的增加应该通过修改原有子系统类或增加新的子系统类来实现,不能通过外观类来实现

模式扩展

一个系统有多个外观类

在外观模式中,通常只需要一个外观类,并且此外观类只有一个实例,换言之它是一个单例类。在很多情况下为了节约系统资源,一般将外观类设计为单例类。当然这并不意味着在整个系统里只能有一个外观类,在一个系统中可以设计多个外观类,每个外观类都负责和一些特定的子系统交互,向用户提供相应的业务功能

不要试图通过外观类为子系统增加新行为

不要通过继承一个外观类在子系统中加入新的行为,这种做法是错误的。外观模式的用意是为子系统提供一个集中化和简化的沟通渠道,而不是向子系统加入新的行为,新的行为的增加应该通过修改原有子系统类或增加新的子系统类来实现,不能通过外观类来实现

外观模式与迪米特法则

外观模式创造出一个外观对象,将客户端所涉及的属于一个子系统的协作伙伴的数量减到最少,使得客户端与子系统内部的对象的相互作用被外观对象所取代。外观类充当了客户类与子系统类之间的“第三者”,降低了客户类与子系统类之间的耦合度,外观模式就是实现代码重构以便达到“迪米特法则”要求的一个强有力的武器

抽象外观类的引入

外观模式最大的缺点在于违背了“开闭原则”

当增加新的子系统或者移除子系统时需要修改外观类,可以通过引入抽象外观类在一定程度上解决该问题,客户端针对抽象外观类进行编程。对于新的业务需求,不修改原有外观类,而对应增加一个新的具体外观类,由新的具体外观类来关联新的子系统对象,



作者:Java高级架构
链接:https://www.jianshu.com/p/3d45b2b5fe39


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消