SpringBoot 数据访问、启动配置原理以及自定义Starter
MOKE 2019-11-07 PM loading 2条

JDBC

我们可以通过使用 Spring Initializr 选择所需要的模块进行项目的创建:
在这里插入图片描述

  1. 依赖(通过上面创建的应用会自动配置依赖)
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
  1. yaml配置
        spring:
          datasource:
            username: root
            password: 123456
            url: jdbc:mysql://127.0.0.1:3306/jdbc
            driver-class-name: com.mysql.jdbc.Driver

原理:

  • 默认是用 org.apache.tomcat.jdbc.pool.DataSource 作为数据源;
  • 数据源的相关配置都在 DataSourceProperties 里面;

自动配置都在 org.springframework.boot.autoconfigure.jdbc 下:

  • DataSourceConfiguration,根据配置创建数据源,默认使用Tomcat连接池;可以通过 spring.datasource.type 指定自定义的数据源类型,可用的数据源类型:

org.apache.tomcat.jdbc.pool.DataSource、HikariDataSource、BasicDataSource

也可以使用自定义的数据源,在 DataSourceConfiguration 下:

        /**
         * Generic DataSource configuration.
         */
        @ConditionalOnMissingBean(DataSource.class)
        @ConditionalOnProperty(name = "spring.datasource.type")
        static class Generic {

           @Bean
           public DataSource dataSource(DataSourceProperties properties) {
               //使用DataSourceBuilder创建数据源,利用反射创建响应type的数据源,并且绑定相关属性
              return properties.initializeDataSourceBuilder().build();
           }

        }
  • DataSourceInitializer 实现了 ApplicationListener,通过 runSchemaScripts() 运行建表语句;runDataScripts() 运行插入数据的sql语句,即可以对数据源中的数据进行初始化。
    所以我们可以通过如下规则进行配置:

schema-.sql、data-.sql
默认规则:schema.sql,schema-all.sql;

指定位置可以使用:

        schema:
            - classpath:initializ.sql
  • JdbcTemplateAutoConfiguration,这个自动配置就是为我们操作数据库提供的 JdbcTemaplate 进行自动配置,所以我们可以直接注入:
        @Autowire
        JdbcTemplate jdbcTemplate;

使用 Druid 数据源

  1. 依赖
        <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.20</version>
        </dependency>
  1. 指定数据源类型
        spring:
          datasource:
            username: root
            password: 123456
            url: jdbc:mysql://127.0.0.1:3306/jdbc
            driver-class-name: com.mysql.jdbc.Driver
            type: com.alibaba.druid.pool.DruidDataSource
  1. 除了上面的基本配置,对于其他连接池的配置,在 SpringBoot 自动配置的 properties 中并没有,所以需要我们自己写:
        @Configuration
        public class DruidConfig {
            //将我们配置文件中的属性(maxAcive、initialSize...)绑定到 DruidDataSource
            @ConfigurationProperties(prefix = "spring.datasource")
            @Bean
            public DataSource druid(){
               return  new DruidDataSource();
            }
        }
  1. 还可以在我们的 DruidConfig 中配置监控:
            //配置Druid的监控
            //1、配置一个管理后台的Servlet
            @Bean
            public ServletRegistrationBean statViewServlet(){
                ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
                Map<String,String> initParams = new HashMap<>();

                initParams.put("loginUsername","admin");
                initParams.put("loginPassword","123456");
                initParams.put("allow","");//默认就是允许所有访问
                initParams.put("deny","192.168.15.21");

                bean.setInitParameters(initParams);
                return bean;
            }

            //2、配置一个web监控的filter
            @Bean
            public FilterRegistrationBean webStatFilter(){
                FilterRegistrationBean bean = new FilterRegistrationBean();
                bean.setFilter(new WebStatFilter());

                Map<String,String> initParams = new HashMap<>();
                initParams.put("exclusions","*.js,*.css,/druid/*");

                bean.setInitParameters(initParams);

                bean.setUrlPatterns(Arrays.asList("/*"));

                return  bean;
            }

MyBatis

在这里插入图片描述整合步骤:

  1. 依赖(创建时自动配置)
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.1</version>
        </dependency>
  1. 配置数据源(参考 Druid 数据源)
  2. MyBatis 正常使用:​数据库建表、JavaBean、Mapper接口...

