spring架構(gòu)脈絡(luò)梳理

前言

spring源碼包含很多個模塊,如sping-core,sping-beans,sping-context,spring-aop,spring-web,spring-webmvc,spring-webflux等

在之前研究源碼時,往往都是統(tǒng)一視為spring,而忽略了各個模塊所承擔(dān)的職責(zé),今天決定徹底梳理一下spring的架構(gòu)脈絡(luò),好對spring有個全局的認(rèn)知

架構(gòu)

下面這張圖來源于Spring 4.3.27版本的doc文檔中

Spring Framework

圖片有些過時了,大體還是差不多,今天從web開發(fā)角度,由里向外,分析以下幾個較重要的模塊

Core: Beans, Core,Context,AOP
Web: Web, WebMVC,WebFlux
Data Access:JDBC
Boot: Boot的整合和自動配置

spring-core

spring-core這個包是spring通用性代碼的封裝,單獨拿出來并沒有什么作用,一般我們自己開發(fā)項目也會有一個通用的包或模塊,spring-core可看做整個spring架構(gòu)中的通用模塊

spring-beans

從名字可以看出:Beans,即很多的bean,我們常說spring是一個bean容器、一個IOC容器,這其實就是spring-beans中BeanFactory的功勞了,所以spring-beans是spring框架的核心基礎(chǔ)

spring-beans中,把bean的生產(chǎn)者抽象為bean工廠(BeanFactory),bean生產(chǎn)的圖紙抽象為bean定義(BeanDefinition),并實現(xiàn)了一個默認(rèn)的bean工廠:DefaultListableBeanFactory,使用它我們可以注冊一個bean定義并最終獲取到bean,從創(chuàng)建到存儲到銷毀的生命周期都交給了BeanFactory

bean在BeanFactory的存儲方式就是傳說中的三級緩存了,實際上也就是三個map而已

引入依賴

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.2.8.RELEASE</version>
</dependency>
bean容器

測試一下bean容器的功能,注冊一個bean定義并獲取

創(chuàng)建一個簡單的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);
    // 調(diào)用方法
    bean.say(); // 輸出hello world
}

獲取bean的方式,可以通過注冊時的名字獲取,也可以如例子中的getByType

后置處理器

spring-beans中抽象了可配置的BeanFactory: ConfigurableBeanFactory,這種BeanFactory是支持添加后置處理器的來配置bean的創(chuàng)建步驟的,而DefaultListableBeanFactory也是一種ConfigurableBeanFactory

bean的后置處理有很多種,可以定制不同類型的后置處理器,以便在bean生命周期的不同階段自定義步驟

比如我們可以實現(xiàn)InstantiationAwareBeanPostProcessor(實例化感知后置處理器),在bean的生產(chǎn)過程注入屬性值

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);
    // 調(diào)用屬性的方法
    bean.getCar().run(); // running
}

最終完成了Car的bean注入到User的屬性中,可以繼續(xù)優(yōu)化一下這個后置處理器: 寫一個注解,然后檢查某類是否有某個屬性有這個注解,如果有就通過getBeanByType的方式設(shè)置屬性值,這就是我們使用@Autowire的邏輯

而這個后置處理器,spring-beans包中也給我們封裝好了,即AutowiredAnnotationBeanPostProcessor

依賴注入

DefaultListableBeanFactory本身支持使用設(shè)置AutowireMode的方式完成自動裝配,但這種方式粒度太大,一般我們需要根據(jù)用戶的配置按需自動注入,這種情況就需要使用后置處理器,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);
    // 調(diào)用屬性的方法
    bean.getCar().run(); // running
}
FactoryBean

bean工廠DefaultListableBeanFactory還支持FactoryBean,使用這種方式BeanFactory只作為一個容器存儲,而實際創(chuàng)建的過程用戶完全自定義,試一下

定義一個生產(chǎn)Car的工廠,實現(xiàn)FactoryBean

public class CarFactory implements FactoryBean<Car> {

    @Override
    public Car getObject() throws Exception { // 生產(chǎn)Car
        return new Car();
    }

