Loading...
墨滴

jrh

2021/11/01  阅读:50  主题:橙心

Java 面试八股文之框架篇(四)

前言

这是系列文章【 Java 面试八股文】框架篇的第四期。

【 Java 面试八股文】系列会陆续更新 Java 面试中的高频问题,旨在从问题出发,带你理解 Java 基础,数据结构与算法,数据库,常用框架等。该系列前几期文章可以通过下方给出的链接进行查看~

按照惯例——首先要做几点说明:

  1. 【 Java 面试八股文】中的面试题来源于社区论坛,书籍等资源;感谢使我读到这些宝贵面经的作者们。
  2. 对于【 Java 面试八股文】中的每个问题,我都会尽可能地写出我自己认为的“完美解答”。但是毕竟我的身份不是一个“真理持有者”,只是一个秉承着开源分享精神的 “knowledge transmitter” & 菜鸡,所以,如果这些答案出现了错误,可以留言写出你认为更好的解答,并指正我。非常感谢您的分享。
  3. 知识在于“融释贯通”,而非“死记硬背”;现在市面上固然有很多类似于“Java 面试必考 300 题” 这类的文章,但是普遍上都是糟粕,仅讲述其果,而不追其源;希望我的【 Java 面试八股文】可以让你知其然,且知其所以然~

那么,废话不多说,我们正式开始吧!

往期文章

框架篇(四)

1、讲一下 Spring Boot 自动配置的原理?


Spring Boot 是 Spring 组件的一站式解决方案。它简化了 Spring 繁重的配置,提供了各种 Starter,使开发者能够更快,更好地构建一个可运行的程序。Spring Boot 使用起来之所以这么方便,得益于其两大核心特性:自动配置(Auto Configuration)与起步依赖(Starter Dependency)。接下来,我们就看一下 Spring Boot 是如何实现自动配置的。

1、 @EnableAutoConfiguration 注解

Spring Boot 的启动类上有一个 @SpringBootApplication 注解,点进源代码后:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
  @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
  @Filter(type 
= FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication 
{
    // ... ...
}

我们可以看到,在 @SpringBootApplication 注解之上有一个 @EnableAutoConfiguration 注解。这个注解的作用便是帮助 Spring Boot 完成自动配置。点进源代码后:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration 
{
    // ... ...
}

我们看到 @EnableAutoConfiguration 注解也属于一个派生注解,其关键功能由导入的 AutoConfigurationImportSelector 实现。我们可以进入到 AutoConfigurationImportSelector 类中,顺着代码逻辑逐步往下走,就会定位到 SpringFactoriesLoader#loadFactoryNames 方法。该方法的作用是扫描所有具备 META-INF/spring.factories 的 jar 包,并获取到 spring.factories 文件中所有 org.springframework.boot.autoconfigure.EnableAutoConfiguration 属性的值。而这些值便是 Spring Boot 需要进行自动配置的类。

我们可以在 spring-boot-autoconfigure 这个 jar 包中找到 META-INF/spring.factories 文件:

# ... ...
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
# ... ...

这些类便是 Spring Boot 为我们实现的自动配置类。

2、 条件注解

我们随意点进去一个自动配置类,就可以发现这些类之上都会有 @Configuration 注解,除此之外,还有一些条件注解(Conditional)。而正是这些条件注解决定了我们的 XXXAutoConfiguration 配置类的一些属性或操作的生效条件,常见的条件注解有:

  • @ConditionalOnClass
  • @ConditionalOnBean
  • @ConditionalOnMissingBean
  • @ConditionalOnProperty
  • ... ...

@ConditionalOnClass 注解的含义为,当 ClassPath 上有指定类时要怎么做;@ConditionalOnBean 注解的含义为当 Spring 容器中有指定的 Bean 时要怎么做;而 @ConditionalOnMissingBean 则是当我们的 Spring 容器中没有指定的 Bean 时,要如何进行操作;@ConditionalOnProperty 的含义是当我配置了特定的属性值后的执行策略。

3、 实现自己的自动配置类

接下来,我们来看一个程序,该程序实现了我自己的自动配置类:

TestApplicationRunner

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;

@Slf4j
public class TestApplicationRunner implements ApplicationRunner {
    
    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("hello");
    }
}

MyAutoConfiguration

import com.github.test.TestApplicationRunner;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnClass(TestApplicationRunner.class)
public class MyAutoConfiguration 
{

