JAVA框架技术之十八节springboot课件上手教程(一)https://developer.aliyun.com/article/1432888
4.SpringBoot实战
接下来,我们来看看如何用SpringBoot来玩转以前的SSM,我们沿用之前讲解SSM用到的数据库tb_user和实体类User。
4.1.创建工程
4.2.编写基本代码
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>cn.yh.user</groupId> <artifactId>yh-user</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.6.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> </project>
参照上边的项目,编写引导类:
@SpringBootApplication public class UserApplication { public static void main(String[] args) { SpringApplication.run(UserApplication.class); } }
编写UserController:
@RestController @RequestMapping("user") public class UserController { @GetMapping("hello") public String test(){ return "hello ssm"; } }
4.3.整合SpringMVC
虽然默认配置已经可以使用SpringMVC了,不过我们有时候需要进行自定义配置。
4.3.1.修改端口
添加全局配置文件:application.properties
端口通过以下方式配置
# 映射端口 server.port=80
重启服务后测试:
4.3.2.访问静态资源
现在,我们的项目是一个jar工程,那么就没有webapp,我们的静态资源该放哪里呢?
回顾我们上面看的源码,有一个叫做ResourceProperties的类,里面就定义了静态资源的默认查找路径:
默认的静态资源路径为:
- classpath:/META-INF/resources/
- classpath:/resources/
- classpath:/static/
- classpath:/public/
只要静态资源放在这些目录中任何一个,SpringMVC都会帮我们处理。
我们习惯会把静态资源放在classpath:/static/
目录下。我们创建目录,并且添加一些静态资源:
重启项目后测试:
在浏览器直接测试,具体内容由你而定。
4.4.整合连接池
HikariCP 是一个高性能的 JDBC 连接池组件,号称性能最好的后起之秀,是一个基于BoneCP做了不少的改进和优化的高性能JDBC连接池。
其作者还有产出了另外一个开源作品HikariJSON——高性能的JSON解析器。
代码体积更是少的可怜,130kb。Spring Boot 2都已经宣布支持了该组件,由之前的Tomcat换成HikariCP。
其性能远高于c3p0、tomcat等连接池,以致后来BoneCP作者都放弃了维护,在Github项目主页推荐大家使用HikariCP。
上边说到是在BoneCP基础上做了优化,那做了哪些优化呢?
字节码精简 :优化代码(HikariCP利用了一个第三方的Java字节码修改类库Javassist来生成委托实现动态代理,动态代理的实现在ProxyFactory类),直到编译后的字节码最少,这样,CPU缓存可以加载更多的程序代码;
优化代理和拦截器:减少代码,例如HikariCP的Statement proxy只有100行代码,只有BoneCP的十分之一;
自定义数组类型(FastStatementList)代替ArrayList:避免每次get()调用都要进行range check,避免调用remove()时的从头到尾的扫描,相对与ArrayList极大地提升了性能,而其中的区别是,ArrayList在每次执行get(Index)方法时,都需要对List的范围进行检查,而FastStatementList不需要,在能确保范围的合法性的情况下,可以省去范围检查的开销。
自定义集合类型(ConcurrentBag):支持快速插入和删除,特别是在同一线程既添加又删除项时,提高并发读写的效率;
针对CPU的时间片算法进行优化:尽可能在一个时间片里面完成各种操作(具体机制比较模糊)。
针对连接中断的情况:比其他CP响应时间上有了极好的优化,响应时间为5S,会抛出SqlException异常,并且后续的getConnection()可以正常进行
关于Connection的操作:另外在Java代码中,很多都是在使用完之后直接关闭连接,以前都是从头到尾遍历,来关闭对应的Connection,而HikariCP则是从尾部对Connection集合进行扫描,整体上来说,从尾部开始的性能更好一些。
jdbc连接池是spring配置中的重要一环,在SpringBoot中该如何处理呢?
答案是不需要处理,我们只要找到SpringBoot提供的启动器即可:
在pom.xml中引入jdbc的启动器:
<!--jdbc的启动器,默认使用HikariCP连接池--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!--不要忘记数据库驱动,因为springboot不知道我们使用的什么数据库,这里选择mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
SpringBoot已经自动帮我们引入了一个连接池:
HikariCP应该是目前速度最快的连接池了,我们看看它与c3p0的对比:
maximum-pool-size 池中最大连接数(包括空闲和正在使用的连接)
minimum-idle 池中最小空闲连接数量。默认值10
pool-name 连接池的名字
auto-commit 是否自动提交池中返回的连接。默认值为true。
idle-timeout 空闲时间。仅在minimum-idle小于maximum-poop-size的时候才会起作用。默认值10分钟。
max-lifetime 连接池中连接的最大生命周期。当连接一致处于闲置状态时,数据库可能会主动断开连接。
connection-timeout 连接超时时间。默认值为30s,可以接收的最小超时时间为250ms。
我们只需要指定连接池参数即可:
# 连接四大参数 spring.datasource.password=root spring.datasource.username=root spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/test #连接池:参数 spring.datasource.hikari.idle-timeout=60000 spring.datasource.hikari.maximum-pool-size=30 spring.datasource.hikari.minimum-idle=10
当然,如果你更喜欢Druid连接池,也可以使用Druid官方提供的启动器:
<!-- 阿里:Druid连接池;效果也非常高; --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.6</version> </dependency>
而连接信息的配置与上面是类似的,只不过在连接池特有属性上,方式略有不同:
#初始化连接数 spring.datasource.druid.initial-size=1 #最小空闲连接 spring.datasource.druid.min-idle=1 #最大活动连接 spring.datasource.druid.max-active=20 #获取连接时测试是否可用 spring.datasource.druid.test-on-borrow=true #监控页面启动 spring.datasource.druid.stat-view-servlet.allow=true
4.5.整合mybatis
使用一下注释可以配置自己定义的AutoConfig
@ConditionalOnBean:当容器中有指定的Bean的条件下
@ConditionalOnClass:当类路径下有指定的类的条件下
@ConditionalOnMissingBean:当容器中没有指定Bean的情况下
@ConditionalOnMissingClass:当类路径下没有指定的类的条件下
此处的源码没图,看秘增视频。
4.5.1.mybatis
SpringBoot官方并没有提供Mybatis的启动器,不过Mybatis官方自己实现了:
<!--mybatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency>
配置(基本没有需要配置,我们这里没有写,大家别写,现在)
# mybatis 别名扫描 # mybatis.type-aliases-package=com.yh.pojo # mapper.xml文件位置,如果没有映射文件,请注释掉 #mybatis.mapper-locations=classpath:mappers/*.xml
需要注意,这里没有配置mapper接口扫描包,因此我们需要给每一个Mapper接口添加@Mapper
注解,才能被识别。
@Mapper注解是由Mybatis框架中定义的一个描述数据层接口的注解,注解往往起到的都是一个描述性作用,用于告诉sprigng框架此接口的实现类由Mybatis负责创建,并将其实现类对象存储到spring容器中。在编译之后会生成相应的接口实现类。
@MapperScan注解的使用
作用:指定要变成实现类的接口所在的包,包下面的所有接口在编译之后都会生成相应的实现类。
@Mapper @Repository public interface StockMapper { @Select("select * from stock") public List<Stock> findAllStocks(); }
Stock对象
@Data @AllArgsConstructor @NoArgsConstructor public class Stock { private int id; //编号 private String name; //名; private int num; //数量 private String desp; //描述信息; }
业务接口和实现类:
public interface StockService { public List<Stock> findAllStocks(); } 实现类:在impl下加一个实体类; /** * 3.service层:业务层(服务层),来调用mapper(dao)层对象;StockMapper * 3.1 需要声明一个成员变量;stockMapper; Autowired:,自动注入,由于spring是IOC机制; * 3.2 在方法内部,由stockMapper,提供数据 * 3.3 千万别忘了,@Service 服务注解; */ @Service public class StockServiceImpl implements StockService { @Autowired StockMapper stockMapper; @Override public List<Stock> findAllStocks() { return stockMapper.findAllStocks(); } }
控制层,增加代码
@Autowired StockService stockService; //由数据库提供数据; @RequestMapping("/findAllStocks") public ModelAndView findAllStocks(){ ModelAndView mv=new ModelAndView("stocklist"); List<Stock>stockList=stockService.findAllStocks(); mv.addObject("stocklist",stockList); return mv; }
测试json数据输出
4.6.完整项目结构
完整的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>cn.yh</groupId> <artifactId>yh-user</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.6.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--jdbc的启动器,默认使用HikariCP连接池--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!--不要忘记数据库驱动,因为springboot不知道我们使用的什么数据库,这里选择mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--mybatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <!-- 通用mapper --> <dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper-spring-boot-starter</artifactId> <version>2.0.2</version> </dependency> </dependencies> </project>
完整的application.properties:
server.port=80 logging.level.org.springframework=debug spring.datasource.url=jdbc:mysql://localhost:3306/dms spring.datasource.username=root spring.datasource.password=root # mybatis 别名扫描 mybatis.type-aliases-package=cn.yh.pojo # mapper.xml文件位置,如果没有映射文件,请注释掉 # mybatis.mapper-locations=classpath:mappers/*.xml
5.Thymeleaf快速入门
SpringBoot并不推荐使用jsp,但是支持一些模板引擎技术。jsp需要服务器,tomcat、jetty,由服务器对页面的指令进行编译、解释,才能够执行。主流开发趋势,是前后端分离,前端页面上信息呈现,可以不依赖后台服务器,可以做预览。
以前大家用的比较多的是Freemarker,但是我们今天的主角是Thymeleaf!
5.1.为什么是Thymeleaf?
简单说, Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,它可以完全替代 JSP 。相较于其他的模板引擎,它有如下四个极吸引人的特点:
- 动静结合:Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。
- 开箱即用:它提供标准和spring标准两种方言,可以直接套用模板实现JSTL、 OGNL表达式效果,避免每天套模板、改jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。
- 多方言支持:Thymeleaf 提供spring标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。
- 与SpringBoot完美整合,SpringBoot提供了Thymeleaf的默认配置,并且为Thymeleaf设置了视图解析器,我们可以像以前操作jsp一样来操作Thymeleaf。代码几乎没有任何区别,就是在模板语法上有区别。
接下来,我们就通过入门案例来体会Thymeleaf的魅力:
5.2.提供数据
编写一个controller方法,返回一些用户数据,放入模型中,将来在页面渲染
@GetMapping("/all") public String all(ModelMap model) { // 查询用户 List<User> users = this.userService.queryAll(); // 放入模型 model.addAttribute("users", users); // 返回模板名称(就是classpath:/templates/目录下的html文件名) return "users"; }
5.3.引入启动器
直接引入启动器:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
5.3.1 把源码
SpringBoot会自动为Thymeleaf注册一个视图解析器:
与解析JSP的InternalViewResolver类似,Thymeleaf也会根据前缀和后缀来确定模板文件的位置:
- 默认前缀:
classpath:/templates/
- 默认后缀:
.html
所以如果我们返回视图:users
,会指向到 classpath:/templates/users.html
一般我们无需进行修改,默认即可。
5.4.静态页面
根据上面的文档介绍,模板默认放在classpath下的templates文件夹,我们新建一个html文件放入其中:
编写html模板,渲染模型中的数据:
注意,把html 的名称空间,改成:xmlns:th="http://www.thymeleaf.org"
会有语法提示
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首页</title> <style type="text/css"> table {border-collapse: collapse; font-size: 14px; width: 80%; margin: auto} table, th, td {border: 1px solid darkslategray;padding: 10px} </style> </head> <body> <div style="text-align: center"> <span style="color: darkslategray; font-size: 30px">欢迎光临!</span> <hr/> <table class="list"> <tr> <th>id</th> <th>姓名</th> <th>用户名</th> <th>年龄</th> <th>性别</th> <th>生日</th> </tr> <tr th:each="stock :${stockList}"> <td th:text="${stock.id}"></td> <td th:text="${stock.name}"></td> <td th:text="${stock.num}"></td> </tr> </div> </body> </html>
我们看到这里使用了以下语法:
${}
:这个类似与el表达式,但其实是ognl的语法,比el表达式更加强大th-
指令:th-
是利用了Html5中的自定义属性来实现的。如果不支持H5,可以用data-th-
来代替
th:each
:类似于c:foreach
遍历集合,但是语法更加简洁th:text
:声明标签中的文本
- 例如,如果user.id有值,会覆盖默认的1
- 如果没有值,则会显示td中默认的1。这正是thymeleaf能够动静结合的原因,模板解析失败不影响页面的显示效果,因为会显示默认值!
- th:if: 做条件判断
th:switch -th:case 做条件判断
忽略所有警告或错误
<!--解决idea thymeleaf 表达式模板报红波浪线,建议用这种。--> <!--suppress ALL -->
或者下面方式解决:
如果是IDEA设置是setting 下方的editor 的inspectors 找到查找窗口,thymeleaf ,第二个勾去掉。
5.5.测试
接下来,我们打开页面测试一下:
5.6.模板缓存
Thymeleaf会在第一次对模板解析之后进行缓存,极大的提高了并发处理能力。但是这给我们开发带来了不便,修改页面后并不会立刻看到效果,我们开发阶段可以关掉缓存使用:
# 开发阶段关闭thymeleaf的模板缓存 spring.thymeleaf.cache=false
注意:
在Idea中,我们需要在修改页面后按快捷键:`Ctrl + Shift + F9` 对项目进行rebuild才可以。 eclipse中没有测试过。
我们可以修改页面,测试一下。
附录:所有的标签,供参考。