Spring Boot学习笔记

创建一个springboot项目

操作流程

File->New->Project->Spring Initializr->Next(如果显示超时就选择Custom并填写我写的阿里云镜像)

在这里插入图片描述

填写相关信息->Next

在这里插入图片描述

选择Spring Boot版本并勾选相关依赖->Next->填写文件路径

在这里插入图片描述

springboot项目目录介绍

在这里插入图片描述

  • resources目录下必须有aplication.yml或application.properties核心配置文件
  • 在Java文件夹目录下有且只有一个项目入口类,一般是项目名+Application

Spring Boot启动显示项目信息

修改启动入口类:

@SpringBootApplication
public class ErpApplication{
    public static void main(String[] args) throws IOException {
        ConfigurableApplicationContext context = SpringApplication.run(ErpApplication.class, args);
        Environment environment = context.getBean(Environment.class);
        System.out.println("启动成功,后端服务API地址:http://" + ComputerInfo.getIpAddr() + ":"
                + environment.getProperty("server.port") + "/jshERP-boot/doc.html");
        System.out.println("您还需启动前端服务,启动命令:yarn run serve 或 npm run serve,测试用户:jsh,密码:123456");
    }
}

运行Spring Boot项目及相关注解介绍

添加一些代码

进入到你的xxxApplicaiton.java文件,填充相关注解和home方法

@RestController//表示这是一个控制器类
@SpringBootApplication//是组合注解,整合了EnableAutoConfiguration和ComponentScan
public class Example {
    @RequestMapping("/")//访问的接口
    public String home() {//处理的函数
        return "Hello World!";//返回值
    }
    public static void main(String[] args) {
        SpringApplication.run(Example.class, args);
    }
}

访问浏览器界面

右键运行程序,打开浏览器,url栏输入localhost:8080

然后就能看到Hello World!

相关注解介绍

  • @ComponentScan扫描当前包以及子包
  • @Controller:修饰class,用来创建处理http请求的对象
  • @RequestMapping()表示@xxxMapping的接口路径前面都会默认加上RequestMapping的路径,配置url映射。现在更多的也会直接用以Http Method直接关联的映射注解来定义,比如:GetMappingPostMappingDeleteMappingPutMapping
  • @RestController:Spring4之后加入的注解,原来在@Controller中返回json需要@ResponseBody来配合,如果直接用@RestController替代@Controller就不需要再配置@ResponseBody,默认返回json格式
  • @SpringBootApplication是组合注解,整合了@EnableAutoConfiguration和@ComponentScan

打包成jar包以及运行

需要在pom.xml文件中添加依赖(Spring Initializr会自动添加)

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

点击右侧的Maven->LifeCycle->compile(不要选package)

在这里插入图片描述

运行方式:

java -jar xxx.jar

一些常用的yml配置

server:
  port: 8080  # 修改端口,别忘了前面的空格
  servlet:
    context-path: /xxx  # 指定项目名,即访问的时候要在8080后面加上/xxx,注意以斜杠开头

staters介绍

staters可以方便地管理相关依赖,例如springboot-stater-web即可得到web应用启动的代码

全部staters请参看官网

https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter

在这里插入图片描述

Spring Boot开发规范

标准开发方式目录:

com
 +- example
     +- myapplication
         +- Application.java---------------->启动类
         |
         +- customer------------------------>管理客户
         |   +- Customer.java
         |   +- CustomerController.java
         |   +- CustomerService.java
         |   +- CustomerRepository.java
         |
         +- order---------------------------->管理订单
             +- Order.java
             +- OrderController.java
             +- OrderService.java
             +- OrderRepository.java
  • config编写配置类
  • controller是业务层
  • entity是实体类层
  • dao是编写数据库操作函数接口的
  • service是实现数据库操作功能的,整合dao层,文件格式是xxxService(interface)+xxxImpl(class)
  • xxxImplimpl的缩写,service层先有接口,再有impl实现接口,整合了DAO层
  • utils是编写工具类
  • mapper是写mapper的,实现dao的接口
  • 接口路径格式是/xxxx,即前面加上/,其实加不加都可以,最好还是加,规范嘛

配置类与xml配置

配置是进行工厂设计模式,即对对象进行加工后再返回,看到的名字一样,但是已经执行了一步程序了

官方推荐使用配置类(Java config):

  • 添加@Configuration注解,将配置类自动注入,类似于@Component
  • @Import不推荐使用,因为这个必须在要用的位置写,即哪用在哪写,但是@Configuration是写一次到处用

