Spring Boot是Spring项目中的一个子工程,与我们所熟知的Spring-framework 同属于spring的产品:
一般把Spring Boot称为搭建程序的脚手架或者说是便捷搭建基于Spring的工程 脚手架。其最主要作用就是帮助开
发人员快速的构建庞大的spring项目,并且尽可能的减少一切xml配置,做到开箱即用,迅速上手,让开发人员关注业务而非配置。
java一直被人诟病的一点就是臃肿、麻烦。
究其原因注意是两点:
复杂的配置
项目各种配置其实是开发时的损耗, 因为在思考 Spring 特性配置和解决业务问题之间需要进行思维切换,所以写
配置挤占了写应用程序逻辑的时间。
个是混乱的依赖管理
项目的依赖管理也是件吃力不讨好的事情。决定项目里要用哪些库就已经够让人头痛的了,你还要知道这些库的哪
个版本和其他库不会有冲突,这难题实在太棘手。并且,依赖管理也是一种损耗,添加依赖不是写应用程序代码。
一旦选错了依赖的版本,随之而来的不兼容问题毫无疑问会是生产力杀手。
而Spring Boot让这一切成为过去!
Spring Boot 简化了基于Spring的应用开发,只需要“run”就能创建一个独立的、生产级别的Spring应用。 Spring Boot为Spring平台及第三方库提供开箱即用的设置(提供默认设置,存放默认配置的包就是启动器 starter),这样我们就可以简单的开始。多数Spring Boot应用只需要很少的Spring配置。
我们可以使用Spring Boot创建java应用,并使用java –jar 启动它,就能得到一个生产级别的web工程。
接下来,我们就来利用Spring Boot搭建一个web工程,体会一下Spring Boot的魅力所在!
使用idea工具创建一个maven工程,该工程为普通的java工程即可
看到这里很多同学会有疑惑,前面说传统开发的问题之一就是依赖管理混乱,怎么这里我们还需要管理依赖呢?难
道Spring Boot不帮我们管理吗?
别着急,现在我们的项目与Spring Boot还没有什么关联。Spring Boot提供了一个名为spring-boot-starter-parent的工程,里面已经对各种常用依赖(并非全部)的版本进行了管理,我们的项目需要以这个项目为父工程,这样我们就不用操心依赖的版本问题了,需要什么依赖,直接引入坐标即可 。
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring‐boot‐starter‐parent</artifactId> <version>2.1.5.RELEASE</version> </parent>
为了让Spring Boot帮我们完成各种自动配置,我们必须引入Spring Boot提供的自动配置依赖,我们称为 启动器
。因为我们是web项目,这里我们引入
web启动器,在 pom.xml 文件中加入如下依赖:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring‐boot‐starter‐web</artifactId> </dependency> </dependencies>
需要注意的是,我们并没有在这里指定版本信息。因为Spring Boot的父工程已经对版本进行了管理了。这个时
候,我们会发现项目中多出了大量的依赖。那些依赖都是Spring Boot根据 spring-boot-starter-web 这个依赖自动引入的,而且所有的版本都已经管理
好,不会出现冲突 。
要通过SpringBoot提供的引导类起步SpringBoot才可以进行访问
package com.summer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MySpringBootApplication { public static void main(String[] args) { SpringApplication.run(MySpringBootApplication.class); } }
在引导类MySpringBootApplication同级包或者子级包中创建QuickStartController
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class QuickController { @RequestMapping("/quick") @ResponseBody public String quick(){ return "springboot 访问成功!"; } }
@ResponseBody注解作用 其实是将java对象转为json格式的数据响应给客户端,如果返回值是字符串,那 么直接将字符串写到客户端
执行SpringBoot起步类的主方法,控制台打印日志如下:
通过日志发现,Tomcat started on port(s): 8080 (http) with context path ‘’
tomcat已经起步,端口监听8080,web应用的虚拟工程名称为空
打开浏览器访问url地址为:http://localhost:8080/quick
我们在开发中反复修改类、页面等资源,每次修改后都是需要重新启动才生效,这样每次启动都很麻烦,浪费了大
量的时间,我们可以在修改代码后不重启就能生效,在 pom.xml 中添加如下配置就可以实现这样的功能,我们称
之为热部署 。
<!‐‐热部署配置‐‐> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring‐boot‐devtools</artifactId> </dependency>
注意:IDEA进行SpringBoot热部署失败原因
出现这种情况,并不是热部署配置问题,其根本原因是因为Intellij IEDA默认情况下不会自动编译,需要对IDEA进
行自动编译的设置,如下:
然后 Shift+Ctrl+Alt+/,选择Registry
通过idea快速创建的SpringBoot项目的pom.xml中已经导入了我们选择的web的起步依赖的坐标
可以使用快速入门的方式创建Controller进行访问,此处不再赘述
在入门案例中,我们没有任何的配置,就可以实现一个SpringMVC的项目了,快速、高效!但是有同学会有疑问,如果没有任何的xml,那么我们如果要配置一个Bean该怎么办?比如我们要配置一个数据库连接池,以前会这么配置:
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource"> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean>
事实上,在Spring3.0开始,Spring官方就已经开始推荐使用java配置来代替传统的xml配置了,我们不妨来回顾一
下 Spring的历史:
Spring1.0时代
在此时因为jdk1.5刚刚出来,注解开发并未盛行,因此一切Spring配置都是xml格式,想象一下所有的bean都
用 xml配置,细思极恐啊,心疼那个时候的程序员2秒
Spring2.0时代
Spring引入了注解开发,但是因为并不完善,因此并未完全替代xml,此时的程序员往往是把xml与注解进行
结 合,貌似我们之前都是这种方式
Spring3.0及以后
3.0以后Spring的注解已经非常完善了,因此Spring推荐大家使用完全的java配置来代替以前的xml,不过似乎
在国内并未推广盛行。然后当Spring Boot来临,人们才慢慢认识到java配置的优雅。
有句古话说的好:拥抱变化,拥抱未来。所以我们也应该顺应时代潮流,做时尚的弄潮儿,一起来学习下java配置的玩法。
java配置主要靠java类和一些注解,比较常用的注解有:
我们接下来用java配置来尝试实现连接池配置:
<!‐‐ Druid连接池 ‐‐> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency>
2.jdbc.properties
jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/test jdbc.username=root jdbc.password=root
配置类
@Configuration @PropertySource("classpath:jdbc.properties") public class JdbcConfig { @Value("${jdbc.url}") String url; @Value("${jdbc.driverClassName}") String driverClassName; @Value("${jdbc.username}") String username; @Value("${jdbc.password}") String password; @Bean public DataSource dataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(driverClassName); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; } }
@Controller public class QuickController { @Autowired private DataSource dataSource; @RequestMapping("/quick") @ResponseBody public String hello() { System.out.println("dataSource = " + dataSource); return "hello, spring boot!"; } }
在上面的案例中 我们实验了java配置方式。不过属性注入使用的是@Value注解。这种方式虽然可行,但是不够强
大,因为它只能注入基本类型值。
在Spring Boot中,提供了一种新的属性注入方式,支持各种java基本数据类型及复杂类型的注入。属性文件的名
称有变化,默认的文件名必须是:
application.properties
或application.yml
/ /将该类声明为属性读取类 @ConfigurationProperties(prefix ="jdbc") public class JdbcProperties { private String driverClassName; private String url; private String username; private String password; // ... 略 // getters 和 setters }
如果要去掉上述的提示,则可以在 pom.xml 文件中添加如下依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring‐boot‐configuration‐processor</artifactId> <!‐‐不传递依赖‐‐> <optional>true</optional> </dependency>
优雅的注入
@Configuration @EnableConfigurationProperties(JdbcProperties.class) public class JdbcConfig { @Bean public DataSource dataSource(JdbcProperties jdbc) { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl(jdbc.getUrl()); dataSource.setDriverClassName(jdbc.getDriverClassName()); dataSource.setUsername(jdbc.getUsername()); dataSource.setPassword(jdbc.getPassword()); return dataSource; } }
更优雅的注入
事实上,如果一段属性只有一个Bean需要使用,我们无需将其注入到一个类(JdbcProperties,将该类上的所有注
解去掉)中。而是直接在需要的地方声明即可;再次修改 JdbcConfig 类为如下代码:
@Configuration public class JdbcConfig { @Bean // 声明要注入的属性前缀,Spring Boot会自动把相关属性通过set方法注入到DataSource中 @ConfigurationProperties(prefix = "jdbc") public DataSource dataSource() { return new DruidDataSource(); } }
配置文件除了可以使用application.properties类型,还可以使用后缀名为:.yml或者.yaml的类型,也就是:
application.yml或者application.yaml
yaml与properties配置文件除了展示形式不相同以外,其它功能和作用都是一样的;在项目中原路的读取方式不需
要改变。
yml配置文件的特征:
基本格式:
jdbc: driverClassName : com.mysql.jdbc.Driver url : jdbc:mysql://localhost:3306/kw username : root password : root
把application.properties修改为application.yml进行测试。
使用Spring Boot之后,一个整合了SpringMVC的WEB工程开发,变的无比简单,那些繁杂的配置都消失不见了,
这是如何做到的?
一切魔力的开始,都是从我们的main函数来的,所以我们再次来看下启动类:
按住Ctrl点击查看启动类 MySpringBootApplication 上的注解 @SpringBootApplication
//创建springboot工程的起步引导类,工程的入口 //自动扫描当前类所在的包以其子包 @SpringBootApplication public class MySpringBootApplication { public static void main(String[] args) { //运行springboot应用 SpringApplication.run(MySpringBootApplication.class,args); } }
注解 @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 { /** * Exclude specific auto‐configuration classes such that they will never be applied. * @return the classes to exclude */ @AliasFor(annotation = EnableAutoConfiguration.class) Class<?>[] exclude() default {}; ... ... ... }
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { ... ... ... }
其中,@EnableAutoConfifiguration的作用就是从META-INF/spring.factories文件中读取有关自动配置的配
置信息
spring.factories 文件中有关自动配置的配置信息如下:
上面配置文件存在大量的以AutoConfiguration为结尾的类名称,这些类就是存有自动配置信息的类,而
SpringApplication在获取这些类名后再加载
我们以ServletWebServerFactoryAutoConfiguration为例来分析源码:
@Configuration @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @ConditionalOnClass(ServletRequest.class) @ConditionalOnWebApplication(type = Type.SERVLET) @EnableConfigurationProperties(ServerProperties.class) @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class }) public class ServletWebServerFactoryAutoConfiguration { ... ... ... }
其中,
@EnableConfigurationProperties(ServerProperties.class) 代表加载ServerProperties服务器配置属性类,这种玩
法我们玩过~~
进入ServerProperties.class源码如下:
@ ConfigurationProperties(prefix = "server", ignoreUnknownFields = true) public class ServerProperties { /** * Server HTTP port. */ private Integer port; /** * Network address to which the server should bind. */ private InetAddress address; ... ... ... }
其中,
prefix = “server” 表示SpringBoot配置文件中的前缀,SpringBoot会将配置文件中以server开始的属性映射到该类
的字段中。映射关系如下:
配置组件扫描的指令。提供了类似与 <context:component-scan> 标签的作用 通过basePackageClasses或者basePackages属性来指定要扫描的包。如果没有指定这些属性,那么将 从声明这个注解的类所在的包开始,扫描包及子包
而我们的@SpringBootApplication注解声明的类就是main函数所在的启动类,因此扫描的包是该类所在包及
其子包。
因此,一般启动类会放在一个比较前的包目录中。
虽然默认配置已经可以使用SpringMVC了,不过我们有时候需要进行自定义配置。
查看SpringBoot的全局属性可知,端口通过以下方式配置:
server: port: 80
现在,我们的项目是一个jar工程,那么就没有webapp,我们的静态资源该放哪里呢?
回顾我们在上面看的源码,有一个叫做ResourceProperties的类,里面就定义了静态资源的默认查找路径
注意:如果访问图片时候没有显示;可以先将项目先clean再启动,或者创建 public、resources 文件夹,然 后 图 片放置到public或resources中。
spring中的jdbc连接和事务是配置中的重要一环,在SpringBoot中该如何处理呢?
答案是不需要处理,我们只要找到SpringBoot提供的启动器即可,在 pom.xml 文件中添加如下依赖:
<!‐‐整合jdbc和事务‐‐> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring‐boot‐starter‐jdbc</artifactId> </dependency>
当然,不要忘了数据库驱动,SpringBoot并不知道我们用的什么数据库,这里我们选择MySQL;同样的在
pom.xml文件中添加如下依赖:
<dependency> <groupId>mysql</groupId> <artifactId>mysql‐connector‐java</artifactId> </dependency>
至于事务,SpringBoot中通过注解来控制。就是我们熟知的 @Transactional 使用的时候设置在对应的类或方法上
即可。
其实,在刚才引入jdbc启动器的时候,SpringBoot已经自动帮我们引入了一个连接池:
HikariCP应该是目前速度最快的连接池了,我们看看它与c3p0的对比:
因此,我们只需要指定连接池参数即可;打开 application.yml 添加修改配置如下:
spring: datasource: driver‐class‐name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF‐ 8&serverTimezone=UTC username: root password: root
添加Mybatis的起步依赖
SpringBoot官方并没有提供Mybatis的启动器,不过Mybatis官网自己实现了。在项目的 pom.xml 文件中加入如下
依赖:
<!‐‐mybatis起步依赖‐‐> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis‐spring‐boot‐starter</artifactId> <version>2.1.2</version> </dependency>
添加数据库驱动坐标
<!‐‐ MySQL连接驱动 ‐‐> <dependency> <groupId>mysql</groupId> <artifactId>mysql‐connector‐java</artifactId> </dependency>
配置 application.yml ,常用配置如下:
#mybatis配置 mybatis: type‐aliases‐package: com.bailiban.domain mapper‐locations: classpath:com/bailiban/mapper/*Mapper.xml configuration: log‐impl: org.apache.ibatis.logging.stdout.StdOutImpl
配置Mapper扫描
需要注意,这里没有配置mapper接口扫描包,因此我们需要给每一个Mapper接口添加 @Mapper 注解,才能被
识别。
@Mapper public interface UserMapper { }
或者,我们也可以不加注解,而是在启动类上添加扫描包注解(推荐):
@SpringBootApplication @MapperScan("com.bailiban.mapper") public class MySpringBootApplication { public static void main(String[] args) { SpringApplication.run(MySpringBootApplication.class); } }
在test数据库中创建user表
DROP table if EXISTS user; ##用户 CREATE TABLE `user` ( `uid` INT AUTO_INCREMENT , `username` VARCHAR(20) DEFAULT NULL, `password` VARCHAR(20) DEFAULT NULL, `realname` VARCHAR(20) DEFAULT NULL, `email` VARCHAR(30) DEFAULT NULL, `telephone` VARCHAR(20) DEFAULT NULL, PRIMARY KEY (`uid`) ); INSERT INTO `user` VALUES (NULL,'wc001','888888','旺财','aaa@store.com','15723689921'), (NULL,'xq001','888888','小强','bbb@store.com','15723689922'), (NULL,'xb001','888888','小宝','ccc@store.com','15723689923');
package com.bailiban.domain; public class User { private int uid;//用户编号 private String username;//用户名 private String password;//密码 private String realname;//真实姓名 private String email;//邮箱 private String telephone;// 电话 //省略 setter getter方法 }
@Mapper public interface UserMapper { public List<User> queryUserList(); }
注意:@Mapper标记该类是一个mybatis的mapper接口,可以被spring boot自动扫描到spring上下文中
在 src/main/resources/com/bailiban/mapper 路径下加入UserMapper.xml配置文件"
< ?xml version="1.0" encoding="utf‐8" ?> <!DOCTYPE mapper PUBLIC "‐//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis‐3‐ mapper.dtd" > <mapper namespace="com.bailiban.mapper.UserMapper"> <select id="queryUserList" resultType="user"> select * from user </select> </mapper>
#mybatis配置 mybatis: type‐aliases‐package: com.bailiban.domain mapper‐locations: classpath:com/bailiban/mapper/*Mapper.xml configuration: log‐impl: org.apache.ibatis.logging.stdout.StdOutImpl
@Controller public class MapperController { @Autowired private UserMapper userMapper; @RequestMapping("/queryUser") @ResponseBody public List<User> queryUser(){ List<User> users = userMapper.queryUserList(); return users; } }
在springboot项目中如果要使用Junit进行单元测试,则需要添加如下的启动器:
<!‐‐单元测试启动器‐‐> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis‐spring‐starter‐test</artifactId> </dependency>
在测试包下编写测试类
在测试类上面必须要添加 @SpringBootTest 注解。
import java.util.List; @RunWith(SpringRunner.class) @SpringBootTest public class UserServiceTest { @Autowired private UserService userService; @Test public void test1(){ List<User> users = userService.selectAll(); System.out.println(users); } }
概念: redis是一款高性能的NOSQL系列的非关系型数据库
NoSQL(NoSQL = Not Only SQL),意即“不仅仅是SQL”,是一项全新的数据库理念,泛指非关系型的数据库。
随着互联网web2.0网站的兴起,传统的关系数据库在应付web2.0网站,特别是超大规模和高并发的SNS类型的
web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得
到了非常迅速的发展。NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题
优点:
优点:
成本:nosql数据库简单易部署,基本都是开源软件,不需要像使用oracle那样花费大量成本购买使用,相比
关系型数据库价格便宜。
查询速度:nosql数据库将数据存储于缓存之中,关系型数据库将数据存储在硬盘中,自然查询速度远不及
nosql数据库。
存储数据的格式:nosql的存储格式是key,value形式、文档形式、图片形式等等,所以可以存储基础类型以及对象或者是集合等各种格式,而数据库则只支持基础类型。
扩展性:关系型数据库有类似join这样的多表查询机制的限制导致扩展很艰难。
缺点:
维护的工具和资料有限,因为nosql是属于新的技术,不能和关系型数据库10几年的技术同日而语。
不提供对sql的支持,如果不支持sql这样的工业标准,将产生一定用户的学习和使用成本。
不提供关系型数据库对事务的处理。
总结:
关系型数据库与NoSQL数据库并非对立而是互补的关系,即通常情况下使用关系型数据库,在适合使用NoSQL的
时候使用NoSQL数据库,让NoSQL数据库对关系型数据库的不足进行弥补。
一般会将数据存储在关系型数据库中,在nosql数据库中备份存储关系型数据库的数据
相关产品: Tokyo Cabinet/Tyrant、Redis、Voldemort、Berkeley DB
典型应用: 内容缓存,主要用于处理大量数据的高访问负载。
数据模型: 一系列键值对
优势: 快速查询
劣势: 存储的数据缺少结构化
文档型数据库
相关产品:CouchDB、MongoDB
典型应用:Web应用(与Key-Value类似,Value是结构化的)
数据模型: 一系列键值对
优势:数据结构要求不严格
劣势: 查询性能不高,而且缺乏统一的查询语法
Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库,官方提供测试数据,50个并发执行
100000个请求,读的速度是110000次/s,写的速度是81000次/s ,且Redis通过提供多种键值数据类型来适应不同场
景下的存储需求,目前为止Redis支持的键值数据类型如下:
\1. 字符串类型 string
\2. 哈希类型 hash
\3. 列表类型 list
\4. 集合类型 set
\5. 有序集合类型 sortedset
在 pom.xml 文件中添加如下依赖
<!‐‐redis‐‐> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring‐boot‐starter‐data‐redis</artifactId> </dependency>
配置 application.yml 文件
spring: redis: host: localhost port: 6379
测试代码
package com.summer.test; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.test.context.junit4.SpringRunner; import java.util.*; @RunWith(SpringRunner.class) @SpringBootTest public class RedisTest { @Autowired private RedisTemplate redisTemplate; @Test public void test1() { //保存字符串 最常用的 redisTemplate.opsForValue().set("hello", "哈哈"); //获取 String hello = (String) redisTemplate.opsForValue().get("hello"); System.out.println(hello); //保存和读取set redisTemplate.opsForSet().add("s_key", "a", "b", "c"); Set set = redisTemplate.opsForSet().members("s_key"); System.out.println("set集合"+set); //保存和读取List List<String> list = Arrays.asList("a", "b", "c"); redisTemplate.opsForList().leftPush("l_key", list); //获取 List<String> list1 = (List<String>) redisTemplate.opsForList().leftPop("l_key"); System.out.println("list集合"+list1); //保存和读取map集合 Map<String, String> map = new HashMap<>(); map.put("name", "旺财"); map.put("age", "18"); redisTemplate.opsForHash().putAll("h_key", map); //获取 map Map<String, String> map1 = redisTemplate.opsForHash().entries("h_key"); //获取key的集合 Set key_set = redisTemplate.opsForHash().keys("h_key"); //获取value的集合 List value_list = redisTemplate.opsForHash().values("h_key"); System.out.println("key的集合:"+key_set); System.out.println("value的集合:"+value_list); System.out.println("map集合:"+map1); //保存有序的set redisTemplate.opsForZSet().add("z_key","a",10); redisTemplate.opsForZSet().add("z_key","b",20); redisTemplate.opsForZSet().add("z_key","c",30); //获取 Set z_set = redisTemplate.opsForZSet().range("z_key", 0, ‐1); System.out.println("有序的set"+z_set); } }