Spring 核心原理解析
Spring核心概念
IoC
IoC(Inversation of Control,控制反转)是实现SOLID原则中的依赖倒置原则(Dependency Inversion Principle)的一个思路,即将对象的初始化、对象的销毁等交给容器来管理。
使用IoC有如下好处:
方便管理对象之间复杂的依赖关系
便于进行单元测试,方便切换Mock组件
便于进行AOP操作,对业务侵入性小
DI
DI(Dependency Injection,依赖注入)是实现控制反转的方法,即由容器动态的将对象的依赖注入到该对象中。
AOP
AOP(Aspect Oriented Programming,面向切面编程)是一种编程思想,旨在通过分离横切关注点来提高模块化,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发效率。主要的应用场景有:日志、安全、性能监控、异常处理等。
JavaBean
JavaBean是符合一定规范编写的Java类,便于封装重用。主要有如下规范:
提供默认的构造函数
对属性提供getter跟setter方法
实现Serializable接口
所有属性都应该为private
Spring Bean
Spring Bean是受Spring管理的对象,所有能受Spring容器管理的对象都可以称为Spring Bean。Spring Bean的限制比JavaBean少很多。
Spring运行时核心组件
Spring Core(spring-core)、Spring Beans(spring-beans)跟Spring Context(spring-context)是实现IoC的核心组件。
Spring Core
核心源码解析
Spring Core源码结构如下:
asm:Java字节码处理框架,能够读写字节码,基于字节码层面对代码进行分析和转换。可以用来实现AOP等功能
cglib(Code Generation Library):可用来实现动态代理,动态生成要代理类的子类(对于final方法,无法进行代理);也可以用来实现AOP;底层使用字节码处理框架ASM;比使用Java反射效率更高;JDK动态代理是使用反射来调用委托类的方法,而CGLIB是通过生成FastClass的方法来调用委托类的方法(FastClass的机制是建立委托类方法的索引,通过索引来查找和调用实际的方法)。
core:提供的核心功能:
annotation:读取注解、处理注解
env:提供了两个核心能力:profiles(用于区分环境来实例化Bean,例如development环境或者production环境)跟properties(可以来自properties文件,JVM属性,系统环境变量等)
convert:类型转换工具包;例如可以定义将一个字符串转换为一个实例对象(Converter<S, T>)
io:对资源(Resource)的抽象,定义了获取资源等操作;主要包括ByteArrayResource、ClassPathResource、FileSystemResource、UrlResource等等
Searializer:序列化反序列化工具包
task:同步task跟异步task的核心接口定义跟工具包
type:Class跟Method等的元数据定义(ClassMetadata,MethodMetadata)
lang:条件编译注解,提供注解给静态代码分析工具检查软件缺陷,也可以增强IDE提示,减少潜在Bug
objenesis:对象实例化工具,封装后提供缓存功能
util:工具包
CGLIB源码解析
可以看出,CGLIB依赖于ASM。CGLIB核心代码结构的说明如下:
beans:JavaBean相关的工具类
core:底层字节码操作等功能,大部分都和ASM有关
proxy:创建代理跟方法拦截器相关的功能
reflect:加快反射相关的功能,例如FastClass使用了索引来加快方法调用
transform:在运行期或者编译器转换类等相关功能
util:排序等相关功能
看一个CGLIB动态代理的例子加强理解:
CGLIB生成的代码如下(由于太多,省略了一部分):
首先来看看Spring Core几个核心类之间的关系:
Enhancer:用于创建代理类,不是线程安全的,在调用静态方法 create 的时候内部会创建一个Enhancer实例,会负责指挥如何生成代理类
Callback:主要有以下Callback:
MethodInterceptor(由MethodInterceptorGenerator生成):实现被代理对象的逻辑植入
FixedValue(由FixedValueGenerator生成):只会调用
FixedValue
的loadObject
方法,不会再调用代理目标类的相应方法InvocationHandler(由InvocationHandlerGenerator生成):
invoke
方法传入了被代理的对象、调用的方法跟方法参数等,可以自定义实现LazyLoader(由LazyLoaderGenerator生成):被代理的对象在第一次调用的时候才进行初始化
Dispatcher(由DispatcherGenerator生成):每次调用被拦截方法的时候都会调用一次
loadObject
方法NoOp(由NoOpGenerator生成):声明一个单单例对象,该代理不对被代理类执行任何操作
再来看看上面 Enhancer.create
的一个简化的时序图:
filterConstructors:默认会filter所有private的Constructor
getMethods:会获取被代理类的所有方法并且过虑掉static、private跟final的方法
emitMethods:生成所有被代理类的方法,生成的方法里会调用Callback定义的行为
emitConstructors:生成代理类的构造方法
JDK Proxy vs CGLIB Proxy
JDK动态代理只能对实现了接口的类生成代理,不能针对类;而CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
JDK使用了反射;CGLIB底层采用ASM字节码生成框架生成代理类
核心注解
@AliasFor
:提供注解属性别名
@Order
:1. 定义AspectJ Advice的执行顺序;2. 定义注入集合的元素的顺序。Spring 4.1后可以用@Priority
代替
@NonNull
:不允许为空
@NonNullApi
:表示方法参数和返回值不为null
@NonNullFields
:表示Field不为null
@Nullable
:允许为空
Spring Beans
Spring Core源码结构如下:
factory:JavaBean容器相关的实现代码
annotation:基于注解配置相关的类
config:JavaBean配置相关的类
parsing:JavaBean定义解析的基础类
serviceloader:Service Loader相关功能
support:Bean Factory的默认实现
BeanFactory
:JavaBean容器,用来实例化、配置并管理Beans
propertyeditors:将string类型数据转为其它类型数据,例如 ClassEditor
传入Class Name可以返回一个 Class
对象
support:Spring Beans项目的支持包,主要有排序Beans等功能
BeanInfoFactory
:生成BeanInfo的工厂类
BeanWrapper
:提供分析跟操作JavaBeans的接口
BeanWrapperImpl
: BeanWrapper
的实现
BeanUtils
:JavaBean的静态工具类,用来实例化Bean、检查Bean属性跟复制Bean属性等
BeanDefinition
:Bean的定义
Spring Beans几个核心类之间的关系:
Bean的作用域
Spring Bean的作用域一共有五类:
Singleton:在Spring Container中只有一个实例
Prototype:每次调用都会得到一个新的实例
Request:一个HTTP请求
Session:跟HTTP Session生命周期一样
Global Session:在一个全局的HTTP Session中
*Aware Interfaces:
实现*Aware相关接口能够感知到Spring容器所提供的资源自身的一些属性。主要有
BeanNameAware
:获取在容器中Bean的名字ApplicationContextAware
:获取当前的ApplicationContextBeanFactoryAware
:获取当前的BeanFactoryResourceLoaderAware
:获取ResourceLoader
Bean的生命周期
BeanFactory会加载Bean的定义,然后用这个定义去配置跟创建Bean。Bean的生命周期主要分为初始化跟销毁阶段,在初始化阶段,下面的方法会被依次调用:
BeanNameAware.setBeanName
BeanClassLoaderAware.setBeanClassLoader
BeanFactoryAware.setBeanFactory
EnviromentAware.setEnviroment
EmbeddedValueResolverAware.setEmbeddedValueResolver
ResourceLoaderAware.setResourceLoader
ApplicationEventPublishAware.setApplicationEventPublisher
MessageSourceAware.setMessageSource
ApplicationContextAware.setApplicationContext
ServletContextAware.setServletContext
BeanPostProcessors.postProcessBeforeInitialization
InitializingBean.afterPropertiesSet
BeanPostProcessors.postProcessAfterInitialization
在销毁阶段,以下方法会被依次调用:
DestructionAwareBeanPostProcessors.postProcessBeforeDestruction
DisposableBean.destroy
自定义的销毁方法
配置Bean的主要方式:
基于XML配置
基于注解配置
基于Java类配置
注入Bean的方式:
基于XML注入:ref属性、factory-method属性
基于注解注入:
@Autowired
属性类构造函数setter接口
Bean Id vs Bean Name
在容器中,Bean Id跟Bean Name都能唯一地标识Bean
Bean只能有一个Id,但可以有多个Name
我们可以不设置Id跟Name,这里Spring会使用类的全限定名来设置Id
可以设置多个Name,此时如果不设置Id,那么会使用第一个用作标识符
核心注解
@Autowired
:依赖注入,该注解能用在属性、Setters跟构造函数上,默认是单例;默认按类型来装配,当有多个匹配的时候会按属性名字来装配;可以用在属性,构造函数跟方法上
@Lookup
:方法注入,Spring会生成一个代理对象,每次都能拿到一个新的Bean实例;即是Prototype作用域
@Configurable
:说明该类有Bean的定义(@Bean
)
@Qualifier
:用来提示跟限制需要注入的Bean
@Value
:配置属性默认值,可以支持从配置文件读取
Spring Context
Spring Context源码结构如下:
cache:通用缓存抽象,基于Spring AOP
context:Spring Context核心代码
annotation:Spring Context相关的一些注解
config:Spring Context相关配置
event:Spring Context相关事件
expression:解析expression
support:Spring Context核心实现
weaving:加载时织入
ApplicationContext
:配置应用的核心接口,应用在运行时是只读的
ApplicationContextAware
:实现 ApplicationContextAware
的对象可以获得 ApplicationContext
实例
ApplicationEvent
:应用事件
ApplicationListener
:应用事件监听器
ConfigurableApplicationContext
:提供设置上下文ID,添加监听器,刷新容器等接口
Lifecycle
:定义start/stop等生命周期接口
instrument.classloading:基于ClassLoader提供运行时织入的功能
scheduling:通用任务调度抽象
stereotype:标记分层系统的注解
validation:提供数据绑定跟校验相关功能
Spring Context几个核心类之间的关系:
Spring Context vs Spring BeanFactory
Spring Context继承自BeanFactory,但创建Bean只是Spring Context的其中一部分功能。Spring Context是整个Spring容器的核心,主要有以下功能:
创建、销毁Bean
加载资源文件
发布应用程序事件
解析消息,支持i18n等
获取Bean Definition的方式
通过
@ComponentScan
指定Bean Definition的包路径通过
@Import
导入Bean Definition直接通过
Beanfactory.registerBeanDefinition
注册Bean Definition直接调用
AnnotationConfigRegistry.scan
注册Bean Definition
Spring AOP vs AspectJ AOP
先看一下AOP的几个核心概念:
Aspect:切面,横跨多个类的关注点的模块化
Join Point:连接点,程序运行中的一个点;例如方法执行前或者处理异常
Advice:通知,切面在连接点处执行的动作
Pointcut:切入点,匹配连接点的断言
Introduction:引入,为类声明额外的方法和字段
Weaving:织入,将切面与目标类联系在一起从而创建目标对象
Spring AOP跟AspectJ AOP的不同之处有:
AspectJ AOP支持编译时、加载时跟运行时织入;Spring AOP只支持运行时织入
AspectJ AOP支持的Join Point比Spring AOP多得多;Spring AOP只支持方法执行Join Point,而AspectJ还有方法调用、对象初始化、赋值等等Join Point
核心注解
@Bean
:标记该方法会生成一个由Spring容器管理的Bean,需要跟 @Configuration
一起使用,Spring会使用CGLIB代理该方法。
@ComponentScan
:标记哪个包需要被Spring扫描并且装载入Bean容器
@ComponentScans
:可以指定多个 @ComponentScan
@Conditional
:只有在满足条件的情况下才装载该Bean
@Configuration
:表明该类是Spring的配置类,会有多个 @Bean
@DependsOn
:表明该Bean依赖于另外一个Bean,需要依赖的Bean先实例化
@Description
:对Bean的描述,类似于注释
@EnableAspectJAutoProxy
:启用AspectJ,支持AOP功能
@EnableLoadTimeWeaving
:启用加载时织入(Load Time Weaving)
@Import
:手动导入Bean到Spring容器,或者根据Bean配置导入Bean到Spring容器中
@ImportResource
:导入Bean配置到Spring容器中
@Lazy
:懒惰初始化Bean实例
@Primary
:当有多个Bean满足注入条件时,标记该注解的会优先注入
@Profile
:标记的Profile被激活时才会将Profile中对应的Bean加载到Spring容器中
@PropertySource
:加载指定的属性配置文件
@PropertySources
:加载多个 @PropertySource
@Scope
:Bean的作用域,基本的作用域有:Singleton、Prototype
Caceh相关注解
@EnableCaching
:开启注解配置缓存的功能
@Cacheable
:缓存函数调用的结果,下次调用会从缓存获取
@CacheConfig
:缓存配置
@CachePut
:只缓存函数的结果,下次调用还是会重新执行函数
@CacheEvit
:删除缓存
@Caching
:定义多个@CacheEvit
或者@CachePut
Scheduling相关注解
@EnableScheduling
:开启任务调度的功能
@EnableAsync
:开启异步任务调度的功能
@Scheduled
:被注解的函数将会被Spring Scheduling调度执行,支持 cron
, fixedDelay
, fixedRate
@Schedules
:定义多个@Scheduled
@Async
:被注解的函数将会被异步执行
stereotype相关注解
@Component
:泛指各种组件,通过配置component-scan类会自动注册到Spring容器
@Controller
:Web Controller
@Repository
:在DDD中定义的Repository
@Service
:在DDD中定义的Service
@Bean
vs @Component
Spring Boot
Spring Boot源码结构如下:
spring-boot:Spring Boot 核心类
spring-boot-actuator:监控跟管理Spring应用
spring-boot-actuator-autoconfigure:自动配置Spring Actuator
spring-boot-autoconfigure:Spring Boot自动配置相关功能
spring-boot-cli:Spring Boot命令行工具(Command Line Interface)
spring-boot-dependencies:Spring Boot相关依赖
spring-boot-devtools:Spring Boot开发者工具
spring-boot-parent:Spring Boot的父项目
spring-boot-starters:包含一些常用的Starter
spring-boot-test:Spring Boot测试相关功能
spring-boot-test-autoconfigure:自动配置Spring Boot Test
spring-boot-tools:Spring Boot相关工具
常见Spring Boot问题
Spring Boot的优点
能够创建独立的Spring应用程序
能够方便地嵌入Tomcat、Jetty等,无须部署WAR文件
配置Starter能够方便地集成需要的框架
尽可能自动配置Spring
提供生产就绪功能,如指标、健康检查和外部配置
Spring Boot 的自动装配原理是什么
Spring Boot自动装配是通过 @Configuration
和 @Conditional
注解来工作的,即只有在满足条件时才创建并装载Bean到Spring Context。
如何自定义Spring Boot Starter
Spring Boot Starter是一个写好自动装配逻辑的JAR依赖,我们只要引用这个Starter,那么程序运行时需要的依赖库、配置属性等就会自动装配到我们的运行环境。Spring Boot在启动时会检查JAR包中是否存在META-INF/spring.factories文件,如果存在则会进一步读取其中的 EnableAutoConfiguration
配置项,该配置项包包含了JAR包中的自动装配类。
官方全名规范:spring-boot-starter-${module}
自定义全名规范:${module}-spring-boot-starter
自定义Spring Boot Starter步骤如下:
创建项目,添加spring-boot-autoconfigure依赖
定义Service服务类
定义配置类(
@ConfigurationProperties
),用于封装 application.properties或application.y配置创建自动化配置类,定义好加载条件(
@Conditional
)添加spring.factories
Spring Boot的启动流程
SpringApplication初始化:
推断应用的类型:
REACTIVE
、SERVLET
或者NONE
从
META-INF/spring.factories
加载ApplicationContextInitializer
从
META-INF/spring.factories
加载ApplicationListener
推断并创建 main 方法所属的类
SpringApplication.run:
从
META-INF/spring.factories
加载SpringApplicationRunListener
创建并配置
Environment
根据推断的应用类型创建
SpringContext
从
META-INF/spring.factories
加载SpringBootExceptionReporter
准备 SpringContext,创建
BeanDefinitionLoader
,加载Bean定义刷新 SpringContext 创建Bean并装载入Spring容器
假如Spring容器里有
ApplicationRunner
或者CommandLineRunner
,那么就执行它们
SpringContext vs SpringApplication
SpringApplication是Spring Boot驱动Spring应用上下文的引导类,实质上是为Spring应用创建并初始化Spring上下文
核心注解
@SpringBootConfuguration
:是 @Configuration
的派生注解,作用与 @Configuration
一样
@AutoConfigurationPackage
:将添加该注解的类所在的Package及子Package下的所有组件扫描加载到Spring容器中去
@EnableAutoConfiguration
:开启自动配置的功能,继承自 @AutoConfigurationPackage
;将 META-INF/spring.factories里面配置的所有 EnableAutoConfiguration
的值加到容器里(必须满足一定的条件都会加载到容器里)
@SpringBootApplication
:继承了 @SpringBootConfiguration
、 @EnableAutoConfiguration
、 @ComponentScan
@AutoConfigureAfter
:在加载指定的类之后再加载当前类
@AutoConfigureBefore
:在加载指定的类之前要先加载当前类
@AutoConfigureOrder
:指定加载的顺序
@ImportAutoConfiguration
:继承了 @Import
,用于导入自动配置类
@ConditionalOnBean
:Spring容器中存在该类型Bean时生效
@ConditionalOnClass
:Classpath中存在该类时生效
@ConditionalOnExpression
:满足该表达式时生效
@ConditionalOnJava
:指定的Java版本存在时生效
@ConditionalOnJndi
:指定的JNDI存在时生效
@ConditionalOnMissingBean
:Spring容器中不存在该类型Bean时生效
@ConditionalOnMissingClass
:Classpath中不存在该类时生效
@ConditionalOnNotWebApplication
:不是Web应用环境下生效
@ConditionalOnProperty
:设置该参数时生效
@ConditionalOnResource
:指定的文件存在时生效
@ConditionalSingleCandidate
:Spring容器中该类型Bean只有一个或@Primary的只有一个时生效
@ConditionalOnWarDeployment
:使用WAR部署时生效
@ConditionalOnWebApplication
:Web应用环境下生效
@ConfigurationProperties
:Spring Boot外部配置,可以让使用者设置 .properties文件配置信息
Spring Actuate Endpoint
@Endpoint
:构建HTTP REST API的请求路径
@ReadEndpoint
:GET请求
@WriteOperator
:POST请求
@DeleteOperator
:DELETE请求
Spring Boot Test
@SpringBootTest
:用来做集成测试,会启动一个内置服务并会初始化一个SpringContext
@LocalServerPort
:通过 @SpringBootTest
可以随机监听一个端口,该注解可以将随机监听的端口注入进来
@WebMvcTest
:用来测试Spring MVC
@WebFluxTest
:用来测试WebFlux
@JdbcTest
:用来测试JDBC
@DataRedisTest
:用来测试Redis应用
@TestConfiguration
:继承自 @Configuration
,用来配置Test相关的Bean
@TestComponent
:继承自 @Component
,用来配置Test相关的Bean
@MockBean
:Mock一个Bean用来测试
@MockBeans
:Mock多个Bean用来测试
@SpyBean
:Spy一个Bean用来测试;
@SpyBeans
:Spy多个Bean用来测试
Spring Reactive
Spring Reactive基于Project Reactor,首先来看看Project Reactor的主要特性。
Reactor实现了Reactive编程范式,Reactive是异步非阻塞编程,有以下特点:
响应式的(Responsive)
适应性强的(Resilient)
弹性的(Elastic)
消息驱动的(Message Driven)
响应式编程是一种关注于数据流(Data Streams)和变化传递(Propagation of Changes)的异步编程范式。在传统的编程范式中,一般通过迭代器(Iterator)模式来遍历并处理数据,采用的是拉的方式;而响应式编程采用的是发布者-订阅者模式,即推的方式。但在推送模式中,如果产生数据的速度过快,会使消息订阅者的处理速度无法跟上生产速度,因此,又引入了Backpressure的功能。所以实际上是一种推拉结合的模式。同时,Flux跟Mono还提供了 map
、 filter
、 flatMap
、 zip
、 reduce
等函数式编程API
Mono:表示有0个或者1个元素
Flux:表示有0~N个元素
Reactive vs Callbacks vs Futures
Callbacks(回调)会导致”回调地狱“,让代码变得难以理解和维护
Futures对多个处理的组合不是很友善
Reactive就完美地解决了“回调地狱”跟多个处理的组合问题
如下图所求,在Spring中,有Reactive Stack跟Servlet Stack。
Reactive Stack示例:
参考
Create Proxies Dynamically Using CGLIB Library
Creating a Proxy Object Using cglib
Spring Boot Redis: Ultimate Guide to Redis Cache with Spring Boot 2
Spring AOP in Spring Certification
Building a Reactive RESTful Web Service
版权声明: 本文为 InfoQ 作者【Chank】的原创文章。
原文链接:【http://xie.infoq.cn/article/a710c94ba3416d1845fbb1b3f】。文章转载请联系作者。
评论