    @Bean
    @ConditionalOnMissingBean(TestApplicationRunner.class)
    @ConditionalOnProperty(name 
"test", havingValue = "true", matchIfMissing = true)
    public TestApplicationRunner testApplicationRunner() {
        return new TestApplicationRunner();
    }
}

这个类便是我们实现的让 Spring Boot 扫描并执行的自动配置类。我们定义了该配置类在类路径中包含 TestApplicationRunner.class 时生效;当 Spring IoC 容器中没有 testApplicationRunner 这个 Bean 的信息,且在全局配置文件中没有 test 属性定义(没有则默认 test 属性值为 true)或 test 属性值为 “true” 时,则生成 testApplicationRunner 这样一个 Bean。

我们还需要在 MyAutoConfiguration 所在工程的 resources 目录下创建 META-INF/spring.factories 文件,该文件中指定了让 Spring Boot 自动扫描并获取的自动配置类的路径信息:

META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.github.myautoconfiguration.MyAutoConfiguration

AutoconfigureDemoApplication

package com.github.autoconfiguredemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AutoconfigureDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(AutoconfigureDemoApplication.classargs);
    }

}

在 AutoconfigureDemoApplication 所在的工程下,我在配置文件中先指定 test 属性值为 true:

application.properties

test=true

运行代码,可以发现在控制台输出了 “hello”,这表明我们的自动配置类生效,testApplicationRunner 成功地注册到了 IoC 容器中。

如果我们将 application.properties 文件的 test 属性值改为 false,再次运行代码,在控制台中则没有输出任何信息,这个结果也完全符合我们的预期。

总结

Spring Boot 自动配置的原理是什么?

简而言之,在 Spring Boot 启动时,会通过 @EnableAutoConfiguration 注解定位到 META-INF/spring.factories 文件中,并获取所有 EnableAutoConfiguration 属性的值。这些值便是 Spring Boot 要执行的自动配置类。这些配置类通常以 XXXAutoConfiguration 这种形式来命名,它的本质就是一个 Java Config 形式的 Spring 容器配置类。这些配置类中使用了 Conditional 条件注解,使得我们可以通过全局配置文件 application.properties(yml)来决定这些配置类的行为。

2、Maven 相关


虽然 Maven 与 Spring Boot 并没有直接的关联,但是在国内,我们的 Spring 项目普遍都是使用 Maven 进行构建的。

我总结了两道关于 Maven 的面试题:

  1. 执行一个 Maven 命令的本质是什么?
  2. Maven 如何解决依赖冲突?

1、 执行一个 Maven 命令的本质是什么?

首先来谈一下 Maven 是什么。

Maven 是 Java 世界中最流行的项目构建工具之一。

说白了, Maven 的主要职责就是“包管理”。在没有出现像 Maven 这样的包管理工具之前,我们做一个 Java 项目,需要第三方依赖包时要怎么做呢?我们需要将别人打好的 jar 包下载到本地,然后手动指定给项目。当我们使用的第三方包需要更新版本时,只能删除原来的 jar 包,然后重新进行下载。当开发者需要手动管理成百上千个 jar 包时,毫无疑问是极其痛苦的。而出现了 Maven 以后,我们需要什么第三方包,都可以直接可以在 POM 文件中添加几行 XML 代码,指定包名,版本等信息就可以了。另外,Maven 还提供了很多插件,比如常用的打包插件,调试插件等等,方便我们项目的开发和部署。

在和你讨论执行一个 Maven 命令的本质之前,我们需要了解一下 Maven 的生命周期。

Maven 拥有三套相互独立的生命周期:

  1. clean
  2. default
  3. site

clean 生命周期的目的是清理项目,default 生命周期的目的是构建项目,site 生命周期的目的是建立项目站点。

每个生命周期都包含一些阶段(phase),这些阶段是有先后顺序的,并且后面的阶段依赖于前面的阶段,用户和 Maven 最直接的交互方式就是调用这些生命周期的阶段。

三套生命周期本身是相互独立的,用户可以仅调用 clean 生命周期的某个阶段,或者仅仅调用 default 生命周期的某个阶段。单独调用 Maven 生命周期的某个阶段时,不会触发其他生命周期的任何阶段。

clean 生命周期

clean 生命周期的目的是清理项目,它包含三个阶段:

  • pre-clean:执行一些清理前需要完成的工作
  • clean:清理上一次构建生成的文件
  • post-clean:执行一些清理后需要完成的工作

