https://github.com/leechenxiang/imooc-springboot-starter这是课程老师的github,大家可以去down代码。
6 配置devtools热部署工具(IDEA)
热部署:热部署
devtools可以实现页面热部署(即页面修改后会立即生效,
这个可以直接在application.properties文件中配置spring.thymeleaf.cache=false来实现)
实现类文件热部署(类文件修改后不会立即生效),实现对属性文件的热部署。
即devtools会监听classpath下的文件变动,并且会立即重启应用(发生在保存时机),
注意:因为其采用的虚拟机机制,该项重启是很快的
(1)base classloader (Base类加载器):加载不改变的Class,例如:第三方提供的jar包。
(2)restart classloader(Restart类加载器):加载正在开发的Class。
为什么重启很快,因为重启的时候只是加载了在开发的Class,没有重新加载第三方的jar包。
(1)配置maven
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<!-- optional=true, 依赖不会传递, 该项目依赖devtools;
之后依赖boot项目的项目如果想要使用devtools, 需要重新引入 -->
<optional>true</optional>
</dependency>
(2)配置application.properties文件
#关闭缓存, 即时刷新
#spring.freemarker.cache=false
#spring.thymeleaf.cache=true
#热部署生效
spring.devtools.restart.enabled=true
#设置重启的目录,添加那个目录的文件需要restart
spring.devtools.restart.additional-paths=src/main/java
#为mybatis设置,生产环境可删除
restart.include.mapper=/mapper-[\\w-\\.]+jar
restart.include.pagehelper=/pagehelper-[\\w-\\.]+jar
#排除那个目录的文件不需要restart
#spring.devtools.restart.exclude=static/**,public/**
#classpath目录下的WEB-INF文件夹内容修改不重启
#spring.devtools.restart.exclude=WEB-INF/**
(3)如果你是eclipse或者sts,应该就可以了,然后IDEA需要再配置一下
1) “File” -> “Settings” -> “Build,Execution,Deplyment” -> “Compiler”,选中打勾 “Build project automatically” 。
2) 组合键:“Shift+Ctrl+Alt+/” ,选择 “Registry” ,选中打勾 “compiler.automake.allow.when.app.running” 。
(4)修改自己的文件然后控制台会显示重新启动
7 从配置文件读取实体类数据
这里有个问题没解决,有思路的麻烦可以给我解答一下,就是Date方式的读取
(1) 添加maven
<!--配置类与资源文件对应的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
(2) User.properties配置文件(放在resources路径下就可以)
com.zpr.autouser.name = 菲菲
com.zpr.autouser.password = 345
com.zpr.autouser.age = 34
#com.zpr.autouser.birthday = 2016-11-11
(3)在实体类上添加注解(我看到还有别的添加注解的方式,所以不仅仅这种方式配置)
@Component //神奇,不知道为什么@Configuration没有用,表明与配置文件对应
@ConfigurationProperties(prefix = “com.zpr.autouser”) //这里是配置文件的参数前面对应的前缀
@PropertySource(value = “classpath:user.properties”) //配置文件路径
(4)创建一个控制层方法
@RequestMapping("/getAutoUser")
public IMoocJSONResult getAutoUser(){
User bean = new User();
BeanUtils.copyProperties(user,bean);
return IMoocJSONResult.ok(bean);
}
返回数据:
{
"status": 200,
"msg": "OK",
"data": {
"name": "菲菲",
"age": 34,
"birthday": null
},
"ok": null
}
8 整合模板引擎(freemaker和thymeleaf)
敲黑板:前端页面的要放在templates(动态模板)路径下,两种模板又分别放在自己的路径下,好区分;
(1)添加maven
<!--添加freemaker模板-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
(2)添加配置(application.properties)
#freemarker 静态资源配置
#
############################################################
#设定ftl文件路径
spring.freemarker.template-loader-path=classpath:/templates
#关闭缓存, 即时刷新, 上线生产环境需要改为true
spring.freemarker.cache=false
spring.freemarker.charset=UTF-8
spring.freemarker.check-template-location=true
spring.freemarker.content-type=text/html
spring.freemarker.expose-request-attributes=true
spring.freemarker.expose-session-attributes=true
spring.freemarker.request-context-attribute=request
spring.freemarker.suffix=.ftl
(3)添加index.ftl文件
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8" />
<title></title>
</head>
<body>
FreeMarker模板引擎
<h1>${user.name}</h1>
<h1>${user.password}</h1>
<h1>${user.age}</h1>
</body>
</html>
(4)控制层访问路径方法
@Controller
@RequestMapping("ftl")
public class FreemarkerController {
@Autowired
private User user;
@RequestMapping("/index")
public String index(ModelMap map) {
map.addAttribute("user", user);
return "freemarker/index";
}
}
(5)访问结果
贴图片得先保存,我这么懒就直接贴页面代码
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8" />
<title></title>
</head>
<body>
FreeMarker模板引擎
<h1>菲菲</h1>
<h1>345</h1>
<h1>34</h1>
</body>
</html>
(6)thymleaf的配置和freemaker的配置大同小异
Maven
<!--添加thymeleaf模板-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
配置
############################################################
#
# thymeleaf 静态资源配置
#
############################################################
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.content-type=text/html
# 关闭缓存, 即时刷新, 上线生产环境需要改为true
spring.thymeleaf.cache=false
#设定静态文件路径,js,css等
spring.mvc.static-path-pattern=/static/**
控制层方法:
@RequestMapping("/index")
public String index(ModelMap map) {
map.addAttribute("name", "thymeleaf-imooc");
return "thymeleaf/index";
}
页面:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8" />
<title></title>
</head>
<body>
Thymeleaf模板引擎
<h1 th:text="${name}">hello world~~~~~~~</h1>
</body>
</html>
显示页面代码:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8" />
<title></title>
</head>
<body>
Thymeleaf模板引擎
<h1>thymeleaf-imooc</h1>
</body>
</html>
9 整合MyBatis和实现分页
这个是MyBatis插件作者的github,可以去看看:
https://github.com/abel533/Mapper/wiki
(1)添加maven
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<!--mapper-->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>1.2.4</version>
</dependency>
<!--pagehelper-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.39</version>
</dependency>
(2)配置文件(数据库要根据自己的)
############################################################
#
# 配置数据源相关 使用阿里巴巴的 druid 数据源
#
############################################################
spring.datasource.url=jdbc:mysql://localhost:3307/springboot1?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC&nullNamePatternMatchesAll=true
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
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
############################################################
#
# mybatis 配置
#
############################################################
# mybatis 配置
mybatis.type-aliases-package=com.zpr.pojo
mybatis.mapper-locations=classpath:mapper/*.xml
# 通用 Mapper 配置
mapper.mappers=com.zpr.utils.MyMapper
mapper.not-empty=false
mapper.identity=MYSQL
# 分页插件配置
pagehelper.helperDialect=mysql
pagehelper.reasonable=true
pagehelper.supportMethodsArguments=true
pagehelper.params=count=countSql
(3)生成逆向工程
添加generatorConfig.xml文件,用来从数据库生成对应的实体类等的,跟pom.xml在同一个路径
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="MysqlContext" targetRuntime="MyBatis3Simple" defaultModelType="flat">
<property name="beginningDelimiter" value="`"/>
<property name="endingDelimiter" value="`"/>
<plugin type="tk.mybatis.mapper.generator.MapperPlugin">
<property name="mappers" value="com.zpr.utils.MyMapper"/>
</plugin>
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3307/springboot1?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC&nullNamePatternMatchesAll=true"
userId="root"
password="123456">
</jdbcConnection>
<!-- 对于生成的pojo所在包 -->
<javaModelGenerator targetPackage="com.zpr.pojo" targetProject="src/main/java"/>
<!-- 对于生成的mapper所在目录 -->
<sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources"/>
<!-- 配置mapper对应的java映射 -->
<javaClientGenerator targetPackage="com.zpr.mapper" targetProject="src/main/java"
type="XMLMAPPER"/>
<table tableName="sys_user"></table>
</context>
</generatorConfiguration>
在utils包下添加GeneratorDisplay.java类
package com.zpr.utils;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
public class GeneratorDisplay {
public void generator() throws Exception{
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
//指定 逆向工程配置文件
File configFile = new File("generatorConfig.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
callback, warnings);
myBatisGenerator.generate(null);
}
public static void main(String[] args) throws Exception {
try {
GeneratorDisplay generatorSqlmap = new GeneratorDisplay();
generatorSqlmap.generator();
} catch (Exception e) {
e.printStackTrace();
}
}
}
还有一个MyMapper,用来给Mapper接口们继承的
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2016 abel533@gmail.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.zpr.utils;
import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.common.MySqlMapper;
/**
* 继承自己的MyMapper
*
* @author liuzh
* @since 2015-09-06 21:53
*/
public interface MyMapper<T> extends Mapper<T>, MySqlMapper<T> {
//TODO
//FIXME 特别注意,该接口不能被扫描到,否则会出错
}
然后在GeneratorDisplay.java类右键运行生成相应的实体类和Mapper接口
(4)在应用入口类添加注解,就是我们启动的那个类
//扫描mybatis mapper的包路径
@MapperScan(basePackages = "com.zpr.mapper")
//扫描需要的所有包,包含一些自用的工具类包所在的路径,后面那个是工具类,用来生成id的
@ComponentScan(basePackages = {"com.zpr","org.n3r.idworker"})
这里我后续的操作是需要在Mapper接口类上面添加@Component,不然注入的时候总是划红线,虽然不影响,强迫症。
其他是MyBatis的CRUD自己看代码吧。
10 定时任务
(1)在应用类添加注解
@EnableScheduling
(2)定时处理类
package com.zpr.task;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class TestTask {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
// 定义每过3秒执行任务
// @Scheduled(fixedRate = 3000)
// @Scheduled(cron = "3-45 * * * * ?")
public void reportCurrentTime() {
System.out.println("现在时间:" + dateFormat.format(new Date()));
}
}
启动类会自动开始定时执行,这个是cron时间表达式转换的工具网址: http://cron.qqe2.com/(记得年是不能用的,具体用法百度吧)
11 启动异步任务
(1)在入口类添加注解
//启动异步任务
@EnableAsync
(2)创建异步方法类
package com.zpr.task;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;
import java.util.concurrent.Future;
@Component
public class AsyncTask {
@Async
public Future<Boolean> doTask11() throws Exception {
long start = System.currentTimeMillis();
Thread.sleep(1000);
long end = System.currentTimeMillis();
System.out.println("任务1耗时:" + (end - start) + "毫秒");
return new AsyncResult<>(true);
}
@Async
public Future<Boolean> doTask22() throws Exception {
long start = System.currentTimeMillis();
Thread.sleep(700);
long end = System.currentTimeMillis();
System.out.println("任务2耗时:" + (end - start) + "毫秒");
return new AsyncResult<>(true);
}
@Async
public Future<Boolean> doTask33() throws Exception {
long start = System.currentTimeMillis();
Thread.sleep(600);
long end = System.currentTimeMillis();
System.out.println("任务3耗时:" + (end - start) + "毫秒");
return new AsyncResult<>(true);
}
}
(3)调用控制类:异步启动时,时间取最长的那个,但是由于程序也需要消耗一些时间,所以会比最大的时间再大一点
package com.zpr.controller;
import com.zpr.task.AsyncTask;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.Future;
@RestController
@RequestMapping("tasks")
public class DoTask {
@Autowired
private AsyncTask asyncTask;
@RequestMapping("test1")
public String test1() throws Exception {
long start = System.currentTimeMillis();
Future<Boolean> a = asyncTask.doTask11();
Future<Boolean> b = asyncTask.doTask22();
Future<Boolean> c = asyncTask.doTask33();
while (!a.isDone() || !b.isDone() || !c.isDone()) {
if (a.isDone() && b.isDone() && c.isDone()) {
break;
}
}
long end = System.currentTimeMillis();
String times = "任务全部完成,总耗时:" + (end - start) + "毫秒";
System.out.println(times);
return times;
}
}
12 拦截器
(1)创建一个类继承WebMvcConfigurerAdapter
package com.zpr.config;
import com.zpr.controller.interceptor.OneInterceptor;
import com.zpr.controller.interceptor.TwoInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class WebMvcConfigurer extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
/**
* 拦截器按照顺序执行
*/
registry.addInterceptor(new OneInterceptor()).addPathPatterns("/one/**");
// registry.addInterceptor(new TwoInterceptor()).addPathPatterns("/two/**")
// .addPathPatterns("/one/**");
super.addInterceptors(registry);
}
}
(2)创建一个拦截器类
package com.zpr.controller.interceptor;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.zpr.pojo.IMoocJSONResult;
import com.zpr.utils.JsonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class OneInterceptor implements HandlerInterceptor {
final static Logger log = LoggerFactory.getLogger(TwoInterceptor.class);
/**
* 在请求处理之前进行调用(Controller方法调用之前)
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object object) throws Exception {
// System.out.println("被one拦截,放行...");
log.error("被one拦截,放行...");
return true;
/*if (true) {
returnErrorResponse(response, IMoocJSONResult.errorMsg("被one拦截..."));
}
return false;*/
}
/**
* 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object object, ModelAndView mv)
throws Exception {
// TODO Auto-generated method stub
}
/**
* 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行
* (主要是用于进行资源清理工作)
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object object, Exception ex)
throws Exception {
// TODO Auto-generated method stub
}
public void returnErrorResponse(HttpServletResponse response, IMoocJSONResult result)
throws IOException, UnsupportedEncodingException {
OutputStream out=null;
try{
response.setCharacterEncoding("utf-8");
response.setContentType("text/json");
out = response.getOutputStream();
out.write(JsonUtils.objectToJson(result).getBytes("utf-8"));
out.flush();
} finally{
if(out!=null){
out.close();
}
}
}
}
(3)创建一个控制类方法测试
@Controller
@RequestMapping("one")
public class OneController {
@RequestMapping("/index")
public String index(ModelMap map) {
map.addAttribute("name", "imooc22");
return "thymeleaf/index";
}
}
(4)日志显示
2019-02-01 08:32:50.497 ERROR 5860 --- [nio-8080-exec-1] c.z.c.interceptor.TwoInterceptor : 被one拦截,放行...
这里我没有拦截,你们可以试着把另一个注释去掉就可以看到拦截效果了。
如果你想看到我的拙略代码,这里是我的github:
https://github.com/momozpr/JavaCode/tree/com/springboot/zpr-springboot-demo
可以逛逛顺便给我颗小星星,或者可以给我的博客点个赞,第二篇进阶博客不是一边学一边写的,是学完最后才写的,所以说不定有一些步骤会遗漏,发现的小伙伴可以给我留言,谢谢,继续加油!