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而已

引入依赖


    org.springframework
    spring-beans
    5.2.8.RELEASE

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 {

    @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依赖


    org.springframework
    spring-aop
    5.2.8.RELEASE

此时我们不需要修改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的相关依赖


    org.aspectj
    aspectjweaver
    1.9.6

按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


    org.springframework
    spring-jdbc
    5.2.8.RELEASE


    mysql
    mysql-connector-java
    8.0.18

此时使用JdbcTemplate,即可操作数据库

@Test
public void jdbc() {
    JdbcTemplate jdbcTemplate = new JdbcTemplate();
    jdbcTemplate.setDataSource(new SingleConnectionDataSource("jdbc:mysql://{url}", "{username}", "{password}", false));
    List> 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

你可能感兴趣的:(spring架构脉络梳理)