default 生命周期

default 生命周期定义了真正构建项目时所需要执行的步骤,它是所有生命周期中最核心的部分,其包含的阶段如下:

  • validate:验证工程是否正确,所需的信息是否完整
  • initialize:初始化构建平台
  • generate-sources
  • process-sources
  • generate-resources
  • process-resources
  • compile:编译项目的代码。
  • process-classes
  • generate-test-sources
  • process-test-sources
  • generate-test-resources
  • process-test-resources
  • test-compile:编译项目的测试代码。
  • process-test-classes
  • test:使用单元测试框架运行测试,测试代码不会被打包或者部署
  • prepare-package
  • package:接受编译好的代码,将工程文件打包为指定的格式,例如 jar 包 或 war 包
  • pre-integration-test
  • integration-test:集成测试
  • post-integration-test
  • verify:检查 package 是否有效,符合标准
  • install:将包安装至 Maven 本地仓库,供本地其他的 Maven 项目使用
  • deploy:将最终的包复制到远程仓库,供其他开发人员使用

site 生命周期

site 生命周期的目的是建立和发布项目站点, Maven 能够基于 POM 文件所包含的信息,自动生成一个友好的站点,方便团队交流和发布项目。该生命周期包含阶段如下:

  • pre-site:执行一些在生成项目站点之前需要完成的工作
  • site:生成项目站点文档
  • post-site:执行一些在生成项目站点之后需要完成的工作
  • site-deploy:将生成的项目站点发布到服务器上

插件与目标

Maven 三套生命周期定义的各个阶段我们已经大致介绍完毕了。

在这些阶段中,Maven 实际上是不会做任何工作的,这些实际的工作是由绑定在各个阶段的插件(plugin)来完成的。

Maven 的生命周期与插件相互绑定,用以完成实际的构建任务。Maven 的核心仅仅是定义了抽象的生命周期,然而具体的任务还是要由插件来完成,如果当前的生命周期阶段没有绑定任何插件的任何目标(goal),那么这个阶段就什么都不会做。

例如项目编译这一任务,它对应了 default 生命周期的 compile 这一阶段,而 maven-compiler-plugin 这一插件的 compile 目标能够完成该任务。因此将它们互相绑定,就能够实现项目编译的目的。

为了让用户不用做任何配置就能构建 Maven 项目,Maven 为一些主要的生命周期阶段绑定了很多插件的目标,这些是内置绑定,当用户通过命令行调用生命周期阶段时,对应的插件目标就会执行相应的任务,一些内置绑定关系如下:

clean 生命周期阶段与插件目标的内置绑定

default 生命周期阶段与插件目标的内置绑定

site 生命周期阶段与插件目标的内置绑定

除了内置绑定外,Maven 也支持用户自己选择将某个插件的目标绑定到生命周期的某个阶段上。

执行一个 Maven 命令的本质是什么?

我们现在回到问题,执行一个 Maven 命令的本质是什么?

譬如我们执行了 mvn test 这一命令,Maven 会将包含 test 这个阶段之前所有的阶段全部执行一遍!如果该阶段没有绑定任何插件的任何目标,那就什么也不执行。

举个例子🌰

我们会使用 mvn clean package 来进行项目打包。在这个命令中,我们调用了 Maven 的 clean 生命周期的 clean 阶段绑定的插件目标,以及 default 生命周期的 package 阶段及以前所有阶段绑定的插件目标:

  • maven-clean-plugin:clean ->
  • maven-resources-plugin:resources ->
  • maven-compiler-plugin:compile ->
  • maven-resources-plugin:testResources ->
  • maven-compiler-plugin:testCompile ->
  • maven-surefire-plugin:test ->
  • maven-jar-plugin:jar

2、 Maven 如何解决依赖冲突?

什么是依赖冲突?

举个例子🌰:

假设我们的项目依赖关系如上图所示。项目本身依赖了 A,B 两个 jar 包;A 依赖了 C,C 依赖了 D 的 version 0.2 版本;B 依赖了 D 的 version 0.1 版本。此时就产生了依赖冲突的问题。

Maven 传递性依赖的管理原则有两点:

  1. 绝对不允许最终的类路径里出现同名不同版本的 jar 包
  2. “近者胜出,如果距离相同则谁在前谁胜出”