如果是xml格式:

  • 编写xml文件
  • @ImportResource添加在对应的类上

自定义项目启动的彩蛋

  • 打开网址http://patorjk.com/software/taag/将字符转为字符画
  • 拷贝生成的字符画到名为banner.txt文件中
  • 将banner.txt拷贝到项目的resources目录中再启动程序

${AnsiColor.BRIGHT_YELLOW}  
  
//                          _ooOoo_                               //  
//                         o8888888o                              //  
//                         88" . "88                              //  
//                         (| ^_^ |)                              //  
//                         O\  =  /O                              //  
//                      ____/`---'\____                           //  
//                    .'  \\|     |//  `.                         //  
//                   /  \\|||  :  |||//  \                        //  
//                  /  _||||| -:- |||||-  \                       //  
//                  |   | \\\  -  /// |   |                       //  
//                  | \_|  ''\---/''  |   |                       //  
//                  \  .-\__  `-`  ___/-. /                       //  
//                ___`. .'  /--.--\  `. . ___                     //  
//              ."" '<  `.___\_<|>_/___.'  >'"".                  //  
//            | | :  `- \`.;`\ _ /`;.`/ - ` : | |                 //  
//            \  \ `-.   \_ __\ /__ _/   .-` /  /                 //  
//      ========`-.____`-.___\_____/___.-`____.-'========         //  
//                           `=---='                              //  
//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        //  
//            佛祖保佑       永不宕机      永无BUG                  //
 
=====================================================================
spring boot:${spring-boot.version}

  • ${AnsiColor.BRIGHT_CYAN}来设定banner字体
  • ${AnsiBackground.BRIGHT_CYAN}来设定banner背景颜色
  • ${AnsiStyle.UNDERLINE}设定字体样式
  • ${spring-boot.version}查看当前版本

配置文件的拆分

情景是这样的,在不同环境下的配置不同,例如本地和上线的配置就不一样,改来改去的很麻烦,我们可以写几个配置文件将配置进行拆分

  • 写几个配置文件,格式为application-dev/prod.yml(测试环境/成产环境)
  • 在application.yml文件中添加一行spring.profiles.active=dev进行激活环境配置

注:如果不是application-dev.yml格式的话,在激活环境的时候需要写全名

创建自定义简单对象

在类上添加如下注解,再使用@Autowired自动注入对象

  • @Service注解标注xxximpl类(实现dao层)
  • @Controller
  • @Repository

注:@Bean注解创建的对象,@Autowired自动注入两个,用==返回true,应该在方法或类上加上@Scope("prototype")注解实现多例模式,默认是singleton单例模式

从配置文件获取变量值

配置文件添加了:

name=zrl
bir=2021/2/16

如果想在对象中取到这个值,需要使用

@Value("${name}")
private String name
@Value("${bir}")
private Date bir

yml数据结构格式:

  • 数组:直接用逗号分隔,例如aa,bb,cc,注入时标明数组格式

  • 列表:直接用逗号分隔,例如aa,bb,cc,注入时标明列表格式

  • Map:@Value取值格式是#{${mapname}}properties格式是mapname={key:value,key:value...},yml格式如下

    • mapname.aa=111
    • mapname.bb=222
    • mapname.cc=333
  • 对象:以user类,properties格式为例,user.name=xx,user.age=xx,然后在实体类添加两个注解

    • @Component
    • @ConfigurationProperties(prefix = "user")

Spring Boot的跨域请求

什么是跨域请求

浏览器(APP不会)出于安全的考虑,使用ⅩIMlhttpRequest对象发起HTTP请求时必须遵守同源策略,否则就是跨域的HTTP请求,这种请求默认情况下是被禁止的。同源策略要求源相同才能正常进行通信,即协议、域名、端口号都完全一致。

CORS是—个W3C标准,全称是“跨域资源共享”( Cross-origin resource sharing),允许浏览器向跨源服务器,发出 XmlHttpreQuesT请求,从而克服了Ajax只能同源使用的限制。

它通过服务器增加个特殊的 Header[Access-Control- Allow-Origin]来告诉客户端跨域的限制,如果浏览器支CORS、并且判断 Origin通过的话,就会允许 XmlHttpReques发起跨域请求。

解决跨域问题的两种方式

方法一:给类或方法添加注解

@CrossOrigin
public class Test {}