    @Override
    public Class<?> getObjectType() { // 生產(chǎn)類型
        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
}
小結(jié)

spring-beans包是spring框架的基礎(chǔ),提供了BeanFactory,其中一個重要的實現(xiàn)即DefaultListableBeanFactory,使用它我們可以實現(xiàn)一個bean容器,通過后置處理器或FactoryBean的方式我們可以控制特殊bean的自定義生產(chǎn)過程,并且spring-beans內(nèi)部寫好了一個注解方式的依賴注入的后置處理器,使用它可以方便實現(xiàn)依賴注入

spring-aop

spring-aop是面向切面編程的支持模塊,相比于spring-beans是一個基礎(chǔ)模塊,spring-aop則是一個可選模塊,如果不需要面向切面編程,完全可以不使用

spring-aop基于spring-beans,它主要提供了一系列特殊的BeanFactory后置處理器,使用這種特殊后置處理器,就可以很輕松實現(xiàn)面向切面編程,例如:

  • AbstractAutoProxyCreator
  • AbstractAdvisorAutoProxyCreator
  • AnnotationAwareAspectJAutoProxyCreator
AbstractAutoProxyCreator

比如還是上面的例子,我們希望給User和Car的方法執(zhí)行前都打印一下方法名,使用AOP可以如下處理:

加入aop依賴

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.2.8.RELEASE</version>
</dependency>

此時我們不需要修改User或Car的方法,只需要實現(xiàn)一個AOP創(chuàng)建器即可,即實現(xiàn)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());
                    }
            };
        }
    });
    // 調(diào)用User方法
    beanFactory.getBean(User.class).say();
}

攔截方式即方法執(zhí)行前打印一下方法名, 輸出如下

AOP
AnnotationAwareAspectJAutoProxyCreator

同時,spring-aop還支持AspectJ語法,對應(yīng)的后置處理器是AnnotationAwareAspectJAutoProxyCreator

使用這種后置處理器,就可以在BeanFactory中查找按AspectJ語法寫出的攔截器,并轉(zhuǎn)換為spring-aop的Advisor以進(jìn)行方法攔截,當(dāng)然需要引入AspectJ的相關(guān)依賴

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
</dependency>

按AspectJ語法寫一個攔截器,后續(xù)加入到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();
    // 因為實現(xiàn)方式是取bean容器查找,所以必須指定beanFactory
    aopCreator.setBeanFactory(beanFactory);
    // 添加到后置處理
    beanFactory.addBeanPostProcessor(aopCreator);
    // 調(diào)用User方法
    beanFactory.getBean(User.class).say();
}

最終輸出如下

AOP
小結(jié)

spring-aop就是給spring-beans提供了一系列的aop相關(guān)功能的后置處理器

spring-context

有了spring-beans,我們可以實現(xiàn)一個IOC容器,有了spring-aop,我們可以在IOC的基礎(chǔ)上實現(xiàn)AOP,那么spring-context模塊又有什么用?

spring-context抽象出了ApplicationContext, 代表應(yīng)用上下文,相比spring-beans是一個bean的生命周期管理的單純工具,spring-context是管理整個spring應(yīng)用,并同時維護(hù)著內(nèi)部的BeanFactory,承擔(dān)的責(zé)任必然更多,比如對事件的支持和國際化的支持,最重要的是我們不需要手動注冊bean定義了,也不需要手動添加后置處理器了

總之,有了ApplicationContext,我們不需要再直接和BeanFactory打交道,這個工作交給了ApplicationContext,我們只需用更簡易的方式操作ApplicationContext即可

ApplicationContext的實現(xiàn)也有很多,最具代表性的是AnnotationConfigApplicationContext,即基于注解的ApplicationContext

自動注冊bean定義

使用AnnotationConfigApplicationContext,不需要再去創(chuàng)建BeanFactory,ApplicationContext初始化時自動創(chuàng)建

也不需要注冊bean定義,spring-context中提供了@ComponentScan@Component注解,AnnotationConfigApplicationContext初始化時,會把@ComponentScan指定包路徑下的帶@Component的類自動生成bean定義并注冊到內(nèi)部的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在初始化時,給內(nèi)部的beanFactory加入了AutowiredAnnotationBeanPostProcessor后置處理器

public void autowire() {
    ApplicationContext context = new AnnotationConfigApplicationContext(ContextTest.class);
    User bean = context.getBean(User.class);
    bean.getCar().run();
}

當(dāng)然,除了識別@Autowired注解的后置處理器,AC初始化時還給bean工廠自動添加了其它的后置處理器,比如識別@Required注解的處理器

FactoryBean

使用AnnotationConfigApplicationContext當(dāng)然一樣支持FactoryBean,只要@Component修飾的類實現(xiàn)了FactoryBean接口即可

AnnotationConfigApplicationContext同時還擴(kuò)展了一種新的工廠bean創(chuàng)建模式,即常用的@Configuration+@Bean注解

BeanFactory后置處理器