在 Maven 遇到上面这种情况时,会干掉其中一个 D jar 包,而因为 D version 0.2 版本的 jar 包 “距离比较远”,所以就会被 Maven 解决掉。如果 D 的前后两个版本改动不大则还好,你的项目可能不会出现问题,但如果 D version 0.2 较前一个版本新增了许多方法,并且在你的项目的某一处调用了这个方法,那就糟糕了。你可能遇到一些譬如像 NoClassDefFoundError,ClassNotFoundException 等异常。如果你没有解决这些问题的丰富经验,便会浪费许多时间在项目调试上。

在这里给大家介绍两种工具🔧:

  1. Maven Helper 插件
  2. mvn dependency:tree 命令

这两种工具都可以帮助我们快速发现依赖冲突问题。

3、在 Spring Boot 中如何获取到容器中的 Bean?


其实本问题完全可以不加 “在 Spring Boot 中” 这个前提条件。

面试官也可以直接问:“如何获取到 Spring 容器中的 Bean?

在前几期文章里,我介绍过 Spring Bean 完整的生命周期,其中就介绍了 XXXAware 接口的作用。Aware 类型的接口可以使我们获取到 Spring 容器中的一些资源。

有一个比较重要的 Aware 接口——ApplicationContextAware,其作用就是获取到应用上下文的资源。当一个类实现了 ApplicationContextAware 接口,就可以获得 ApplicationContext 容器中所有的 Bean。所以,我们可以写一个获取容器中的 Bean 的工具类,来达到我们的目的:

ContextUtils

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class ContextUtils implements ApplicationContextAware {

    private static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }

    public static ApplicationContext getContext() {
        return context;
    }

    public static Object getBean(String beanName) {
        return context.getBean(beanName);
    }

    public static Object getBean(Class<?> aClass) {
        return context.getBean(aClass);
    }
}

4、 Spring Boot 中如何读取自定义的配置文件?


我们都知道,Spring Boot 中默认的全局配置文件为 application.properties(.yml)。当我们想读取并注入配置文件中的属性值时可以使用 @Value 注解或 @ConfigurationProperties 注解。

不过,当我们的应用配置项比较繁杂时,将所有模块的配置全部放在一个配置文件中就不太容易维护了。此时,我们一般会选择将一个配置文件里的内容进行拆分,分成多个配置文件。而在 Spring Boot 中,我们可以使用 @PropertySource 注解,配合以上两种注解完成读取并注入自己的配置文件中的属性值。

如示例程序:

resources/config/my.properties

name=kim

PropertysourcedemoApplication

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.PropertySource;

@SpringBootApplication
@Slf4j
@PropertySource("classpath:config/my.properties")
public class PropertysourcedemoApplication implements ApplicationRunner {

    public static void main(String[] args) {
        SpringApplication.run(PropertysourcedemoApplication.classargs);
    }

    @Value("${name}")
    private String name;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info(name);
    }
}

程序执行后,我们可以看到在控制台输出了我们自定义配置文件的属性值。

@PropertySource 注解的功能就是加载指定的配置文件(.properties)到 Spring 的 Environment 中,不过它默认是不支持对 YAML(.yml) 文件进行读取的。

其实,无论是 Spring Boot 本身的全局配置文件,还是我们自定义的配置文件,这些配置最终在 Spring 中都会被抽象为 PropertySource。我们完全可以不通过 @PropertySource 注解,而是定制一个自己的 PropertySource 来完成配置文件的装载。首先从 Environment 里取得 PropertySources,然后再将自己的 propertySource 添加到合适的位置。

示例代码如下:

MyEnvironmentPostProcessor

import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.env.PropertiesPropertySourceLoader;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ClassPathResource;

import java.io.IOException;