Mapper使用注解:

        @Mapper
        public interface DepartmentMapper {

            @Select("select * from department where id=#{id}")
            public Department getDeptById(Integer id);

            @Delete("delete from department where id=#{id}")
            public int deleteDeptById(Integer id);

            @Options(useGeneratedKeys = true,keyProperty = "id")
            @Insert("insert into department(departmentName) values(#{departmentName})")
            public int insertDept(Department department);

            @Update("update department set departmentName=#{departmentName} where id=#{id}")
            public int updateDept(Department department);
        }

我们还可以自定义MyBatis的配置规则,给容器中添加一个ConfigurationCustomizer:

        @Configuration
        public class MyBatisConfig {

            @Bean
            public ConfigurationCustomizer configurationCustomizer(){
                return new ConfigurationCustomizer(){

                    @Override
                    public void customize(Configuration configuration) {
                        configuration.setMapUnderscoreToCamelCase(true);//开启驼峰命名映射
                    }
                };
            }
        }

除了使用 @Mapper 标识一个 Mapper 外,还可以在主类或者MyBatisCofig 上加上扫描注解:

        @MapperScan(value = "com.moke.springboot.mapper")
        @SpringBootApplication
        public class SpringBootDemoApplication {
            public static void main(String[] args) {
                SpringApplication.run(SpringBootDemoApplication .class, args);
            }
        }