方法二:创建一个配置类

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class CorsConfig {
    @Bean
    public FilterRegistrationBean corsFilter() {
        // 注册CORS过滤器
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true); // 是否支持安全证书
        config.addAllowedOrigin("*"); // 允许任何域名使用
        config.addAllowedHeader("*"); // 允许任何头
        config.addAllowedMethod("*"); // 允许任何方法(post、get等)
        // 预检请求的有效期,单位为秒。
        //config.setMaxAge(3600L);
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
        bean.setOrder(0);
        return bean;
    }
}

解决跨域的原理

在请求头添加了:

Origin: http://ip:port

在响应头添加了:

Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://ip:port

Spring Boot集成thymeleaf模板引擎

简单使用

Spring Boot推荐使用thymeleaf模板,因为thymeleaf模板使用的是html,可以直接运行,同时因为不需要转换提高了效率。同时为了spring做了使用方法,很方便。注意控制器的@Controller注解一定要注意不要写成@RestController,除此之外,直接返回页面时不要加/,例如index不要写成/index,否则打包为jar包后会无法找到页面

依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

配置(这个不写也没关系,因为下面的都是默认配置):

spring.thymeleaf.prefix=classpath:/templates/  # 模板目录
spring.thymeleaf.suffix=.html  # 模板后缀
spring.thymeleaf.encoding=UTF-8  # 模板编码
spring.thymeleaf.enabled=true  # 开始thymeleaf模板
spring.thymeleaf.servlet.content-type=text/html  # 使用模板响应类型
springboot.resources.static-location=classpath:/templates/,classpath:/static/  # 修改静态文件目录,这个默认不包含templates

控制器:

@Controller//一定要注意不要写成@RestController
@RequestMapping("/user")
public class UserController {
	@GetMapping("/findAll")
    public String findAll(){
        return "index";//只写页面逻辑名
    }
}

后端传值:

方法一,返回ModelAndView

@Controller
@RequestMapping("/user")
public class TestController {

    @GetMapping("findAll")
    public ModelAndView findAll(){
        HashMap<String, Object> map = new HashMap<>();
        map.put("name","xiaoming");
        return new ModelAndView("index", map);
    }
}

方法二:model.addAttribute()或request.setAttribute()

@Controller
@RequestMapping("/user")
public class TestController {

    @GetMapping("findAll")
    public String findAll(Model model){
        model.addAttribute("name","xiaoming");
        return "index";
    }
}

thymeleaf语法

页面加入命名空间

<html lang="en" xmlns:th="http://www.thymeleaf.org">

表达式:

  • ${}用的最多也是最强大的表达式,获取变量值;底层是OGNL;
  • Selection Variable Expressions: *{…}:选择表达式:和${}在功能上是一样;
  • Message Expressions: #{…}:获取国际化内容
  • Link URL Expressions: @{…}:定义URL
@{/order/process(execId=${execId},execType='FAST')}
  • Fragment Expressions: ~{…}:片段引用表达式,引入另一个界面
<div th:insert="~{commons :: main}">...</div>

表达式支持的语法:

Literals(字面量)
    Text literals: 'one text' , 'Another one!' ,…
    Number literals: 0 , 34 , 3.0 , 12.3 ,…
    Boolean literals: true , false
    Null literal: null
    Literal tokens: one , sometext , main ,…
Text operations:(文本操作)
    String concatenation: +
    Literal substitutions: |The name is ${name}|
Arithmetic operations:(数学运算)
    Binary operators: + , - , * , / , %
    Minus sign (unary operator): -
Boolean operations:(布尔运算)
    Binary operators: and , or
    Boolean negation (unary operator): ! , not
Comparisons and equality:(比较运算)
    Comparators: > , < , >= , <= ( gt , lt , ge , le )
    Equality operators: == , != ( eq , ne )
Conditional operators:(条件运算)(三元运算符)
    If-then: (if) ? (then)
    If-then-else: (if) ? (then) : (else)
    Default: (value) ?: (defaultvalue)
Special tokens:
    No-Operation: _

综合案例:

<!DOCTYPE html>
<!--导入名称空间-->
<html  xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>成功</h1>
    <!--th:text 将div的文本内容设置为-->
    <div id="div01" class="myDiv" th:id="${hello}" th:class="${hello}" th:text="${hello}">这里显示的是欢迎信息</div>
    <br>
		<!--th:utext 不会转义特殊字符,会直接做出来一个h1标签,但是text不会,会显示<h1>你好</h1>-->
    <div th:text="${hello}">这里显示的是欢迎信息</div>
    <div th:utext="${hello}">这里显示的是欢迎信息</div>
    <br>

    <!--遍历-->
    <h4 th:text="${user}" th:each="user:${users}"></h4>
    <br>
    <!--行内写法-->
    <h4>
        <span th:each="user:${users}">[[${user}}]]</span>
    </h4>
</body>
</html>

thymeleaf常见问题

引入js等静态资源文件

<script src="./../static/layui/layui.all.js" th:src="@{/layui/layui.all.js}"></script>

th:src在springboot中默认就是static文件夹下,所以不用在./…/

js引用thymeleaf值

注:script标签中 th:inline 一定不能少,通常在取值的前后会加上不同的注释

<script th:inline="javascript">
    var message = [[${message}]];
    console.log(message);
</script>

给a标签的href传递参数

<a th:href="@{/delete(id=${emp.id})}"> delete</a>

Spring Boot从后端传值给前端

前端直接使用Thymeleaf语法即可

使用ModelAndView+Map

ModelAndView是用来返回页面的,防止添加了@RestController注解

@GetMapping("/seller/logout")
public ModelAndView logout(Map<String,Object> map){
    map.put("msg","登出!");
    map.put("url","/sell");
    return new ModelAndView("common/success",map);
}

使用HttpServletRequest

注意不要加@RestController注解

@GetMapping("/index")
public Object index(HttpServletRequest request) {
    //先获取principal,这个是通过MyRealm的认证方法rutuen的,进行了注入
    Object principal = SecurityUtils.getSubject().getPrincipal();
    AccountProfile user = (AccountProfile) principal;
    //添加session
    request.setAttribute("username",user.getUsername());
    return "index";
}

使用Model

@RequestMapping(value = "/")
public String index(Model model){ 
    String students ="刘洋";
    model.addAttribute("s",students)
    return "index";
}

Spring Boot接收从前端传来数据

前后端同名传参(GET)

直接把表单的参数写在Controller相应的方法的形参中,提交的参数需要和Controller方法中的入参名称一致。

url形式:http://localhost:8080/addUser1?username=lixiaoxi&password=111111

@RequestMapping("/addUser1")
public String addUser1(String username,String password) {
    System.out.println("username is:"+username);
    System.out.println("password is:"+password);
    return "demo/index";
}

@RequestParam(POST)

@RequestParam注解绑定请求参数到方法入参,当请求参数username不存在时会有异常发生,可以通过设置属性required=false解决,例如: @RequestParam(value="username", required=false)

这种POST只适合普通表单的POST,如果是axios则需要使用@RequestBody注解

@RequestMapping(value="/addUser6",method=RequestMethod.GET)
public String addUser6(@RequestParam("username") String username,@RequestParam("password") String password) {
    System.out.println("username is:"+username);
    System.out.println("password is:"+password);
    return "demo/index";
}

@RequestBody(axios的POST)

创建一个实体类,用于接收所有类型的POST请求,请求参数和UserModel属性相对应

@RequestMapping("/addUser3")
public String addUser3(@Validated @RequestBody UserModel user) {
    System.out.println("username is:"+user.getUsername());
    System.out.println("password is:"+user.getPassword());
    return "demo/index";
}

RequestBody可以使用Map作为兼容,同时axios不用进行设置请求头

@PathVariable获取路径中的参数

例如,访问http://localhost/addUser4/lixiaoxi/111111 路径时,则自动将URL中模板变量{username}和{password}绑定到通过@PathVariable注解的同名参数上,即入参后username=lixiaoxi、password=111111。

@RequestMapping(value="/addUser4/{username}/{password}",method=RequestMethod.GET)
public String addUser4(@PathVariable String username,@PathVariable String password) {
    System.out.println("username is:"+username);
    System.out.println("password is:"+password);
    return "demo/index";
}

HttpServletRequest接收(GET、POST)

通过HttpServletRequest接收,post方式和get方式都可以

@RequestMapping("/addUser2")
public String addUser2(HttpServletRequest request) {
    String username=request.getParameter("username");
    String password=request.getParameter("password");
    System.out.println("username is:"+username);
    System.out.println("password is:"+password);
    return "demo/index";
}

axios 默认是 Payload 格式进行数据请求,需要为 axios 的 post 请求设置请求头

headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
}

@ModelAttribute(POST)