public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {

    private PropertiesPropertySourceLoader loader = new PropertiesPropertySourceLoader();

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        MutablePropertySources propertySources = environment.getPropertySources();
        ClassPathResource resource = new ClassPathResource("/config/my.properties");
        try {
            PropertySource ps = loader.load("my", resource).get(0);
            propertySources.addLast(ps);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

PropertysourcedemoApplication

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@Slf4j
public class PropertysourcedemoApplication implements ApplicationRunner {

    public static void main(String[] args) {
        SpringApplication.run(PropertysourcedemoApplication.classargs);
    }

    @Value("${name}")
    private String name;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info(name);
    }
}

程序运行后,在控制台上也同样输出了自定义配置文件的属性值。

5、说一下在 Spring Boot 中配置多数据源的思路?


如果我有两个主从数据源,一般会将这两个数据源的配置完全分开,并将不同数据源对应的 mapper 放在不同的包下。然后在我的数据源的 Java Config 类中,使用 @MapperScan 注解,来区分这些 mapper 所在的包。

在一个数据源的 Java Config 类中,通常我会声明以下几个 Bean:

  • DataSourceProperties
  • DataSource
  • DataSourceTransactionManager
  • SqlSessionFactory

在主数据源的 Java Config 类中,我会在这些 Bean 对应的注册方法上加上 @Primary 注解,声明这是一个主数据源。

以上内容便是在 Spring Boot 中配置多数据源的思路。

示例程序:

application.properties

# master 数据源配置
master.datasource.url=jdbc:mysql://localhost:3306/master?useUnicode=true&characterEncoding=utf8
master.datasource.username=root
master.datasource.password=root
master.datasource.driverClassName=com.mysql.cj.jdbc.Driver

# slave 数据源配置
slave.datasource.url=jdbc:mysql://localhost:3306/slave?useUnicode=true&characterEncoding=utf8
slave.datasource.username=root
slave.datasource.password=root
slave.datasource.driverClassName=com.mysql.cj.jdbc.Driver

# mybatis
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.multidatasourcedemo.domain
mybatis.configuration.map-underscore-to-camel-case=true

MasterDataSourceConfig

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.annotation.Resource;
import javax.sql.DataSource;

@Configuration
@MapperScan(basePackages = "com.github.multidatasourcedemo.dao.master", sqlSessionFactoryRef = "masterSqlSessionFactory")
public class MasterDataSourceConfig {

    static final String MAPPER_LOCATION = "classpath:mapper/master/*.xml";

    @Bean
    @ConfigurationProperties("master.datasource")
    @Primary
    public DataSourceProperties masterDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @Primary
    public DataSource masterDataSource() {
        return masterDataSourceProperties().initializeDataSourceBuilder().build();
    }

    @Bean
    @Resource
    @Primary
    public PlatformTransactionManager masterTxManager(@Qualifier("masterDataSource") DataSource masterDataSource) {
        return new DataSourceTransactionManager(masterDataSource);
    }

    @Bean(name = "masterSqlSessionFactory")
    @Primary
    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource masterDataSource)
            throws Exception 
{
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(masterDataSource);
        sessionFactory.setMapperLocations(
                new PathMatchingResourcePatternResolver().getResources(MasterDataSourceConfig.MAPPER_LOCATION));
        return sessionFactory.getObject();
    }
}

SlaveDataSourceConfig

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.annotation.Resource;
import javax.sql.DataSource;

@Configuration
@MapperScan(basePackages = "com.github.multidatasourcedemo.dao.slave", sqlSessionFactoryRef = "slaveSqlSessionFactory")
public class SlaveDataSourceConfig {

    static final String MAPPER_LOCATION = "classpath:mapper/slave/*.xml";

    @Bean
    @ConfigurationProperties("slave.datasource")
    public DataSourceProperties slaveDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    public DataSource slaveDataSource() {
        return slaveDataSourceProperties().initializeDataSourceBuilder().build();
    }

    @Bean
    @Resource
    public PlatformTransactionManager slaveTxManager(@Qualifier("slaveDataSource") DataSource slaveDataSource) {
        return new DataSourceTransactionManager(slaveDataSource);
    }

    @Bean(name = "slaveSqlSessionFactory")
    public SqlSessionFactory slaveSqlSessionFactory(@Qualifier("slaveDataSource") DataSource slaveDataSource)
            throws Exception 
{
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(slaveDataSource);
        sessionFactory.setMapperLocations(
                new PathMatchingResourcePatternResolver().getResources(SlaveDataSourceConfig.MAPPER_LOCATION));
        return sessionFactory.getObject();
    }
}

总结

在今天的文章中,我给出了几个比较常考的 Spring Boot 框架相关的面试题与知识点。不过 Spring Boot 框架的内容还有很多,在后面我会给出更多的问题并进行讲解,敬请期待~

好啦,至此为止,这篇文章就到这里了,感谢您的阅读与支持~~欢迎大家关注我的公众号,在这里希望你可以收获更多的知识,我们下一期再见!

jrh

2021/11/01  阅读:50  主题:橙心

作者介绍

jrh