JDBC
我们可以通过使用 Spring Initializr 选择所需要的模块进行项目的创建:
- 依赖(通过上面创建的应用会自动配置依赖)
<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>
- 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 数据源
- 依赖
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.20</version>
</dependency>
- 指定数据源类型
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
- 除了上面的基本配置,对于其他连接池的配置,在 SpringBoot 自动配置的 properties 中并没有,所以需要我们自己写:
@Configuration
public class DruidConfig {
//将我们配置文件中的属性(maxAcive、initialSize...)绑定到 DruidDataSource
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druid(){
return new DruidDataSource();
}
}
- 还可以在我们的 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
整合步骤:
- 依赖(创建时自动配置)
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
- 配置数据源(参考 Druid 数据源)
- 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:
- 配置数据源:参考前文
- 编写一个实体类(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;
...
}
- 编写一个Dao接口来操作实体类对应的数据表(Repository)
//继承JpaRepository来完成对数据库的操作
public interface UserRepository extends JpaRepository<User,Integer> {
}
- 基本配置(JpaProperties)
spring:
jpa:hibernate:
ddl‐auto: update # 自动更新或者创建数据表结构
show‐sql: true# 控制台打印SQL语句
启动配置原理
在之前的文章中我们学习了 自动配置原理,那么 SpringBoot 又是如何启动的呢?
在我们Main方法中调用了 SpringBootApplication 的 run 方法,其底层如下:
由此可知,启动流程分为两步:
- 创建 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();
}
- 运行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 启动过程中的几个重要的事件回调:
- ApplicationContextInitializer、SpringApplicationRunListener:配置在META-INF/spring.factories
- ApplicationRunner、CommandLineRunner:只需要放在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>
- 自动配置模块
- 修改 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>
- 编写自动配置类
- 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; } }
- 所需要的功能
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(); } }
- 自动配置类
@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; } }
- 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>