使用@ModelAttribute注解获取POST请求的FORM表单数据

<form action ="<%=request.getContextPath()%>/demo/addUser5" method="post"> 
     用户名: <input type="text" name="username"/><br/>
     密  码: <input type="password" name="password"/><br/>
     <input type="submit" value="提交"/> 
     <input type="reset" value="重置"/> 
</form>
@RequestMapping(value="/addUser5",method=RequestMethod.POST)
public String addUser5(@ModelAttribute("user") UserModel user) {
    System.out.println("username is:"+user.getUsername());
    System.out.println("password is:"+user.getPassword());
    return "demo/index";
}

Spring Boot整合Mybatis

添加依赖:

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.3</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<!--阿里巴巴-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.12</version>
</dependency>

添加配置:

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/sell

mybatis:
  mapper-locations: classpath:/com/baizhi/mapper/*.xml  # mapper位置
  type-aliases-package: com.baizhi.entity  # 实体类位置
  configuration:
    map-underscore-to-camel-case: true  # 开启自动驼峰命名

入口类添加注解:

# 注意
	必须在xxxApplication.java入口类上加上@mapperScan("com.xxx.xxx.dao")注解,扫描DAO接口所在包,或者是在dao层的每个接口都加上@Mapper注解

使用注解方式操作数据库,直接在dao层加上注解:

@Select("select xxxx where id=#{id}")
public Blog selectBlog(int id)
@Delete("delete xxxx where id=#{blog.id}")
public int selectBlog(Blog blog)

使用xml配置文件方式操作数据库,即写mapper:

<?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="org.mybatis.example.BlogMapper">
  <select id="selectBlog" resultType="Blog">
    select * from Blog where id = #{id}
  </select>
</mapper>

在这里插入图片描述

Spring Boot的devtools热部署

热部署就是在不重启Spring Boot项目的情况下,让我们的更改生效。这样在启动项目的时候会创建两个类加载器,dev-tools是通过两个类加载器实现热部署的,更改后会由空闲的进行编译,然后会进行类加载器的切换,空闲的替换正在工作的。甚至不用点小锤子

依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
</dependency>

在idea中设置支持自动编译

# 开启自动编译
	Preferences-->Build,Execution,Development-->Compiler-->勾选Build project automatically
# 开启允许在运行过程中修改文件
	ctrl+alt+shift+/---->选择Registry---->勾选compiler.automake.allow.when.app.running

关闭thymeleaf缓存

springboot.thymeleaf.cache=false

Spring Boot日志的使用

logback、log4j啥的使用起来挺麻烦的,直接使用插件吧,需要安装lombok插件并引入lombok依赖

日志级别:debug < info < warn < error < off

控制台输出默认是info级别,可以在配置文件修改当前包的日志级别

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;

@Slf4j
class TestControllerTest {
    @Test
    void test1() {
        log.debug("嘿嘿嘿");
        log.info("哈喽,你好啊");
        log.error("出错啦,问题是{}","没事");
    }
}

或者是不使用插件:

import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class TestControllerTest {
    @Test
    void test1() {
        Logger logger = LoggerFactory.getLogger(User.class);
        logger.debug("xxx");
    }
}

Spring Boot实现文件上传

上传界面:

<form method="post" action="路径" enctype="multipart/form-data">
    <input type="file" name="file"><br>
    <input type="submit" value="提交">
</form>
<!--
提交方式必须为post
enctype属性必须为multipart/form-data
后台方法接收MultipartFile变量名字要与文件选择的name属性一致,可以都写为file
-->

控制器:

@PostMapping("/upload")
public String upload(MultipartFile file, HttpServletRequest request) throws IOException {
    //获取绝对路径
    //String realPath = request.getServletContext().getRealPath("/files");这个是服务器的临时目录,不推荐
    String realPath = ResourceUtils.getURL("classpath:").getPath()+"/static/files";
    System.out.println(realPath);
    File dir = new File(realPath);
    if(!dir.exists()) {//如果路径不存在就创建文件夹
        dir.mkdir();
    }
    //文件相关信息
    System.out.println(file.getName());//文件名
    System.out.println(file.getSize());//文件大小
    System.out.println(file.getContentType());//文件类型
    file.transferTo(new File(dir,file.getOriginalFilename()));//文件上传
    return "redirext:index";//不重定向的话表单会重复提交
}

修改配置文件的允许上传最大文件大小,不写默认为10MB:

# 不写单位的话默认是以字节为单位
# 服务器最大文件大小
spring.servlet.multipart.max-file-size=500MB
# 文件上传最大大小
spring.servlet.multipart.max-request-size=500MB

文件上传控制器优化:

@PostMapping("/upload")
public String upload(MultipartFile file, HttpServletRequest request) throws IOException {
    //获取绝对路径
    String realPath = ResourceUtils.getURL("classpath:").getPath()+"/static/files";
    //按日期放置文件
    String dateDir = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
    File dir = new File(realPath,dateDir);
    if(!dir.exists()) {//如果路径不存在就创建文件夹
        dir.mkdir();
    }
    //修改文件名
    //前缀
    String newFileNamePrefix = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date())+ UUID.randomUUID().toString();
    //后缀
    /*
    	需要引入依赖
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.4</version>
    */
    String extension = FilenameUtils.getExtension(file.getOriginalFilename());
    String newFileName = newFileNamePrefix+"."+extension;
    file.transferTo(new File(dir,newFileName));//文件上传
    return "redirect:index";
}

