前言
spring源码包含很多个模块,如sping-core,sping-beans,sping-context,spring-aop,spring-web,spring-webmvc,spring-webflux等
在之前研究源码时,往往都是统一视为spring,而忽略了各个模块所承担的职责,今天决定彻底梳理一下spring的架构脉络,好对spring有个全局的认知
架构
下面这张图来源于Spring 4.3.27版本的doc文档中
图片有些过时了,大体还是差不多,今天从web开发角度,由里向外,分析以下几个较重要的模块
Core: Beans, Core,Context,AOP
Web: Web, WebMVC,WebFlux
Data Access:JDBC
Boot: Boot的整合和自动配置
spring-core
spring-core这个包是spring通用性代码的封装,单独拿出来并没有什么作用,一般我们自己开发项目也会有一个通用的包或模块,spring-core可看做整个spring架构中的通用模块
spring-beans
从名字可以看出:Beans,即很多的bean,我们常说spring是一个bean容器、一个IOC容器,这其实就是spring-beans中BeanFactory
的功劳了,所以spring-beans是spring框架的核心基础
spring-beans中,把bean的生产者抽象为bean工厂(BeanFactory),bean生产的图纸抽象为bean定义(BeanDefinition),并实现了一个默认的bean工厂:DefaultListableBeanFactory
,使用它我们可以注册一个bean定义并最终获取到bean,从创建到存储到销毁的生命周期都交给了BeanFactory
bean在BeanFactory的存储方式就是传说中的三级缓存了,实际上也就是三个map而已
引入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
bean容器
测试一下bean容器的功能,注册一个bean定义并获取
创建一个简单的User类
public class User {
public void say() {
System.out.println("hello world");
}
}
@Test
public void container() {
// bean工厂
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 注册用户bean定义
beanFactory.registerBeanDefinition("user", new RootBeanDefinition(User.class));
// 获取
User bean = beanFactory.getBean(User.class);
// 调用方法
bean.say(); // 输出hello world
}
获取bean的方式,可以通过注册时的名字获取,也可以如例子中的getByType
后置处理器
spring-beans中抽象了可配置的BeanFactory: ConfigurableBeanFactory
,这种BeanFactory是支持添加后置处理器的来配置bean的创建步骤的,而DefaultListableBeanFactory也是一种ConfigurableBeanFactory
bean的后置处理有很多种,可以定制不同类型的后置处理器,以便在bean生命周期的不同阶段自定义步骤
比如我们可以实现InstantiationAwareBeanPostProcessor
(实例化感知后置处理器),在bean的生产过程注入属性值
public void postProcessor() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 注册用户bean定义
beanFactory.registerBeanDefinition("user", new RootBeanDefinition(User.class));
// 注册汽车的bean定义
beanFactory.registerBeanDefinition("car", new RootBeanDefinition(Car.class));
// 添加后置处理器
beanFactory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() {
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
throws BeansException {
if (bean instanceof User) { // 给user的bean填充属性
((User) bean).setCar(beanFactory.getBean(Car.class));
}
return pvs;
}
});
// 获取bean
User bean = beanFactory.getBean(User.class);
// 调用属性的方法
bean.getCar().run(); // running
}
最终完成了Car的bean注入到User的属性中,可以继续优化一下这个后置处理器: 写一个注解,然后检查某类是否有某个属性有这个注解,如果有就通过getBeanByType的方式设置属性值,这就是我们使用@Autowire
的逻辑
而这个后置处理器,spring-beans包中也给我们封装好了,即AutowiredAnnotationBeanPostProcessor
依赖注入
DefaultListableBeanFactory本身支持使用设置AutowireMode的方式完成自动装配,但这种方式粒度太大,一般我们需要根据用户的配置按需自动注入,这种情况就需要使用后置处理器,spring-beans包中已为我们封装好了: AutowiredAnnotationBeanPostProcessor
,使用它我们可以用注解@Autowired
来配置属性自动注入
@Autowired
private Car car;
@Test
public void autowiredAnnotation() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 注册用户bean定义
beanFactory.registerBeanDefinition("user", new RootBeanDefinition(User.class));
// 注册汽车的bean定义
beanFactory.registerBeanDefinition("car", new RootBeanDefinition(Car.class));
// 添加注解方式自动装配后置处理
AutowiredAnnotationBeanPostProcessor postProcessor = new AutowiredAnnotationBeanPostProcessor();
postProcessor.setBeanFactory(beanFactory);
beanFactory.addBeanPostProcessor(postProcessor);
// 获取bean
User bean = beanFactory.getBean(User.class);
// 调用属性的方法
bean.getCar().run(); // running
}
FactoryBean
bean工厂DefaultListableBeanFactory
还支持FactoryBean,使用这种方式BeanFactory只作为一个容器存储,而实际创建的过程用户完全自定义,试一下
定义一个生产Car的工厂,实现FactoryBean
public class CarFactory implements FactoryBean<Car> {
@Override
public Car getObject() throws Exception { // 生产Car
return new Car();
}
@Override
public Class<?> getObjectType() { // 生产类型
return Car.class;
}
}
测试一下
public void factoryBean() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
RootBeanDefinition carFactoryBeanDefinition = new RootBeanDefinition(CarFactory.class);
// 注册工厂bean定义
beanFactory.registerBeanDefinition("carFactory", carFactoryBeanDefinition);
Car bean = beanFactory.getBean(Car.class);
bean.run(); // running
}
小结
spring-beans包是spring框架的基础,提供了BeanFactory,其中一个重要的实现即DefaultListableBeanFactory
,使用它我们可以实现一个bean容器,通过后置处理器或FactoryBean的方式我们可以控制特殊bean的自定义生产过程,并且spring-beans内部写好了一个注解方式的依赖注入的后置处理器,使用它可以方便实现依赖注入
spring-aop
spring-aop是面向切面编程的支持模块,相比于spring-beans是一个基础模块,spring-aop则是一个可选模块,如果不需要面向切面编程,完全可以不使用
spring-aop基于spring-beans,它主要提供了一系列特殊的BeanFactory后置处理器,使用这种特殊后置处理器,就可以很轻松实现面向切面编程,例如:
- AbstractAutoProxyCreator
- AbstractAdvisorAutoProxyCreator
- AnnotationAwareAspectJAutoProxyCreator
AbstractAutoProxyCreator
比如还是上面的例子,我们希望给User和Car的方法执行前都打印一下方法名,使用AOP可以如下处理:
加入aop依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
此时我们不需要修改User或Car的方法,只需要实现一个AOP创建器即可,即实现AbstractAutoProxyCreator
,他就是spring-aop模块提供的特殊的专做AOP的后置处理器
@Test
public void autoProxyCreator() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 注册用户bean定义
beanFactory.registerBeanDefinition("user", new RootBeanDefinition(User.class));
// 添加aop拦截后置处理
beanFactory.addBeanPostProcessor(new AbstractAutoProxyCreator() {
@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource customTargetSource) throws BeansException {
return new Object[]{
(MethodBeforeAdvice) (method, args, target) -> {
System.out.println("the method is " + method.getName());
}
};
}
});
// 调用User方法
beanFactory.getBean(User.class).say();
}
拦截方式即方法执行前打印一下方法名, 输出如下
AnnotationAwareAspectJAutoProxyCreator
同时,spring-aop还支持AspectJ语法,对应的后置处理器是AnnotationAwareAspectJAutoProxyCreator
使用这种后置处理器,就可以在BeanFactory中查找按AspectJ语法写出的拦截器,并转换为spring-aop的Advisor以进行方法拦截,当然需要引入AspectJ的相关依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
按AspectJ语法写一个拦截器,后续加入到bean容器
@Aspect
public class ShowMethodAspect {
@Pointcut("execution(* me.pq.spring.beans.*.*(..))")
public void showMethod() {
}
@Before("showMethod()")
public void before(JoinPoint joinPoint) {
System.out.println(joinPoint); // 打印下方法即可
}
}
拦截器加入到bean容器,并注册该后置处理
@Test
public void annotationAwareAspectJAutoProxyCreator() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 注册用户bean定义
beanFactory.registerBeanDefinition("user", new RootBeanDefinition(User.class));
// 注册AspectJ语法拦截器至bean容器
beanFactory.registerBeanDefinition("showMethodAspect", new RootBeanDefinition(ShowMethodAspect.class));
// 初始化AnnotationAwareAspectJAutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator aopCreator = new AnnotationAwareAspectJAutoProxyCreator();
// 因为实现方式是取bean容器查找,所以必须指定beanFactory
aopCreator.setBeanFactory(beanFactory);
// 添加到后置处理
beanFactory.addBeanPostProcessor(aopCreator);
// 调用User方法
beanFactory.getBean(User.class).say();
}
最终输出如下
小结
spring-aop就是给spring-beans提供了一系列的aop相关功能的后置处理器
spring-context
有了spring-beans,我们可以实现一个IOC容器,有了spring-aop,我们可以在IOC的基础上实现AOP,那么spring-context模块又有什么用?
spring-context抽象出了ApplicationContext
, 代表应用上下文,相比spring-beans是一个bean的生命周期管理的单纯工具,spring-context是管理整个spring应用,并同时维护着内部的BeanFactory,承担的责任必然更多,比如对事件的支持和国际化的支持,最重要的是我们不需要手动注册bean定义了,也不需要手动添加后置处理器了
总之,有了ApplicationContext,我们不需要再直接和BeanFactory打交道,这个工作交给了ApplicationContext,我们只需用更简易的方式操作ApplicationContext即可
ApplicationContext的实现也有很多,最具代表性的是AnnotationConfigApplicationContext
,即基于注解的ApplicationContext
自动注册bean定义
使用AnnotationConfigApplicationContext,不需要再去创建BeanFactory,ApplicationContext初始化时自动创建
也不需要注册bean定义,spring-context中提供了@ComponentScan
和@Component
注解,AnnotationConfigApplicationContext初始化时,会把@ComponentScan指定包路径下的带@Component的类自动生成bean定义并注册到内部的BeanFactory
@Component
public class User {
public void say() {System.out.println("hello world");}
}
@Test
public void context() {
ApplicationContext context = new AnnotationConfigApplicationContext(ContextTest.class);
User bean = context.getBean(User.class);
bean.say();
}
依赖注入
上面使用BeanFactory添加AutowiredAnnotationBeanPostProcessor后置处理器,即可识别@Autowired注解完成自动注入
同样使用AnnotationConfigApplicationContext也支持@Autowired注解的依赖注入方式,原因就是:AnnotationConfigApplicationContext在初始化时,给内部的beanFactory加入了AutowiredAnnotationBeanPostProcessor后置处理器
public void autowire() {
ApplicationContext context = new AnnotationConfigApplicationContext(ContextTest.class);
User bean = context.getBean(User.class);
bean.getCar().run();
}
当然,除了识别@Autowired注解的后置处理器,AC初始化时还给bean工厂自动添加了其它的后置处理器,比如识别@Required注解的处理器
FactoryBean
使用AnnotationConfigApplicationContext当然一样支持FactoryBean,只要@Component修饰的类实现了FactoryBean接口即可
AnnotationConfigApplicationContext同时还扩展了一种新的工厂bean创建模式,即常用的@Configuration+@Bean
注解
BeanFactory后置处理器
spring-context抽象出了一种新的后置处理器,即BeanFactoryPostProcessor
(实际在spring-beans模块中)
相比于BeanPostProcessor是在bean的创建过程中添加自定义操作,BeanFactoryPostProcessor一般是在BeanFactory初始化之后的自定义操作
这个很好理解,有了ApplicationContext一般情况下不需要直接操作BeanFactory,但难免会有一些特殊情况,用户就要按照自己的逻辑给BeanFactory中加入一些bean定义、bean后置处理等(比如Mybaits),有了BeanFactoryPostProcessor我们就可以在AC内部初始化BeanFactory后加入自己的一些自定义操作
实际上,上面AnnotationConfigApplicationContext解析@ComponentScan、@Component、@Bean,包括常用的@Import注解都是AnnotationConfigApplicationContext初始化时自己给自己加入的一个特殊的BeanFactory后置处理器来实现的,即ConfigurationClassPostProcessor
AOP
spring-context是直接依赖spring-aop的,所以自然也有了AOP的功能,但之前也说过AOP的功能是可选的,所以spring-context提供了EnableAspectJAutoProxy
,使用了这个注解,spring-context初始化时才会给beanFactory加入上面提到的AnnotationAwareAspectJAutoProxyCreator后置处理器
总之,使用了EnableAspectJAutoProxy注解,spring-context就拥有了AOP的能力,我们只要给实现了@Aspect的拦截器加上@Component注解让它进入bean容器即可实现方法拦截
@Aspect
@Component
public class ShowMethodAspect {
小结
spring-context整合了spring-beans和spring-aop,让我们不需要手动注册bean定义,也不需要手动添加后置处理器
使用spring-context时,不管是普通的bean还是后置处理器我们只需指定其为bean即可(比如注解@Component),spring-context会自动把实现了后置处理的特殊bean从容器中取出作为后置处理器执行
在一文通透spring的初始化对上面的具体实现有详细介绍
spring-jdbc
这个基本都知道,用于操作数据库的一个好用的JDBC工具
模块中提供了一个JdbcTemplate
,使用它可以非常轻松完成数据库操作,而它只是jdbc的一个封装,想连接数据库对应的驱动是少不了的,比如mysql
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
此时使用JdbcTemplate,即可操作数据库
@Test
public void jdbc() {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(new SingleConnectionDataSource("jdbc:mysql://{url}", "{username}", "{password}", false));
List<Map<String, Object>> result = jdbcTemplate.query("select * from xxx", new ColumnMapRowMapper());
System.out.println(result);
}
spring-tx
提到jdbc就要考虑事务,spring-tx是专门做事务支持的,所以spring-jdbc也需要依赖spring-tx模块
spring-web
有了spring-context、spring-beans、spring-aop,我们就有了一个IOC和AOP的容器
有了spring-jdbc,我们可以去操作数据库
但做web开发,还需要处理http通讯,包括根据请求路径分发服务,这时就需要spring-web模块了
而spring-web只是web开发中一些通用类的封装,实际要做一个web服务,还是直接用spring-webmvc
关于二者的详细区别,可以移步spring-web与spring-webmvc
spring-webmvc
这个再熟悉不过了,是一个mvc的框架,也支持restful协议
spring-webmvc基于于spring-web(web通用模块)、spring-context(spring上下文),可以运行在servlet容器如tomcat上
spring-webmvc单独使用一般就是老套的xml配置,打war包,最终运行在类似tomcat上
spring-boot
这大家都在用,就不细说了
有了它,比如写web项目,不需要再考虑spring架构各模块的作用了,也不需要再去配合tomcat使用了,只要引入spring-boot-starter-web
就引入了springmvc和嵌入式的tomcat,该写的复杂配置boot都写好了,直接run就行了
spring-boot还有一个超好用的自动配置机制:springboot之AutoConfiguration
spring-webflux
响应式的web服务,基于spring-web,讽刺的是spring-web的很多通用代码在响应式领域并不适用,因此spring-webflux对spring-web的依赖相对比较薄弱
spring-webflux不仅支持运行在传统Servlet容器,还支持运行在Netty上,比如spring-boot-starter-webflux
就引用spring-webflux并默认运行在Netty上
spring-webflux的详细介绍:响应式编程之WebFlux