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>
发布了96 篇原创文章 · 获赞 57 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/MOKEXFDGH/article/details/102485830