Spring Boot实现文件下载

下载界面:

<a href="../file/download?fileName=text.txt">text.txt</a>

控制器1——不直接下载文件而是点一下展示文件:

@GetMapping("download")
public void download(String fileName, HttpServletResponse response) throws Exception {
    String realPath = ResourceUtils.getURL("classpath:").getPath()+"/static/files";
    //获取文件输入流
    FileInputStream is = new FileInputStream((new File(realPath, fileName)));
    //相应输出流
    ServletOutputStream os = response.getOutputStream();
    //文件拷贝
    int len = 0;
    byte[] b = new byte[1024];
    while(true) {
        len = is.read(b);
        if(len==-1) {
            break;
        }
        os.write(b,0,len);
    }
    is.colse();
    os.close();
}

控制器2——直接下载文件:

@GetMapping("download")
public void download(String fileName, HttpServletResponse response) throws Exception {
    String realPath = ResourceUtils.getURL("classpath:").getPath()+"/static/files";
    //获取文件输入流
    FileInputStream is = new FileInputStream((new File(realPath, fileName)));
    //相应输出流
    ServletOutputStream os = response.getOutputStream();
    //attachment附件下载,inline是在线打开(图片,pdf等)
    response.setHeader("content-disposition","attachment;fileName="+ URLEncoder.encode(fileName,"UTF-8"));
    //拷贝文件
    /*
    	需要引入依赖
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.4</version>
    */
    IOUtils.copy(is,os);
    IOUtils.closeQuietly(is);
    IOUtils.closeQuietly(os);
}

Spring Boot中的拦截器开发

如果在每个控制器(controller)方法中都使用session.grtAttribute("user")会显得十分冗余,我们可以使用拦截器,请求先经过拦截器,通过了然后再进入控制器。

作用:通过拦截器执行通用代码逻辑,以减少控制器中代码冗余

特点:

  • 只能拦截控制器相关请求,不能拦截静态资源和页面相关情况(css,js…)
  • 请求发送经过拦截器,响应回来同样经过拦截器
  • 拦截器可以中断用户请求
  • 拦截器可以针对性拦截某些控制器请求

拦截器开发流程:

# 1、类(xxxInterceptor) implements HandlerInterceptor
	preHandler----预先处理,返回值true放行到controller中,false中断请求同时在原页面
	postHandler----请求过程中处理,即controller执行之后的操作
	afterCompletion----响应之后执行
# 2、springboot中配置拦截器
	注册到springboot拦截数组中,方法是写一个配置类

拦截器:

public class MyInterceptor implements HandlerInterceptor {
	//实现你想要的接口即可,可以只实现一个,一般放在interceptor包下
}

配置类——1.x版本:

import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class InterceptorConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor())//添加拦截器
                .addPathPatterns("/**")//定义拦截请求
                .excludePathPatterns("/hello/**");//排除拦截请求
    }
}

配置类——2.x版本:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
	//即继承变成了实现接口,JDK8有了接口的默认实现,因此只实现一个方法也是可以的
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor())//添加拦截器
                .addPathPatterns("/**")//定义拦截路径
                .excludePathPatterns("/hello/**");//排除拦截路径
    }
}

拦截器执行顺序:

配置类中先添加的inter1,再添加的inter2

在这里插入图片描述

Spring Boot面向切面的编程

