Spring boot学习笔记
Quick Start
配置pom.xml文件 新增依赖
```xml
org.springframework.bootspring-boot-starter-parent2.1.6.RELEASE
org.springframework.boot spring-boot-starter-web
3. 创建Example.java
* 目录:src/main/java/Example.java
```java
import org.springframework.boot.*;
import org.springframework.boot.autoconfigure.*;
import org.springframework.web.bind.annotation.*;
@RestController
@EnableAutoConfiguration
public class Example {
@RequestMapping("/")
String home() {
return "Hello World!";
}
public static void main(String[] args) {
SpringApplication.run(Example.class, args);
}
}
注解开发
1. @SpringBootApplication
等价于@SpringBootConfiguration+ @EnableAutoConfiguration+ @ComponentScan
//@SpringBootApplication (一个替代下面三个)
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
2. @RestController
等价于 @Controller+@ResponseBody
注:@RestController与@RequestMapping是Spring MVC的注解,不是Spring Boot特有的
3.@RequestMapping,@PathVariable
/**
* 测试Restful协议 从路径中获取字段
* @param cityId cityId
* @param userId userId
* @return
*/
@RequestMapping(value = "/{city_id}/{user_id}",method = RequestMethod.GET)
public Object findUser(@PathVariable("city_id") String cityId, @PathVariable("user_id") String userId){
params.clear();
params.put("cityId",cityId);
params.put("userId",userId);
return params;
}
4.@GetMapping
简化method设置
/**
* 测试GetMapping
* @param from
* @param size
* @return
*/
@GetMapping("/v1/page_user1")
public Object getUser(int from,int size){
params.clear();
params.put("from",from);
params.put("size",size);
return params;
}
5.@RequestParam
/**
* 测试RequestParam 设置默认值
* @param from
* @param size
* @return
*/
@GetMapping("/v1/page_user2")
public Object getUser2(@RequestParam(defaultValue = "100",name = "page") int from,int size){
params.clear();
params.put("from",from);
params.put("size",size);
return params;
}
6.@RequestBody
请求头的 Content-Type需设置为application/json,参数放在body中
/**
* 测试RequestBody
* @param user
* @return
*/
@PostMapping("/v1/page_user3")
public Object getUser3(@RequestBody User user){
params.clear();
params.put("user",user);
return params;
}
/**
* 测试RequestHeaders
* @param token
* @param id
* @return
*/
@GetMapping("/v1/page_user4")
public Object getUser4(@RequestHeader("access_token")String token,String id){
params.clear();
params.put("access_token",token);
params.put("id",id);
return params;
}
8.HttpServletRequest
/**
* HttpServletRequest
* @param request
* @return
*/
@GetMapping("/v1/page_user5")
public Object getUser5(HttpServletRequest request){
params.clear();
params.put("id",request.getParameter("id"));
return params;
}
其它Http请求
private Map<String,Object> params=new HashMap<>();
@PostMapping("/test/post")
public Object testPost(Integer id){
params.clear();
params.put("id",id);
return params;
}
@DeleteMapping("/test/del")
public Object testDel(Integer id){
params.clear();
params.put("id",id);
return params;
}
@PutMapping("/test/put")
public Object testPut(Integer id){
params.clear();
params.put("id",id);
return params;
}
json处理(jackson)
public class Student {
// 使用别名
@JsonProperty("userName")
private String name;
// 省略
@JsonIgnore
private Integer age;
// 空字段不返回
@JsonInclude(JsonInclude.Include.NON_NULL)
private Character sex;
// 日期格式化
@JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss",locale = "zh",timezone = "GMT+8")
private Date birthday;
// 省略 getter and setter
//constructor
}
spring-boot 目录结构
src/main/java:存放代码
sec/main/resource:
static:存放静态文件
templates:存放静态页面文件
config:存放配置文件
resource:存放其它资源
同名静态资源,加载顺序
META/resources->resources->static->public 有则直接返回,没有则404
引入thymeleaf
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf<artifactId>
</dependency>
默认的模板映射路径是:src/main/resources/templates,
@Controller
public class PageController {
@RequestMapping(value = "/page/index")
public String index(){
return "index";
}
}
访问路径:localhost:8080/page/index
其它静态资源
src/main/resource
static
images
css
js
others
默认配置
spring.resource.static-locations=
classpath:/META-INF/resources/
更改配置写入自定义静态资源文件夹
resources目录下创建application.properties,加入test目录
spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,classpath:/test/
文件上传
服务端
private static final String SaveDirector="D:\\documents\\study\\maven\\project\\demo\\src\\main\\resources\\static\\images";
@PostMapping("/file/upload")
public Object uploadFile(MultipartFile file, HttpServletRequest request){
if(file.isEmpty()){
return null;
}
String filename = file.getOriginalFilename();
System.out.println(filename);
String suffix=filename.substring(filename.lastIndexOf("."));
filename= UUID.randomUUID()+filename.substring(0,filename.lastIndexOf("."));
filename=filename+suffix;
System.out.println(filename);
File saveFile=new File(SaveDirector+"\\"+filename);
try {
file.transferTo(saveFile);
} catch (IOException e) {
e.printStackTrace();
return new ResJSON(20001,filename,"上传失败");
}
return new ResJSON(200,filename,"上传成功");
}
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
<input type="file" id="file">
<button type="button" onclick="sureUpload()">上传</button>
<button type="button">取消</button>
<script>
let xhr=null;
//开始上传
function sureUpload() {
const file=document.getElementById('file').files[0],
form=new FormData();
xhr=new XMLHttpRequest();
form.append("file",file);
xhr.open("post","/file/upload",true);
xhr.onload=uploadSuccess;
xhr.onerror=uploadError;
xhr.onprogress=process;
xhr.send(form);
}
function uploadSuccess(e) {
console.log(JSON.parse(e.currentTarget.response));
alert("上传成功");
}
function uploadError(err) {
console.log(err);
alert("上传失败");
}
function cnacelUpload() {
xhr.abort();
}
function process(e) {
console.log("-----");
console.log(e.lengthComputable);
let max=e.max;
let loaded=e.loaded;
console.log(max,"---",loaded)
}
</script>
</body>
</html>
硬编码方式修改文件上传大小限制
# 上传文件大小限制
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
springProApplication文件(带有@SpringBootApplication注解)中添加
@Bean
public MultipartConfigElement multipartConfigElement(){
MultipartConfigFactory factory=new MultipartConfigFactory();
factory.setMaxFileSize("10240KB");
factory.setMaxRequestSize("102400KB");
return factory.createMultipartConfig();
}
打包jar包
引入maven插件
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
运行命令(Running as a Packaged Application)
java -jar target/myapplication-0.0.1-SNAPSHOT.jar
指定文件上传预览路径
配置application.properties
spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,classpath:/test/,file:D:/documents/study/pic/
文件服务器
pom引入插件
<!--热更新插件-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
注意:IDEA中需进行以下设置
不被热部署的文件
默认:/META-INF/maven,/META-INF/resources,/resources,/static,/public,/template
指定不进行热部署的文件
指定不监听application.properties文件,
在配置application.properties,中添加
spring.devtools.restart.exclude=application.properties
通过触发器,控制什么时候进行热更新
```properties
spring.devtools.restart.trigger-file=.reloadtrigger拦截器
## spring配置文件
[默认文档](https://docs.spring.io/spring-boot/docs/2.1.8.RELEASE/reference/htmlsingle/#common-application-properties)
## 配置文件自动映射到属性和实体类方式
1. 配置文件加载
* 方式1
* Controller上配置 @PropertySource({"classpath:xxxxx.properties"})
* xxxxx.properties
```properties
web.file.uploadDirectory=D:/documents/study/pic
取值
@Value("${web.file.uploadDirectory}")
private String saveDirectory="";
使用实体类配置文件
@PropertySource 注解指定配置文件位置
WConfigurationProperties 注解设置相关属性
必须用过注入IOC对象Resource,才能在类中获取到配置文件的值
@Autowired
private ServerSettings serverSettings;
示例
ServerSettings.java
@Component
@PropertySource({"classpath:common.properties"})
//如果带有prefix属性则可忽略@Value注解,自动根据配置文件同名自动注入
@ConfigurationProperties(prefix = "test")
public class ServerSettings {
//@Value("${test.name}")
private String name;
//@Value("${test.domain}")
private String domain;
//省略get-set方法
}
common.properties
#测试实体类配置文件
test.domain=sugarat.top
test.name=spring-boot
testController.java
```java
@Autowired
private ServerSettings serverSettings;
@GetMapping("/test/properties") public Object getProperties(){ return serverSettings; }
```
单元测试
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
普通测试
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {DemoApplication.class})
public class FirstTest {
@Test
public void firsttest(){
System.out.println("success");
//断言
TestCase.assertEquals(2,3);
}
// 测试之心之前启动
@Before
public void testBefore(){
System.out.println("before");
}
// 执行之后启动
@After
public void testAfter(){
System.out.println("after");
}
}
API测试
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {DemoApplication.class})
@AutoConfigureMockMvc
public class MockMvcTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testGetApi() throws Exception {
// perform 执行一个RequestBuilder请求
// andExpect添加ResultMatcher验证规则
// andReturn 最后返回对应的MvcResult
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/mock/test1"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andReturn();
int status = result.getResponse().getStatus();
System.out.println("success"+status);
}
}
个性化启动banner设置和debug日志
banner.txt
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: 自定义文字 spring boot:: (v2.1.8.RELEASE)
application.properties
spring.banner.location=banner.txt
打成jar包使用命令运行即可查看 启动debug日志
java -jar target/myapplication-0.0.1-SNAPSHOT.jar --debug
配置全局异常处理
@RestControllerAdvice
public class GlobalExHandler {
/**
* 全局异常
* @param e
* @param request
* @return
*/
@ExceptionHandler(value = Exception.class)
Object handleGlobalException(Exception e, HttpServletRequest request){
Map<String,Object> res=new HashMap<>();
res.put("code",555);
res.put("msg",e.getMessage());
res.put("url",request.getRequestURI());
return res;
}
}
自定义异常捕获
MyException.java
```java public class MyExcepion extends RuntimeException {
private Integer code;
private String msg;
public Integer getCode() { return code; }
public void setCode(Integer code) { this.code = code; }
public MyExcepion(Integer code, String msg) { this.code = code; this.msg = msg; }
public String getMsg() { return msg; }
public void setMsg(String msg) { this.msg = msg; } }
* HandleException.java
```java
@RestControllerAdvice
public class GlobalExHandler {
/**
* 捕获自定义异常
*/
@ExceptionHandler(value = MyExcepion.class)
Object handleMyException(MyExcepion e, HttpServletRequest request){
Map<String,Object> res=new HashMap<>();
res.put("code",e.getCode());
res.put("msg",e.getMsg());
res.put("url",request.getRequestURI());
return res;
}
}
部署war包于tomcat中
maven常规打包成war包放入tomcat Webapps目录
配置自定义过滤器
启动类中添加
@ServletComponentScan
创建一个Filter
```java
/**
urlPatterns支持正则
*/
@WebFilter(urlPatterns ={"/api/user/*"},filterName = "userFilter")
public class UserFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("user Filter Init");
}
@Override
public void destroy() {
System.out.println("user Filter destroy");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req= (HttpServletRequest)servletRequest;
HttpServletResponse resp=(HttpServletResponse) servletResponse;
String username=req.getParameter("username");
if(username.equals("admin")){
filterChain.doFilter(servletRequest,servletResponse);
}else{
return;
}
}
}
## Servlet3.0注解自定义原生Servlet
```java
@WebServlet(name = "userServlet",urlPatterns = "/user/servlet/*")
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(req.getRequestURI());
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().print("{name:\"小明\"}");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
}
Servlet3.0自定义原生Listerner
@WebListener
public class RequestListener implements ServletRequestListener {
private static Integer count=0;
@Override
public void requestDestroyed(ServletRequestEvent sre) {
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
count++;
System.out.println(count);
}
}
@WebListener
public class ContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("init Context");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("destryy Context");
}
}
spring boot2.0拦截器
CustomWebMvcConfigurer.java
@Configuration
public class CustomWebMvcConfigurer implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册拦截器
registry.addInterceptor(new UserIntercepter()).addPathPatterns("/api/user/**");
WebMvcConfigurer.super.addInterceptors(registry);
}
}
UserIntercepter.java
```java public class UserIntercepter implements HandlerInterceptor {
/**
进入controller之前 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println(request.getRequestURI()); System.out.println("UserIntercepter--->preHandle");
return request.getParameter("username").equals("admin"); }
/**
进入controller之后,是图渲染之前 */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("UserIntercepter--->postHandle"); }
/**
执行controller之后
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("UserIntercepter--->afterHandle");
}
}
* 补充
* /* 代表目录
* /**表示所有
## 整合Mybatis
### 引入依赖
```xml
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
修改启动类 增加 mapperScan扫描mapper
@SpringBootApplication
@MapperScan(value = "com.sugar.demo.mapper")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource(){
DruidDataSource druidDataSource=new DruidDataSource();
return druidDataSource;
}
}
配置数据源
#UTC 国际标注时
#GMT%2B8 北京东八区
spring.datasource.url=jdbc:mysql://localhost:3306/springboot?serverTimezone=GMT%2B8
spring.datasource.username=sugar
spring.datasource.password=a123456
#切换数据源
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#打印sql
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
编写mapper
使用#替代$ 防止sql注入
public interface UserMapper {
@Insert("insert into user(username,sex,phone,createAt) values(#{username},#{sex},#{tel},#{createData})")
@Options(useGeneratedKeys = true,keyProperty = "id",keyColumn = "id")
int insert(User user);
}
service调用
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public int addUser(User user) {
return userMapper.insert(user);
}
}
controller调用
@RestController
@RequestMapping("user")
public class UserController {
@Autowired
private UserService userService;
private HashMap<String,Object> res=new HashMap<>();
@PostMapping("add")
public Object registerUser(){
User test=new User(null,"sugar",'F',"15196520474",new Date());
res.clear();
res.put("code",200);
res.put("data",userService.addUser(test));
return res;
}
}
其它mapper
public interface UserMapper {
/**
* 新增用户
* @param user 新用户数据
* @return 新增用户id
*/
@Insert("insert into user(username,sex,phone,create_date) values(#{username},#{sex},#{tel},#{createData})")
@Options(useGeneratedKeys = true,keyProperty = "id",keyColumn = "id")
int insert(User user);
/**
* 查询所有的用户信息
* @return 用户列表
*/
@Select("select * from user")
@Results({
@Result(column = "create_date",property = "createData"),
@Result(column = "phone",property = "tel"),
@Result(column = "id",property = "id")
})
List<User> getAll();
/**
* 通过id删除User
* @param userId 用户id
*/
@Delete("delete from user where id = #{userId}")
void delete(Integer userId);
/**
* 通过id更新用户名字段
* @param user 新数据
*/
@Update("update user set username=#{username} where id =#{id}")
void update(User user);
}
事务
事务出错回滚
@PostMapping("testEvent")
@Transactional(propagation = Propagation.REQUIRED)
public Object testEvent(){
User test=new User("admin",12,"M","15196520474",new Date());
res.clear();
res.put("code",200);
res.put("data",userService.addUser(test));
int i=1/0;
return res;
}
Redis
相关链接
在线命令测试
redis官网
菜鸟教程
linux下
``` 下载 wget http://download.redis.io/releases/redis-5.0.5.tar.gz
解压 tar xzf redis-5.0.5.tar.gz
编译源码 cd redis-5.0.5 make
启动服务 ./src/redis-server
连接服务 ./src/redis-cli
测试 set test hello-world get test
# 整合redis
## 依赖
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
简单使用
public class testRedisController {
private HashMap<String,Object> res=new HashMap<>();
@Autowired
private StringRedisTemplate stringRedisTemplate;
@PostMapping("set")
public Object set(String key,String value){
res.clear();
res.put("key",key);
res.put("value",value);
stringRedisTemplate.opsForValue().set(key,value);
return res;
}
@GetMapping("get")
public Object get(String key){
res.clear();
res.put("key",key);
res.put("value",stringRedisTemplate.opsForValue().get(key));
return res;
}
}
Redis桌面应用
封装工具类
定时任务与异步任务处理
定时任务
启动类增加新注解
使用 (testTimerTask.class)
@Component
public class TestTimerTask {
//每隔5s执行一次
@Scheduled(fixedRate = 1000*5)
// 每隔2s执行一次
// @Scheduled(cron = "*/2 * * * * *")
// @Scheduled(fixedDelay=3000)
public void test1(){
System.out.println(new Date());
}
}
@fixedDelay 在上一次执行结束之后再隔 多久执行一次
@fixedDelayString 采用字符串作为参数
异步任务
启动类添加注解
@EnableAsync //开启异步任务
异步任务类
@Component
public class AsyncTask {
@Async
public void task1() throws InterruptedException {
long begin = System.currentTimeMillis();
Thread.sleep(3000L);
System.out.println(System
.currentTimeMillis() - begin);
}
}
业务调用
@RequestMapping("asnyc")
@RestController
public class testAsyncController {
@Autowired
private AsyncTask AsyncTask;
@GetMapping("task1")
public String testAsyncTask() throws InterruptedException {
AsyncTask.task1();
return "success";
}
}
等待异步回调完成
// 异步函数
@Async
public Future<String> task2() throws InterruptedException {
long begin = System.currentTimeMillis();
Thread.sleep(5000L);
System.out.println(System
.currentTimeMillis() - begin);
return new AsyncResult<String>("任务2完成结束");
}
// 等待回调完成
Future<String> task2 = AsyncTask.task2();
while (true){
if(task2.isDone()){
break;
}
}