SpringBoot 數據訪問、啓動配置原理以及自定義Starter


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. 編寫自動配置類
    1. 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>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章