springboot支持spring中的aop编程,切面就是将现有功能进行抽取,将这个功能应用在各种各样的业务处理上,达到代码复用,例如,可以把蓝色圈看成业务层,里面套了性能处理,外面套了整体业务执行的处理

在这里插入图片描述

依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

三种切面:

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;

@Aspect
@Configuration
@Order(1)//多个环绕通知可以加这个注解,数字越小越先执行,这个注解现在只能加在类上,加在方法上已经不生效了
public class MyAspect {
    //环绕通知,用的比较多,目标方法执行时会先进入环绕通知,放行后执行目标方法,目标方法执行完后回到环绕通知
    @Around("within(com.example.service.*ServiceImpl)")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("进入环绕通知业务处理");
        Object proceed = proceedingJoinPoint.proceed();//放行,去执行业务方法
        System.out.println("方法执行后的业务处理逻辑如下:");
        //todo
        return proceed;
    }

    //前置通知,在目标方法执行前执行
    @Before("within(com.example.service.*ServiceImpl)")
    public void before(JoinPoint joinPoint) {
        System.out.println("目标方法名称:"+joinPoint.getSignature().getName());
        System.out.println("目标参数:"+joinPoint.getArgs());
        System.out.println("目标对象:"+joinPoint.getTarget());
        System.out.println("前置业务通知");
    }

    //后置通知,在目标方法执行后执行
    @After("within(com.example.service.*ServiceImpl)")
    public void after(JoinPoint joinPoint) {
        System.out.println("目标方法名称:"+joinPoint.getSignature().getName());
        System.out.println("目标参数:"+joinPoint.getArgs());
        System.out.println("目标对象:"+joinPoint.getTarget());
        System.out.println("后置业务通知");
    }
}

Spring Boot手写starter

命名规范:第三方的starter应该是类似projectName-spring-boot-starter的格式

starter中真正工作的是starter的自动配置类

引入依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

写属性类:

@Component
@Getter
@Setter
@ConfigurationProperties(prefix = "tx.redis")
public class RedisProperties {
    private int database = 0;
    private String url;
    private String host = "localhost";
    private String password;
    private int port;
}

写自动配置类:

@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfiguration {
    @Autowired
    RedisProperties properties;

    @Bean
    public RedisTemplate<Object, Object> redisTemplate() {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        JedisConnectionFactory factory = new JedisConnectionFactory(new RedisStandaloneConfiguration(properties.getHost()));
        redisTemplate.setConnectionFactory(factory);
        return redisTemplate;
    }
}

resources文件夹中创建名为META-INF的文件夹,创建spring.factories文件,第二行写自动配置类的地址

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.demo.auto.RedisAutoConfiguration

测试自写的starter:

可以在yml文件中,通过tx.redis配置到配置信息,酷酷的。然后可以直接使用RedisTemplate这个类,因为在spring.factories文件中做了键值对配置,会被自动扫描。

在使用的时候,有时候会遇到加上@Qualifier("testClass1")这个注解的时候,作用是对自动配置类中,不同方法产生了相同对象进行类区,详情可以参看https://blog.csdn.net/qq_30062181/article/details/107619529

Spring Boot开发通用手段

# 删除数据
	发起请求,在请求中进行重定向界面
# 修改数据
	和创建用户一个界面,只不过需要判断是否有id,有的话进行填充数据,提交就是保存
	或者时重新写一个页面
# 页面重定向
	在return页面的时候,写成`return "redirect:index"`,即重定向到index界面,或者请求,注意不要写RestController
# 支持事务
	@Transactional(propagation = Propagation.SUPPORTS)
# 使用Map作为返回值
	如果业务简单可以使用Map作为返回值,避免了实体类的复杂处理

Spring Boot开发全局异常处理

@ControllerAdvice+@ExceptionHandler的形式:

创建一个类,作为Controller的全局异常,类通过@ControllerAdvice修饰,方法通过@ExceptionHandler修饰

//vaule可以是多个异常,用数组分开
@ExceptionHandler(value={xxxx.class})
public ModelAndView xxxxHandler(Exception e) {
	return new ModelAndView("error.html",map);
}

注:只是在Controller中添加@ExceptionHandler方法不能达到全局的效果,需要使用@ControllerAdvice

使用SimpleMappingExceptionResolver的形式:创建配置类但是无法传递给界面异常信息,因此也不常用了

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

笼中小夜莺

嘿嘿嘿,请用金钱尽情地蹂躏我吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值