spring-context抽象出了一種新的后置處理器,即BeanFactoryPostProcessor(實際在spring-beans模塊中)

相比于BeanPostProcessor是在bean的創(chuàng)建過程中添加自定義操作,BeanFactoryPostProcessor一般是在BeanFactory初始化之后的自定義操作

這個很好理解,有了ApplicationContext一般情況下不需要直接操作BeanFactory,但難免會有一些特殊情況,用戶就要按照自己的邏輯給BeanFactory中加入一些bean定義、bean后置處理等(比如Mybaits),有了BeanFactoryPostProcessor我們就可以在AC內(nèi)部初始化BeanFactory后加入自己的一些自定義操作

實際上,上面AnnotationConfigApplicationContext解析@ComponentScan、@Component、@Bean,包括常用的@Import注解都是AnnotationConfigApplicationContext初始化時自己給自己加入的一個特殊的BeanFactory后置處理器來實現(xiàn)的,即ConfigurationClassPostProcessor

AOP

spring-context是直接依賴spring-aop的,所以自然也有了AOP的功能,但之前也說過AOP的功能是可選的,所以spring-context提供了EnableAspectJAutoProxy,使用了這個注解,spring-context初始化時才會給beanFactory加入上面提到的AnnotationAwareAspectJAutoProxyCreator后置處理器

總之,使用了EnableAspectJAutoProxy注解,spring-context就擁有了AOP的能力,我們只要給實現(xiàn)了@Aspect的攔截器加上@Component注解讓它進(jìn)入bean容器即可實現(xiàn)方法攔截

@Aspect
@Component
public class ShowMethodAspect {
小結(jié)

spring-context整合了spring-beans和spring-aop,讓我們不需要手動注冊bean定義,也不需要手動添加后置處理器

使用spring-context時,不管是普通的bean還是后置處理器我們只需指定其為bean即可(比如注解@Component),spring-context會自動把實現(xiàn)了后置處理的特殊bean從容器中取出作為后置處理器執(zhí)行

一文通透spring的初始化對上面的具體實現(xiàn)有詳細(xì)介紹

spring-jdbc

這個基本都知道,用于操作數(shù)據(jù)庫的一個好用的JDBC工具

模塊中提供了一個JdbcTemplate,使用它可以非常輕松完成數(shù)據(jù)庫操作,而它只是jdbc的一個封裝,想連接數(shù)據(jù)庫對應(yīng)的驅(qū)動是少不了的,比如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,即可操作數(shù)據(jù)庫

@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

提到j(luò)dbc就要考慮事務(wù),spring-tx是專門做事務(wù)支持的,所以spring-jdbc也需要依賴spring-tx模塊

spring-web

有了spring-context、spring-beans、spring-aop,我們就有了一個IOC和AOP的容器

有了spring-jdbc,我們可以去操作數(shù)據(jù)庫

但做web開發(fā),還需要處理http通訊,包括根據(jù)請求路徑分發(fā)服務(wù),這時就需要spring-web模塊了

而spring-web只是web開發(fā)中一些通用類的封裝,實際要做一個web服務(wù),還是直接用spring-webmvc

關(guān)于二者的詳細(xì)區(qū)別,可以移步spring-web與spring-webmvc

spring-webmvc

這個再熟悉不過了,是一個mvc的框架,也支持restful協(xié)議

spring-webmvc基于于spring-web(web通用模塊)、spring-context(spring上下文),可以運行在servlet容器如tomcat上

spring-webmvc單獨使用一般就是老套的xml配置,打war包,最終運行在類似tomcat上

spring-boot

這大家都在用,就不細(xì)說了

有了它,比如寫web項目,不需要再考慮spring架構(gòu)各模塊的作用了,也不需要再去配合tomcat使用了,只要引入spring-boot-starter-web就引入了springmvc和嵌入式的tomcat,該寫的復(fù)雜配置boot都寫好了,直接run就行了

spring-boot還有一個超好用的自動配置機(jī)制:springboot之AutoConfiguration

spring-webflux

響應(yīng)式的web服務(wù),基于spring-web,諷刺的是spring-web的很多通用代碼在響應(yīng)式領(lǐng)域并不適用,因此spring-webflux對spring-web的依賴相對比較薄弱

spring-webflux不僅支持運行在傳統(tǒng)Servlet容器,還支持運行在Netty上,比如spring-boot-starter-webflux就引用spring-webflux并默認(rèn)運行在Netty上

spring-webflux的詳細(xì)介紹:響應(yīng)式編程之WebFlux

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容