spring架构脉络梳理

前言

spring源码包含很多个模块,如sping-core,sping-beans,sping-context,spring-aop,spring-web,spring-webmvc,spring-webflux等

在之前研究源码时,往往都是统一视为spring,而忽略了各个模块所承担的职责,今天决定彻底梳理一下spring的架构脉络,好对spring有个全局的认知

架构

下面这张图来源于Spring 4.3.27版本的doc文档中

Spring Framework

图片有些过时了,大体还是差不多,今天从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();
}

拦截方式即方法执行前打印一下方法名, 输出如下

AOP
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();
}

最终输出如下

AOP
小结

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容