使用配置文件方式

        mybatis:
          config-location: classpath:mybatis/mybatis-config.xml #指定全局配置文件的位置
          mapper-locations: classpath:mybatis/mapper/*.xml  #指定sql映射文件的位置

Spring Data JPA

Spring 官网下的 Spring Data 项目的目的是为了简化构建基于 Spring 框架应用的数据访问技术,包括关系数据库以及非关系数据库。

而 SpringData 为我们提供使用统一的API来对数据访问层进行操作
在这里插入图片描述
接下来我们就来使用 SpringData JPA:

  1. 配置数据源:参考前文
  2. 编写一个实体类(bean)
        @Entity //告诉JPA这是一个实体类(和数据表映射的类) 
        @Table(name = "t_user") //@Table来指定和哪个数据表对应;如果省略默认表名就是user; 
        public class User { 
            @Id //这是一个主键 
            @GeneratedValue(strategy = GenerationType.IDENTITY)//自增主键 
            private Integer id; 

            @Column(name = "username",length = 50) //这是和数据表对应的一个列 
            private String username; 

            @Column //省略默认列名就是属性名 
            private String password;
            ...
        }
  1. 编写一个Dao接口来操作实体类对应的数据表(Repository)
        //继承JpaRepository来完成对数据库的操作 
        public interface UserRepository extends JpaRepository<User,Integer> { 
        }
  1. 基本配置(JpaProperties)
        spring: 
            jpa:hibernate: 
                ddl‐auto: update # 自动更新或者创建数据表结构  
            show‐sql: true# 控制台打印SQL语句

启动配置原理

在之前的文章中我们学习了 自动配置原理,那么 SpringBoot 又是如何启动的呢?
在我们Main方法中调用了 SpringBootApplication 的 run 方法,其底层如下:
在这里插入图片描述由此可知,启动流程分为两步:

  1. 创建 SpringApplication 对象
    SpringApplication 的构造方法中都调用了 initialize 方法,方法如下:
        private void initialize(Object[] sources) { 
            //保存主配置类 
            if (sources != null && sources.length > 0) { 
                this.sources.addAll(Arrays.asList(sources)); 
            }
            //判断当前是否一个web应用 
            this.webEnvironment = deduceWebEnvironment(); 
            //从类路径下找到META‐INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起来 
            setInitializers((Collection) getSpringFactoriesInstances( 
                    ApplicationContextInitializer.class)); 
            //从类路径下找到ETA‐INF/spring.factories配置的所有ApplicationListener 
            setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); 
            //从多个配置类中找到有main方法的主配置类 
            this.mainApplicationClass = deduceMainApplicationClass(); 
        }
  1. 运行run方法
        public ConfigurableApplicationContext run(String... args) {
           StopWatch stopWatch = new StopWatch();
           stopWatch.start();
           ConfigurableApplicationContext context = null;
           FailureAnalyzers analyzers = null;
           configureHeadlessProperty();

           //获取SpringApplicationRunListeners;从类路径下META-INF/spring.factories
           SpringApplicationRunListeners listeners = getRunListeners(args);
            //回调所有的获取SpringApplicationRunListener.starting()方法
           listeners.starting();
           try {
               //封装命令行参数
              ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
              //准备环境
              ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
                    //创建环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准备完成

              Banner printedBanner = printBanner(environment);

               //创建ApplicationContext;决定创建web的ioc还是普通的ioc
              context = createApplicationContext();

              analyzers = new FailureAnalyzers(context);
               //准备上下文环境;将environment保存到ioc中;而且applyInitializers();
               //applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法
               //回调所有的SpringApplicationRunListener的contextPrepared();
              prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);
               //prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded();

               //刷新容器;ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat);
               //扫描,创建,加载所有组件的地方;(配置类,组件,自动配置)
               //参考Spring源码的refresh方法
              refreshContext(context);
               //从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调
               //ApplicationRunner先回调,CommandLineRunner再回调
              afterRefresh(context, applicationArguments);
               //所有的SpringApplicationRunListener回调finished方法
              listeners.finished(context, null);
              stopWatch.stop();
              if (this.logStartupInfo) {
                 new StartupInfoLogger(this.mainApplicationClass)
                       .logStarted(getApplicationLog(), stopWatch);
              }
               //整个SpringBoot应用启动完成以后返回启动的ioc容器;
              return context;
           }
           catch (Throwable ex) {
              handleRunFailure(context, listeners, analyzers, ex);
              throw new IllegalStateException(ex);
           }
        }

从 run 方法中,可以看到,SpringBoot 启动过程中的几个重要的事件回调:

  • ApplicationContextInitializerSpringApplicationRunListener:配置在META-INF/spring.factories
  • ApplicationRunnerCommandLineRunner:只需要放在ioc容器中

ApplicationContextInitializer
作用:在 ConfigurableApplicationContext 类型(或者子类型)的 ApplicationContext 做 refresh 之前,允许我们对 ConfigurableApplicationContext 的实例做进一步的设置或者处理,简单使用:

        public class HelloApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
            @Override
            public void initialize(ConfigurableApplicationContext applicationContext) {
                System.out.println("HelloApplicationContextInitializer..."+applicationContext);
            }
        }

SpringApplicationRunListener
作用:在 Spring Boot 启动初始化的过程中可以通过 SpringApplicationRunListener 接口回调来让用户在启动的各个流程中可以加入自己的逻辑。

        public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {

            //必须有的构造器
            public HelloSpringApplicationRunListener(SpringApplication application, String[] args){

            }

            @Override
            public void starting() {
                System.out.println("SpringApplicationRunListener...starting...");
            }

            @Override
            public void environmentPrepared(ConfigurableEnvironment environment) {
                Object o = environment.getSystemProperties().get("os.name");
                System.out.println("SpringApplicationRunListener...environmentPrepared.."+o);
            }

            @Override
            public void contextPrepared(ConfigurableApplicationContext context) {
                System.out.println("SpringApplicationRunListener...contextPrepared...");
            }

            @Override
            public void contextLoaded(ConfigurableApplicationContext context) {
                System.out.println("SpringApplicationRunListener...contextLoaded...");
            }

            @Override
            public void finished(ConfigurableApplicationContext context, Throwable exception) {
                System.out.println("SpringApplicationRunListener...finished...");
            }
        }

上面两种需要在 META-INF/spring.factories 中进行配置:

        org.springframework.context.ApplicationContextInitializer=\
        com.moke.springboot.listener.HelloApplicationContextInitializer

        org.springframework.boot.SpringApplicationRunListener=\
        com.moke.springboot.listener.HelloSpringApplicationRunListener

而接下来的两个接口这都提供了一个run方法,这个run方法会在SpringApplication.run(…)执行完前被调用,允许我们在 SpringApplication 启动后做一些事情。

ApplicationRunner

        @Component
        public class HelloApplicationRunner implements ApplicationRunner {
            @Override
            public void run(ApplicationArguments args) throws Exception {
                System.out.println("ApplicationRunner...run....");
            }
        }

CommandLineRunner

        @Component
        public class HelloCommandLineRunner implements CommandLineRunner {
            @Override
            public void run(String... args) throws Exception {
                System.out.println("CommandLineRunner...run..."+ Arrays.asList(args));
            }
        }

自定义 starter

starter,即启动器,可以为我们 SpringBoot 应用中引入已有的自动配置类。
而 starter 只用来做依赖的,启动器依赖于自动配置,所以实际上我们需要自定义的是自动配置模块。
最后别人只需要引入 starter,就会进行相应的自动配置。

可以将以上分为几步:

  • 启动器模块
    新建一个空的 SpringBoot 项目,修改 pom.xml 文件:
        <?xml version="1.0" encoding="UTF-8"?>
        <project xmlns="http://maven.apache.org/POM/4.0.0"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
            <modelVersion>4.0.0</modelVersion>

            <groupId>com.moke.starter</groupId>
            <artifactId>moke-spring-boot-starter</artifactId>
            <version>1.0-SNAPSHOT</version>

            <!--启动器-->
            <dependencies>
                <!--引入自动配置模块-->
                <dependency>
                    <groupId>com.moke.starter</groupId>
                    <artifactId>moke-spring-boot-starter-autoconfigurer</artifactId>
                    <version>0.0.1-SNAPSHOT</version>
                </dependency>
            </dependencies>

        </project>
  • 自动配置模块
  1. 修改 pom.xml:
        <?xml version="1.0" encoding="UTF-8"?>
        <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
           <modelVersion>4.0.0</modelVersion>

           <groupId>com.moke.starter</groupId>
           <artifactId>moke-spring-boot-starter-autoconfigurer</artifactId>
           <version>0.0.1-SNAPSHOT</version>
           <packaging>jar</packaging>

           <name>moke-spring-boot-starter-autoconfigurer</name>
           <description>Demo project for Spring Boot</description>

           <parent>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-parent</artifactId>
              <version>1.5.10.RELEASE</version>
              <relativePath/>
           </parent>

           <properties>
              <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
              <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
              <java.version>1.8</java.version>
           </properties>

           <dependencies>
              <!--引入spring-boot-starter;所有starter的基本配置-->
              <dependency>
                 <groupId>org.springframework.boot</groupId>
                 <artifactId>spring-boot-starter</artifactId>
              </dependency>
           </dependencies>

        </project>
  1. 编写自动配置类

  2. Properties 类

            @ConfigurationProperties(prefix = "moke.hello")
            public class HelloProperties {
    
                private String prefix;
                private String suffix;
    
                public String getPrefix() {
                    return prefix;
                }
    
                public void setPrefix(String prefix) {
                    this.prefix = prefix;
                }
    
                public String getSuffix() {
                    return suffix;
                }
    
                public void setSuffix(String suffix) {
                    this.suffix = suffix;
                }
            }
    1. 所需要的功能
            public class HelloService {
    
                HelloProperties helloProperties;
    
                public HelloProperties getHelloProperties() {
                    return helloProperties;
                }
    
                public void setHelloProperties(HelloProperties helloProperties) {
                    this.helloProperties = helloProperties;
                }
    
                public String sayHellAtguigu(String name){
                    return helloProperties.getPrefix()+"-" +name + helloProperties.getSuffix();
                }
            }
    1. 自动配置类
            @Configuration
            @ConditionalOnWebApplication //web应用才生效
            @EnableConfigurationProperties(HelloProperties.class)//引入properties
            public class HelloServiceAutoConfiguration {
    
                @Autowired
                HelloProperties helloProperties;
                @Bean
                public HelloService helloService(){
                    HelloService service = new HelloService();
                    service.setHelloProperties(helloProperties);
                    return service;
                }
            }
    1. META-INF/spring.factories 中添加自动配置类
            org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
            com.moke.starter.HelloServiceAutoConfiguration 
  • starter 的使用:引入 自定义starter 即可
            <dependency>
                <groupId>com.moke.starter</groupId>
                <artifactId>moke-spring-boot-starter</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
标签: Spring

非特殊说明,本博所有文章均为博主原创。

评论啦~



已有 2 条评论


  1. BB
    BB

    测试缓存

    回复 2020-05-14 00:16
  2. MOKE
    MOKE 博主

    测试评论ヾ(≧∇≦*)ゝ

    回复 2020-05-13 06:29
召唤看板娘