什么是SpringBoot?
- Spring Boot 是由 Pivotal 团队提供的全新框架
- 目的是用来简化新 Spring 应用的初始搭建以及开发过程
- 该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置
- 通过这种方式,Spring Boot 致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。
- Spring Boot 并不是对 Spring 功能上的增强,而是提供了一种快速使用 Spring 的方式。
特性
- 约定大于配置
- 创建独立的spring应用程序。
- 嵌入的tomcat jetty 或者undertow 不用部署WAR文件。
- 允许通过Maven来根据需要获取starter
- 尽可能的使用自动配置spring
- 提供生产就绪功能,如指标,健康检查和外部配置properties yaml yml
- 开箱即用,没有代码生成,也无需 XML 配置,同时也可以修改默认值来满足特定的需求。
传统开发模式
-
优点
- 开发简单,集中式管理
- 基本不会重复开发
- 功能都在本地,没有分布式的管理和调用消耗
-
缺点
- 效率低:开发都在同一个项目改代码,相互等待,冲突不断
- 维护难:代码功功能耦合在一起,新人不知道何从下手
- 不灵活:构建时间长,任何小修改都要重构整个项目,耗时
- 稳定性差:一个微小的问题,都可能导致整个应用挂掉
- 扩展性不够:无法满足高并发下的业务需求
- 对服务器的性能要求要统一,要高
微服务开发模式
- 优点
- 每个微服务都很小,这样能聚焦一个指定的业务功能或业务需求
- 微服务能够被小团队开发,这个小团队2-5人就可以完成了
- 微服务是松耦合的,是有功能,有意义的服务,开发阶段或部署阶段都是独立的
- 微服务可以使用不同的语言开发
- 微服务能部署在中低端配置的服务器上
- 很容易和第三方集成
- 每个服务都有自己的存储能力,单独的库,也可以有统一的库
- 缺点:
- 微服务会带来过多的操作
- 可能有双倍的努力
- 分布式系统可能复杂难管理
- 分布跟踪部署难
- 当服务数量增加时,管理复杂度增加
SpringBoot的配置
配置文件
-
properties文件
#第一种配置文件 #mysql jdbc.mysql.driverClassName=com.mysql.cj.jdbc.Driver jdbc.mysql.url=jdbc:mysql://localhost:3306/crm?serverTimezone=UTC jdbc.mysql.username=root jdbc.mysql.password=root #druid druid.initialSize=10 druid.minIdle=10 druid.maxActive=50 druid.maxWait=60000
yml文件
#第二种配置文件 #myKey: myValue #yaml 层级式键值对配置 #层级:通过换行符+空格,同一层级必须对其,父层与子层之间通常使用2个空格 #level1: # level21: aaa # level2: # level31: bb # level32: cc #数据类型 #简单值 #字符串:可以不使用引号包裹,但是有特殊符号时需要使用引号包裹(例如:空格) name: 张三 age: 10 birthday: 1998/07/25 gender: true obj: ~ #数组(也认为是LIst集合) #行内 array1: [spring, springmvc, mybatis] array2: - html - css - javascript #对象(也可认为是Map集合) student: name: 李四 age: 22 birth: 1999/10/17 gender: false hobby: [football, games, swimming] address: city: 郑州市 area: 金水区 street: 民航路 #主配置 #激活环境配置profiles #spring: # profiles: # active: dev #server: # port: 8080
读取配置文件
-
本质是将配置类对象放到ioc容器中
-
单个读取
@Value("${name}") private String username;
-
使用配置类读取
-
第一种方式
@Component + @ConfigurationProperties
-
第二种方式
@EnableConfigurationProperties + @ConfigurationProperties
-
-
读取第三方的properties文件
@Component @ConfigurationProperties(prefix = "jdbc.mysql") //加载第三方资源文件 @PropertySource(value = "classpath:db.properties")
-
读取原生的xml文件
-
demo
/** * 读取配置文件数据 */ @RestController public class ReadConfigController { /** * 单个读取,不方便 * 使用@Value注解+SpringEL来实现配置读取 */ @Value("${name}") private String username; @Value("${age}") private Integer age; @Value("${birthday}") private Date birthday; @Value("${gender}") private Boolean gender; @Value("${student.name}") private String studentName; @GetMapping("/read") public String read(){ return "姓名:" + username + " 年龄:" + age + " 生日:" + birthday + " 性别:" + gender + " 学生姓名:" + studentName; } /** * 使用配置类 * 将配置类放到IOC容器,在这里自动注入 */ @Autowired private StudentProperties studentProperties; @GetMapping("/readProperties") public StudentProperties readProperties(){ return studentProperties; } /** * 读取第三方的properties文件 */ @Autowired private MysqlProperties mysqlProperties; @GetMapping("/readProp") public MysqlProperties readProp(){ return mysqlProperties; } /** * 读取原生的xml配置文件 */ @Autowired private Emp emp; @GetMapping("/readXml") public Emp readXml(){ return emp; } }
在配置类上加注解
//加载原生的xml spring配置文件 @ImportResource(locations = "classpath:applicationContext.xml") //开启配置文件读取,属性为要读取的配置类,可以批量读取配置类 @EnableConfigurationProperties({StudentProperties.class, AddressProperties.class}) @SpringBootApplication public class Springboot02ConfigApplication { public static void main(String[] args) { SpringApplication.run(Springboot02ConfigApplication.class, args); } }
读取yaml文件的配置类
/** * 学生配置读取类,自动读取yml配置文件 * * 成员变量名称必须与配置名称保持一致(驼峰与中划线) * 1.@Component + @ConfigurationProperties * 2.@EnableConfigurationProperties + @ConfigurationProperties * * 第二种方式的@EnableConfigurationProperties注解 * 需要放到配置类上 * 作用是:开启配置文件读取 */ @Data //@Component //配置bean @ConfigurationProperties(prefix = "student") //读取配置文件,属性用来指定层级 public class StudentProperties { private String name; private Integer age; private Date birth; private Boolean gender; private String[] hobby; private AddressProperties address; //get... set... }
// @Component @Data @ConfigurationProperties(prefix = "student.address") public class AddressProperties { private String city; private String area; private String street; }
读取properties文件的配置类
/** * 读取#第三方的priperties文件 */ @Data @Component @ConfigurationProperties(prefix = "jdbc.mysql") //加载第三方资源文件 @PropertySource(value = "classpath:db.properties") public class MysqlProperties { private String driverClassName; private String url; private String username; private String password; }
读取原生的applicationContext.xml文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--使用原生的xml配置文件,此文件默认不读,需要在配置--> <bean id="dept" class="com.dx.bean.Dept"> <property name="id" value="10"/> <property name="dname" value="研发部"/> </bean> <bean id="emp" class="com.dx.bean.Emp"> <property name="id" value="1001"/> <property name="ename" value="汤姆"/> <property name="dept" ref="dept"/> </bean> </beans>
@Data public class Emp { private Integer id; private String ename; private Dept dept; }
@Data //开启链式调用 @Accessors(chain = true) public class Dept { private Integer id; private String dname; }
profile配置文件
-
三种工作环境配置文件:开发环境(dev)、生产环境(prod)、测试环境(test)
#环境配置 #需要激活:在application.yml中激活 #命名规则:application-xxx.xml #开发环境配置 server: #端口号 port: 8001 servlet: #根路径 context-path: /web-dev
#生产环境配置 server: port: 8003 servlet: context-path: /web-prod
#测试环境 server: port: 8002 servlet: context-path: /web-test
在测试的时候,需要修改pom的编译路径,确保把所有的配置文件都编译以后再测试
<build> <!-- 将所有的配置文件都编译--> <resources> <resource> <directory>D:\workspace\SpringBoot-Code\02-spring-boot-config</directory> <includes> <include>**/*.yml</include> <include>application.yml</include> </includes> </resource> </resources> </build>
部署时配置文件
-
外部配置文件
在D盘放一个application.yml文件 端口指定为8009 打包后使用命令行运行并且指定 java -jar aaa.jar --spring.config.location=D:/application.yml
-
命令修改配置文件
可以使用命令行参数指定(文档一行写不下的时候,不要回车) java -jar aaa.jar --server.port=8888 --server.servlet.context-path=/bjpowernode
配置优先级
-
后加载的会覆盖先加载的
-
最后加载的优先级最高
-
spring boot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件
其中同一目标下的properties文件的优先级大于yml文件
-
配置文件可以放的位置和优先级
classpath:/ --优先级4classpath:/config/ --优先级3
file:./ --优先级2
file:./config/ --优先级1
日志logback
配置日志级别
-
@RestController //注解式日志:动态为该类声明一个静态Logger成员变量 //@Slf4j public class LogController { /** * 使用日志: * 在当前类中声明一个成员变量,类型为Logger类型 */ private Logger log = LoggerFactory.getLogger(LogController.class); @GetMapping("/getLog") public String getLog(){ /** * 在各个日志框架都有级别 * 错误error,警告warn,信息info,调试debug,底层trace * * springboot默认级别为info * 可以在application.yml配置文件中配置 * * 例如:当级别为info,日志输入仅会输入info以及以上级别日志 */ //程序出现异常 log.error("error级别的日志"); //程序有可能出现问题,提醒或警告 log.warn("warn级别的日志"); //信息:记录用户操作 log.info("info级别的日志"); //调试信息:开发时使用 log.debug("debug级别的日志"); //程序底层信息 log.trace("trace级别的日志"); //热部署添加内容 log.info("无需重启自动生效..."); return "正在记录日志"; } }
-
全局配置 局部配置
#配置日志 logging: level: #使用root配置全局的日志级别 root: info #局部日志级别,两种写法 # com.dx.controller: trace com: dx: controller: trace
自定义日志输出格式
#自定义日志格式
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss SSS} %-5level [%thread] %logger -- %msg%n"
生成日志文件
#日志在文件中的输出
file:
#日志文件名,在根目录下可以找到文件
name: demo.log
#指定日志文件路径,但是不能与那么同时配置,两个只能用一个
# path: c:/log/
#如果想既自定义文件名 又指定文件位置,可以导入logback.xml文件,如果使用logback.xml记得把这里的配置去掉,不然会冲突
logback.xml:可以直接使用,直接放到项目中就可以
<?xml version="1.0" encoding="UTF-8"?>
<!--
scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
-->
<configuration scan="false" scanPeriod="60 seconds" debug="false">
<!-- 定义日志的根目录 -->
<property name="LOG_HOME" value="d:/logback/" />
<!-- 定义日志文件名称 -->
<property name="appName" value="springboot"></property>
<!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 -->
<appender name="cc" class="ch.qos.logback.core.ConsoleAppender">
<!--
日志输出格式:
%d表示日期时间,
%thread表示线程名,
%-5level:级别从左显示5个字符宽度
%logger{50} 表示logger名字最长50个字符,否则按照句点分割。
%msg:日志消息,
%n是换行符
-->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</layout>
</appender>
<!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->
<appender name="ff" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 指定日志文件的名称
d:/logback/springboot.log
-->
<file>${LOG_HOME}/${appName}.log</file>
<!--
当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名
TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。
-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--
滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}:按天进行日志滚动
%i:当文件大小超过maxFileSize时,按照i进行文件滚动
-->
<fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
<!--
可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每天滚动,
且maxHistory是365,则只保存最近365天的文件,删除之前的旧文件。注意,删除旧文件是,
那些为了归档而创建的目录也会被删除。
-->
<MaxHistory>365</MaxHistory>
<!--
当日志文件超过maxFileSize指定的大小是,根据上面提到的%i进行日志文件滚动 注意此处配置SizeBasedTriggeringPolicy是无法实现按文件大小进行滚动的,必须配置timeBasedFileNamingAndTriggeringPolicy
-->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<!-- 日志输出格式: -->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern>
</layout>
</appender>
<!--
logger主要用于存放日志对象,也可以定义日志类型、级别
name:表示匹配的logger类型前缀,也就是包的前半部分
level:要记录的日志级别,包括 TRACE < DEBUG < INFO < WARN < ERROR
additivity:作用在于children-logger是否使用 rootLogger配置的appender进行输出,
false:表示只用当前logger的appender-ref,true:
表示当前logger的appender-ref和rootLogger的appender-ref都有效
-->
<!-- 自定义的 logger -->
<logger name="com.bjpowernode.controller" level="debug" />
<!-- Spring framework logger -->
<logger name="org.springframework" level="debug" additivity="false"></logger>
<!--
root与logger是父子关系,没有特别定义则默认为root,任何一个类只会和一个logger对应,
要么是定义的logger,要么是root,判断的关键在于找到这个logger,然后判断这个logger的appender和level。
-->
<root level="info">
<!-- 控制台-->
<appender-ref ref="cc" />
<!-- 滚动文件-->
<appender-ref ref="ff" />
</root>
</configuration>
工具
自定义配置提示
<!--实现自定义配置的提示功能-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
热部署
<!--热部署:更新代码或页面不需要重启服务,直接Build一下就可以-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
lombok
<!--简化工具
作用:实现注解式get/set,构造方法,toString方法...注解式日志
条件:1.依赖,2.IDEA插件
-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
//开启对象中set方法的链式调用
@Accessors(chain = true)
//链式调用
Student s3 = new Student()
.setId(30)
.setName("王五")
.setAddress("深圳")
.setScore(82.6);
System.out.println(s3);
注解式日志
//注解式日志:动态为该类声明一个静态Logger成员变量
@Slf4j
public class LogController {
//可以代替这条语句
// private Logger log = LoggerFactory.getLogger(LogController.class);
@GetMapping("/getLog")
public String getLog(){
log.error("error级别的日志");
log.warn("warn级别的日志");
log.info("info级别的日志");
log.debug("debug级别的日志");
log.trace("trace级别的日志");
log.info("无需重启自动生效...");
return "正在记录日志";
}
}
处理静态资源
配置静态资源路径
-
默认位置
# 默认静态资源路径: # "classpath:/META-INF/resources/" # "classpath:/resources/" # "classpath:/static/" # "classpath:/public/"
-
配置自定义位置
-
配置静态资源访问路径
spring: web: resources: #设置当前项目静态资源目录 #注意:配置自定义静态资源目录时,需要添加原有配置项,否则会覆盖原有配置 #classpath表示在类路径下查找资源,file表示在操作系统的文件系统下查找资源 d:/ static-locations: classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,classpath:/my/,file:${custom.upload} mvc: #设置当前项目静态资源的访问前缀 static-path-pattern: /static/** #配置自定义本地的资源路径 custom: upload: E:\文件\图片\壁纸
使用静态资源jar包
-
导入bootstrap和jqury依赖
<!--jquery--> <dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.6.0</version> </dependency> <!--bootstrap--> <dependency> <groupId>org.webjars</groupId> <artifactId>bootstrap</artifactId> <version>3.4.1</version> </dependency>
-
使用demo
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/webjars/bootstrap/3.4.1/css/bootstrap.min.css"> </head> <body> <div style="text-align: center"> <h1>首页</h1> <div> <span id="hello">你好</span> </div> <div> <button type="button" class="btn btn-primary">按钮</button> </div> <div> <img src="/static/bbb.jpg" alt="" style="width: 300px; height: 200px"> </div> <!--webjars:以jar包的方式引入静态资源,可以从maven配置依赖--> <script type="text/javascript" src="/webjars/jquery/3.6.0/jquery.min.js"></script> <script type="text/javascript"> $(function (){ var str = $('#hello').html(); alert(str); $('#hello').append('springboot'); }) </script> </div> </body> </html>
使用模板引擎 Thymeleaf
配置Thymeleaf
-
开发时关闭缓存、发布时打开缓存
spring: #Thymeleaf的配置 thymeleaf: #页面缓存,开发时关闭缓存、发布时打开缓存 cache: false #前后缀,使用默认值就可以 # prefix: classpath:/templates/ # suffix: .html
动态属性获取数据
-
取值
<!-- 要在头上添加这个,不然${msg}会报错 xmlns:th="http://www.w3.org/1999/xhtml"--> <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml"> ...... <!-- 通过动态属性来获取数据 th:text 从域对象中获取数据,渲染到指定标签中,对特殊符号进行转码 会将传过来的标签直接当字符串输出 th:utext 从域对象中获取数据,渲染到指定标签中,不处理特殊符号 传过来的标签会生效 [[${msg}]] 加两个中括号,表示里面的内容是动态的 --> <p th:text="${msg}"></p> <p th:text="${message}"></p> <p th:utext="${message}"></p> <p>[[${msg}]]</p> <!--表单取各种值--> <form action="/emp/edit" th:action="@{/emp/edit}" method="post"> <div class="form-group"> <label>员工编号</label> <!--取值方式一:${emp.id}--> <input type="text" name="id" class="form-control" th:value="${emp.id}"> </div> <div class="form-group"> <label>员工姓名</label> <!--取值方式二:*{ename}--> <input type="text" name="ename" class="form-control" th:value="*{ename}"> </div> <div class="form-group"> <label>入职日期</label> <!--取日期--> <input type="date" name="hiredate" class="form-control" th:value="${#dates.format(emp.hiredate, 'yyyy-MM-dd')}"> </div> <div class="form-group"> <label>员工性别</label> <!--select取值--> <select name="gender" class="form-control"> <option value=""></option> <option value="1" th:selected="${emp.gender=='1'}">男性</option> <option value="2" th:selected="${emp.gender=='2'}">女性</option> </select> </div> <div class="form-group"> <button type="button" class="btn btn-primary">提交</button> </div> </form>
-
循环
<tr th:each="emp : ${empList}"> <td>[[${emp.id}]]</td> <td th:text="${emp.ename}"></td> </tr>
-
判断
<td> <span th:if="${emp.gender=='1'}">男性</span> <span th:if="${emp.gender=='2'}">女性</span> </td> <td th:switch="${emp.gender}"> <span th:case="'1'">帅哥</span> <span th:case="'2'">靓女</span> </td>
-
日期格式化
//日期格式化 //在成员变量上添加注解 @DateTimeFormat:接收参数时格式化 @JsonFormat:发送参数时格式化 @DateTimeFormat(pattern = "yyyy-MM-dd") @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") private Date hiredate;
<!--动态页面中的日期格式化--> <td th:text="${#dates.format(emp.hiredate, 'yyyy-MM-dd')}"></td>
-
自动根路径
@{...}
#在application.yml文件中 #修改项目的根路径 server: servlet: context-path: /boot
<!-- 静态路径:href="/emp/list" 动态路径:th:href="@{/emp/list}" 可以设置一个静态的和一个动态的,静态的留给前端用 --> <p><a href="/emp/list" th:href="@{/emp/list}">员工列表</a></p>
-
路径传参
<td> <!--路径传参--> <a href="/emp/get/1" th:href="@{'/emp/get/' + ${emp.id}}" class="btn btn-warning btn-xs">编辑</a>  <!--路径拼接传参,传的是名值对--> <a href="#" th:href="@{/emp/remove(id=${emp.id},ename=${emp.ename},job=${emp.job})}" class="btn btn-danger btn-xs">删除</a> </td> <!--表单传参--> <form th:action="@{/auth/login}" method="post" style="width: 350px;margin: 10px auto;"> <div style="color: red;">[[${message}]]</div> <div class="form-group"> <label>用户名</label> <input type="text" name="username" class="form-control"> </div> <div class="form-group"> <label>密码</label> <input type="password" name="password" class="form-control"> </div> <div class="form-group"> <button type="submit" class="btn btn-primary">登录</button> </div> </form>
SpringMVC配置
/**
* 自定义WebMvc配置类
* 实现一个接口WebMvcConfigurer的配置类,相当于原生springmvc.xml配置文件
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
/**
* 动态页面访问映射
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/page/login").setViewName("login");
registry.addViewController("/").setViewName("login");
registry.addViewController("/page/index").setViewName("index");
}
/**
* 注册拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry
//配置拦截器对象
.addInterceptor(new LoginInterceptor())
//配置拦截器拦截路径
.addPathPatterns("/**")
//配置拦截器放行路径(白名单)
.excludePathPatterns("/static/**", "/webjars/**", "/auth/login", "/page/login", "/");
}
/**
* 对静态资源处理
*/
// @Override
// public void addResourceHandlers(ResourceHandlerRegistry registry) {
// registry
// .addResourceHandler("/static2/**")
// .addResourceLocations("classpath:/my2/");
// }
/**
* 添加自定义的数据格式转换器
*/
@Override
public void addFormatters(FormatterRegistry registry) {
// registry.addFormatter();
registry.addConverter(new PointConverter());
}
/**
* 前后端分离的项目:设置全局跨域处理
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
}
}
拦截器
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object loginUser = request.getSession().getAttribute("loginUser");
if(loginUser == null){
request.setAttribute("message", "没有登录");
request.getRequestDispatcher("/page/login").forward(request, response);
return false;
}
return true;
}
}
类型转换器
/**
* 自定义的类型转换器:
* 作用:将String转换为自定义Point
*/
public class PointConverter implements Converter<String, Point> {
@Override
public Point convert(String source) {
String[] ss = source.split(",");
Point point = new Point(ss[0], ss[1]);
return point;
}
}
<p><a th:href="@{/getPoint(point='15,58')}">类型转换器案例</a></p>
web中的三大组件
-
servlet、filter、listener
-
默认不支持,需要在配置类中配置bean
-
配置三大组件
@Configuration public class ServletConfig { /** * 注册servlet */ @Bean public ServletRegistrationBean servletRegistrationBean(){ ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(); //设置Servlet对象 servletRegistrationBean.setServlet(new DemoServlet()); //设置路径映射 servletRegistrationBean.addUrlMappings("/demo"); //设置初始化参数 //单个 servletRegistrationBean.addInitParameter("msg", "hello"); //批量 // servletRegistrationBean.setInitParameters(); return servletRegistrationBean; } /** * 注册Filter */ @Bean public FilterRegistrationBean filterRegistrationBean(){ FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(new DemoFilter()); filterRegistrationBean.addUrlPatterns("/*"); // filterRegistrationBean.addInitParameter(); return filterRegistrationBean; } /** * 注册Listener */ @Bean public ServletListenerRegistrationBean servletListenerRegistrationBean(){ ServletListenerRegistrationBean servletListenerRegistrationBean = new ServletListenerRegistrationBean(); servletListenerRegistrationBean.setListener(new DemoListener()); return servletListenerRegistrationBean; } }
DemoFilter.java
@WebFilter("/*") public class DemoFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("DemoFilter执行了........."); //过滤器放行 filterChain.doFilter(servletRequest, servletResponse); } }
DemoListener.java
@WebListener public class DemoListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { System.out.println("DemoListener执行了....."); } }
DemoServlet.java
@WebServlet("/demo") public class DemoServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("DemoServlet.........." + msg); resp.getWriter().write("ok"); } private String msg; @Override public void init(ServletConfig config) throws ServletException { msg = config.getInitParameter("msg"); } }
全局异常处理器
-
public class CustomException extends RuntimeException{ }
-
/** * 全局异常处理器 * @RestControllerAdvice:全局异常处理器的注解 */ // @ControllerAdvice @RestControllerAdvice public class GlobalExceptionHander { @ExceptionHandler(CustomException.class) public Map<String, Object> customExceptionHandle(CustomException e){ Map<String, Object> map = new HashMap<>(); map.put("code", -2); map.put("message", "自定义异常发生"); return map; } @ExceptionHandler(Exception.class) public Map<String, Object> exceptionHandle(Exception e){ Map<String, Object> map = new HashMap<>(); map.put("code", -1); map.put("message", "其他异常发生"); return map; } }
全局日期格式化
-
在SpringMVC使用@DateTimeFormat和@JsonFormat
@DateTimeFormat(pattern = "yyyy-MM-dd") @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") private Date hiredate;
-
application.yml
#全局输入日期格式化,局部@DateTimeFormat spring: mvc: format: date: yyyy-MM-dd HH:mm:ss #全局输出日期格式化,局部@JsonFormat jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8
配置文件上传
#配置文件上传
spring:
servlet:
multipart:
#单个文件大小限制
max-file-size: 1MB
#单次请求总文件大小限制
max-request-size: 10MB
前后端分离
设置全局跨域处理
-
WebConfig.java配置文件
/** * 前后端分离的项目:设置全局跨域处理 * 浏览器的同源策略: * 在发送异步请求时,请求地址中 协议、服务器地址、服务器端口号必须保持一致 * 如果不一致就违反浏览器同源策略,浏览器禁止访问,会接收到一个CORS错误 * * 注意:协议http与https不同源 * 地址localhost与127.0.0.1不同源 * * 前后端分离的项目:设置全局跨域处理 */ @Override public void addCorsMappings(CorsRegistry registry) { registry //跨域访问的路径(资源) .addMapping("/**") //设置允许跨域的服务器地址 // .allowedOriginPatterns("http://localhost:63342") .allowedOriginPatterns("*") //设置允许跨域访问的请求方式 // .allowedMethods("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS") .allowedMethods("*") //设置允许跨域访问时的头部信息 // .allowedHeaders() //设置暴露给请求服务器头部信息 // .exposedHeaders() //设置跨域访问时是否允许携带凭证(cookie中JSESSIONID) .allowCredentials(true) //跨域访问时会先发送一个询问请求(询问当前请求是否允许跨域) //设置询问请求的发送周期 .maxAge(3600); }
-
后端资源
@RestController public class UserController { @GetMapping("/user/list") public List<User> list(){ User user1 = new User(1, "zhangsan", "123"); User user2 = new User(2, "lisi", "123"); User user3 = new User(3, "wangwu", "123"); User user4 = new User(4, "zhaoliu", "123"); return Arrays.asList(user1,user2,user3,user4); } }
-
前端请求(另一个项目)
$.ajax('http://localhost:8080/boot/user/list',{ type:'get', success:function (res) { var str=''; $.each(res,function () { str+='<tr>'; str+='<td>'+this.id+'</td>'; str+='<td>'+this.username+'</td>'; str+='<td>'+this.password+'</td>'; str+='</tr>'; }); $('#tab').html(str); } });
外部Tomcat
-
外部Tomcat支持tomcat jsp,内部Tomcat不支持tomcat jsp
-
新建项目的时候与SpringBoot相似,唯一不同是打包方式改为war
-
自己配置TomCat
-
将静态资源文件放到
webapp/WEB-IF/
下面 -
自动生成ServletInitializer.java文件
/** * 在外部tomcat启动服务器时加载springboot程序 * 如果把这个类去掉,不会启动springboot,也就无法使用@Controller等组件 */ public class ServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(SpringBoot05JspApplication.class); } }
-
可以使用 jsp 文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <base href="${pageContext.request.contextPath}/"> <title>Title</title> </head> <body> <div style="text-align: center"> <h1>首页</h1> <p><a href="/hello">测试</a></p> </div> </body> </html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <base href="${pageContext.request.contextPath}/"> <title>成功</title> </head> <body> <div style="text-align: center"> <h1>成功</h1> <p>${message}</p> </div> </body> </html>
-
Controller
@Controller public class HelloController { @GetMapping("/hello") public String hello(HttpServletRequest request){ System.out.println("HelloController中的hello方法执行了...."); request.setAttribute("message", "hello jsp"); return "success"; } }
-
application.yml
#配置jsp页面的前缀与后缀 spring: mvc: view: prefix: /WEB-INF/pages/ suffix: .jsp
后端数据校验
-
新建项目时除了选中工具和核心包外,还要选中IO中的Validation依赖
-
自动引入依赖
<!--后台数据校验--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
数据校验
-
/** * hibernate-validator框架,后台数据校验,通过注解的方式来实现 */ @Data @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) public class Student { /** * @NotNull 数据不能为null * @NotEmpty 数据不能为null,也不能为空字符串 * @NotBlank 数据不能为null,也不能为空字符串,也不能由空白字符组成 * @Size 校验字符串、数组、集合的长度 * @Range 校验数字的范围 * @Email 校验有效是否合法 * @Pattern 通过正则表达式校验 * * 注解通用配置项:message,校验失败时提示信息 */ @NotBlank(message = "姓名不能为空") @Size(message = "姓名长度为4~10个字符", min = 4, max = 10) private String name; @NotNull(message = "年龄不能为空") @Range(message = "年龄范围为0~100岁", min = 0, max = 100) private Integer age; @NotBlank(message = "邮箱不能为空") //xxx@xx.xx @Email(message = "邮箱格式不合法") //正则校验 // @Pattern(message = "", regexp = "") private String email; }
-
开启数据校验
@RestController public class ValidationController { /** * 开启校验 @Validated * * 在方法的参数上标注@Validated表示开启后台数据校验,每个开启的数据校验数据之后,需要跟随一个BindingResult类型的参数 * BindingResult对象用于接收校验失败的信息 */ @PostMapping("/save") public String save(@Validated Student student, BindingResult bindingResult){ //判断校验是否通过 if(bindingResult.hasErrors()){ List<ObjectError> allErrors = bindingResult.getAllErrors(); for (ObjectError error : allErrors) { System.out.println(error.getDefaultMessage()); } }else{ System.out.println(student); } return "ok"; } }
配置文件校验
-
application.yml
#用于校验配置文件 user: username: zhangsan age: 20 email: aa@aa.aa
-
配置类
@SpringBootApplication //使配置类的注解生效 @EnableConfigurationProperties(UserProperties.class) public class Springboot06ValidationApplication { public static void main(String[] args) { SpringApplication.run(Springboot06ValidationApplication.class, args); } }
@Data @ConfigurationProperties(prefix = "user") //添加批量读取配置文件的类上 @Validated public class UserProperties { @NotBlank(message = "用户名不能为空") @Size(message = "用户名长度为6~16个字符", min = 6, max = 16) private String username; @NotNull(message = "年龄不能为空") @Range(message = "年龄范围为0~100岁", min = 0, max = 100) private Integer age; @NotBlank(message = "邮箱不能为空") @Email(message = "邮箱格式不合法") private String email; }
正则表达式
-
可以参考JDK手册:java.util.regex / Pattern
-
后端使用正则表达式
public class ParttenTests { @Test public void run(){ /** * 正则表达式,验证字符串是否符合指定的规则 */ //1.String类 String str = "zhang#123"; System.out.println(str.matches("[abc]")); System.out.println(str.matches("[^abc]")); System.out.println(str.matches("[a-zA-Z]")); System.out.println(str.matches("[0-9]")); System.out.println(str.matches("[a-zA-Z0-9]")); System.out.println(str.matches("\\S")); System.out.println(str.matches("\\w{5,10}")); System.out.println(str.matches(".{5,10}")); System.out.println(str.matches(".*")); System.out.println("14345678901".matches("^1[3|5|6|7|8|9]\\d{9}$")); //2.Pattern类 boolean result = Pattern.matches("^[0-9]+$", "123"); System.out.println(result); //3.Matcher类 Pattern pattern = Pattern.compile("^[0-9]+$"); Matcher matcher = pattern.matcher("123a"); System.out.println(matcher.matches()); } }
-
前端使用正则表达式
<script type="text/javascript"> var regex = /^[0-9]+$/; var result = regex.test('123'); alert(result); </script>
-
使用注解
//正则校验 // @Pattern(message = "", regexp = "") private String email;
aop
-
引入依赖
<!--aop--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
-
Controller
@RestController public class AopController { @GetMapping("/list") public String list(){ System.out.println("AopController中的list方法...."); return "ok"; } }
-
AopAspect.java
/** * 切面类 */ @Component @Aspect // @Order(1) //设置切面的启动顺序 public class AopAspect { //公共切点表达式,方式一 public static final String POINT_CUT = "execution(* com.bjpowernode.controller.*.*(..))"; //公共切点表达式,方式二 @Pointcut("execution(* com.bjpowernode.controller.*.*(..))") public void exp(){} /** * 前置通知 */ @Before("execution(* com.bjpowernode.controller.*.*(..))") public void before(){ System.out.println("切面的前置通知..."); } /** * 后置通知 */ @After(POINT_CUT) public void after(){ System.out.println("切面的后置通知..."); } /** * 返回通知 */ @AfterReturning(value = "exp()", returning = "result") public void returning(Object result){ System.out.println("切面的返回通知..." + result); } }
连接数据库
使用jdbc
-
创建项目选择 jdbc api和Mysql Dirver 依赖
-
application.yml
#使用jdbc spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/student username: root password: 111111 #?切换数据源,需要配置才能生效 type: com.alibaba.druid.pool.DruidDataSource #数据源配置 initialSize: 5 minIdle: 10 maxActive: 50 maxWait: 30000
-
entity层 略
-
dao
public class DeptDaoImpl implements DeptDao { /** * 注入JdbcTemplate对象 * JdbcTemplate是spring-jdbc中提供的工具类,用于操作数据库 */ @Autowired private JdbcTemplate jdbcTemplate; @Override public int insert(Dept dept) { String sql="insert into dept(dname,loc) values(?,?)"; return jdbcTemplate.update(sql,dept.getDname(),dept.getLoc()); } }
-
测试
@SpringBootTest class Springboot08JdbcApplicationTests { @Autowired private DataSource dataSource; //查看数据源配置信息 @Test void contextLoads() { //自带数据源是: HikariDataSource System.out.println("数据源:"+dataSource); } /** * 使用jdbc完成查询 */ @Test void testJdbc() throws SQLException { Connection connection = dataSource.getConnection(); String sql="select deptno,dname,loc from dept where deptno=?"; PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setInt(1,60); ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()){ int deptno = resultSet.getInt("deptno"); String dname = resultSet.getString("dname"); String loc = resultSet.getString("loc"); System.out.println("编号="+deptno+" 名字="+dname+" 地址="+loc); } resultSet.close(); preparedStatement.close(); connection.close(); } }
使用数据库连接池Druid
自定义配置类
-
引入依赖,版本号不能省略
<!--德鲁伊数据源,自己写配置类--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency>
-
配置文件按application.yml
#配置数据源 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/student username: root password: 111111 #切换数据源,需要配置才能生效 type: com.alibaba.druid.pool.DruidDataSource #数据源配置 initialSize: 5 minIdle: 10 maxActive: 50 maxWait: 30000 #开启监控: 监控stat,SQL防火墙wall,日志slf4j,需要配置 filters: stat,wall,slf4j
-
自定义的配置类
/** * 自定义德鲁伊数据源配置类 */ @Configuration public class DruidConfig { @Bean //使用注解使配置文件的数据源生效 @ConfigurationProperties(prefix = "spring.datasource") public DruidDataSource dataSource(){ DruidDataSource druidDataSource = new DruidDataSource(); //可以在这配置,也可以使用配置文件配置 // druidDataSource.setInitialSize(10); return druidDataSource; } /** * sql监控的servlet */ @Bean public ServletRegistrationBean servletRegistrationBean(){ ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(); servletRegistrationBean.setServlet(new StatViewServlet()); servletRegistrationBean.addUrlMappings("/druid/*"); servletRegistrationBean.addInitParameter("loginUsername","admin"); servletRegistrationBean.addInitParameter("loginPassword", "123"); return servletRegistrationBean; } /** * sql监控过滤器 */ @Bean public FilterRegistrationBean filterRegistrationBean(){ FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(new WebStatFilter()); filterRegistrationBean.addUrlPatterns("/*"); //排除路径 filterRegistrationBean.addInitParameter("exclusions", "/druid/*,*.js,*.css,*.jpg,*.png"); return filterRegistrationBean; } }
自带配置类
-
引入依赖
<!--自带启动器的德鲁伊,配置类不用写了--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency>
-
配置
#使用自带启动类的德鲁伊 spring: datasource: #切换数据源 type: com.alibaba.druid.pool.DruidDataSource druid: #必要信息 driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/student username: root password: 111111 #连接池基本信息 initial-size: 6 min-idle: 7 max-active: 15 max-wait: 20000 #sql监控 filter: stat,wall,slf4j #serlvet stat-view-servlet: enabled: true url-pattern: /druid/* login-password: 111 login-username: root #filter web-stat-filter: enabled: true url-pattern: /* exclusions: "/druid/*,*.js,*.css,*.jpg,*.png"
JdbcTemplate工具
-
封装了jdbc的工具
-
DeptDaoImpl.java
@Repository public class DeptDaoImpl implements DeptDao { /** * 注入JdbcTemplate对象 * JdbcTemplate是spring-jdbc中提供的工具类,用于操作数据库 */ @Autowired private JdbcTemplate jdbcTemplate; @Override public int insert(Dept dept) { String sql="insert into dept(dname,loc) values(?,?)"; return jdbcTemplate.update(sql,dept.getDname(),dept.getLoc()); } @Override public List<Dept> select() { /** * queryForList() 批量查询返回数据封装在map集合中 * query(sql, rowMapper) 批量查询返回数据封装在指定的实体对象中 * * RowMapper 行映射器,将字段与实体中的属性对照 * BeanPropertyRowMapper 是RowMapper接口的实现类对象,根据字段名称与实体类中属性名称实现自动映射 */ String sql = "select deptno, dname, loc from dept order by deptno desc"; //手动映射 // jdbcTemplate.query(sql, new RowMapper<Dept>(){ // @Override // public Dept mapRow(ResultSet rs, int rowNum) throws SQLException { // Dept dept = new Dept(); // dept.setDeptno(rs.getInt("deptno")); // dept.setDname(rs.getString("dname")); // dept.setLoc(rs.getString("loc")); // return dept; // } // }); RowMapper<Dept> rowMapper = new BeanPropertyRowMapper<>(Dept.class); List<Dept> list = jdbcTemplate.query(sql, rowMapper); return list; } @Override public Dept selectById(Integer deptno) { String sql = "select deptno, dname, loc from dept where deptno=?"; /** * queryForObject(String sql, RowMapper rowMapper) 返回结果为单行多列 * queryForObject(String sql, Class<T> requiredType) 返回结果为单行单列 */ // int i = jdbcTemplate.queryForObject(sql, Integer.class); RowMapper<Dept> rowMapper = new BeanPropertyRowMapper<>(Dept.class); Dept dept = jdbcTemplate.queryForObject(sql, rowMapper, deptno); return dept; } }
-
测试类
@SpringBootTest class Springboot08JdbcApplicationTests { @Autowired private DataSource dataSource; @Test void contextLoads() { //自带数据源是: HikariDataSource //查看数据源配置信息 System.out.println("数据源:"+dataSource); if (dataSource instanceof DruidDataSource){ DruidDataSource druidDataSource=(DruidDataSource) dataSource; System.out.println(druidDataSource.getInitialSize()); System.out.println(druidDataSource.getMinIdle()); System.out.println(druidDataSource.getMaxActive()); System.out.println(druidDataSource.getMaxWait()); } } @Autowired DeptDao deptDao; @Test void testInsert(){ Dept dept = new Dept(); dept.setDname("采购部"); dept.setLoc("深圳"); int result = deptDao.insert(dept); System.out.println(result); } @Test void testSelect(){ List<Dept> list = deptDao.select(); for (Dept dept : list) { System.out.println(dept); } } @Test void testSelectById(){ Dept dept = deptDao.selectById(48); System.out.println(dept); } }
整合MyBatis
-
创建项目时,加上MyBatisFramwork依赖
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.2</version> </dependency>
-
数据库连接池依赖和分页插件
<!--德鲁伊数据源--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <!--分页插件--> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.4.2</version> </dependency>
-
配置文件
#配置数据源 spring: jackson: date-format: yyyy-MM-dd time-zone: GMT+8 mvc: format: date: yyyy-MM-dd datasource: #切换数据源 type: com.alibaba.druid.pool.DruidDataSource druid: #必要信息 driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/bjpowernode username: root password: root #连接池基本信息 initial-size: 6 min-idle: 7 max-active: 15 max-wait: 20000 #sql监控 filter: stat,wall,slf4j #serlvet stat-view-servlet: enabled: true url-pattern: /druid/* login-password: 111 login-username: root #filter web-stat-filter: enabled: true url-pattern: /* exclusions: "/druid/*,*.js,*.css,*.jpg,*.png" #mybatis配置 #1.SqlSessionFactory配置bean -- 在自动化配置中已实现 #2.mapper代理对象配置bean -- 通过注解的方式来实现 mybatis: #加载核心配置文件 # config-location: classpath:mybatis.xml #加载mapper文件 mapper-locations: classpath:mapper/*.xml # mapper-locations: classpath:mapper/**/*.xml #类型别名 type-aliases-package: com.bjpowernode.entity #全局参数: 与加载核心配置文件项冲突 configuration: #开启下划线转驼峰 map-underscore-to-camel-case: true #日志输出 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #插件:分页(单独配置) #分页组件pagehelper pagehelper: #自动识别数据库,应用数据库方言 #mysql: select * from table_name limit offset,pageSize #oracle: select * from (select *,rownum myrow from (select * from table_name) where rownum <= 10) where myrow >= 5 # auto-dialect: true #合理化分页 reasonable: true
-
@SpringBootApplication /** * 批量配置mapper代理 * 需要指定dao接口所在包 */ @MapperScans({ @MapperScan(basePackages = "com.bjpowernode.dao"), @MapperScan(basePackages = "com.bjpowernode.dao2") }) @MapperScan(basePackages = "com.bjpowernode.dao") public class Springboot09MybatisApplication { public static void main(String[] args) { SpringApplication.run(Springboot09MybatisApplication.class, args); } }
-
entity
@Data public class Emp { private Integer empno; private String ename; private String job; private Integer mgr; private Date hiredate; private Double sal; private Double comm; private Integer deptno; }
-
dao
/** * 使用注解来配置mapper代理对象 * * @Mapper * 作用:为当前接口生成mapper代理对象,并将代理对象放入ioc容器 * 类似于spring中单个接口代理对象的配置 */ @Mapper public interface EmpDao { List<Emp> select(); Emp selectById(Integer id); int insert(Emp entity); int update(Emp entity); int delete(Integer id); }
-
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="com.bjpowernode.dao.EmpDao"> <select id="select" resultType="emp"> select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp order by empno asc </select> <select id="selectById" parameterType="int" resultType="Emp"> select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp where empno=#{empno} </select> <insert id="insert" parameterType="com.bjpowernode.entity.Emp"> insert into emp(ename,job,mgr,hiredate,sal,comm,deptno) values(#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm},#{deptno}) </insert> <update id="update" parameterType="com.bjpowernode.entity.Emp"> update emp set ename=#{ename},job=#{job},mgr=#{mgr},hiredate=#{hiredate},sal=#{sal},comm=#{comm},deptno=#{deptno} where empno=#{empno} </update> <delete id="delete" parameterType="int"> delete from emp where empno=#{empno} </delete> </mapper>
-
service
public interface EmpService { Map<String, Object> page(Integer pageNumber, Integer pageSize); Emp get(Integer id); boolean save(Emp entity); boolean update(Emp entity); boolean remove(Integer id); }
@Service public class EmpServiceImpl implements EmpService { @Autowired private EmpDao empDao; @Override public Map<String, Object> page(Integer pageNumber, Integer pageSize) { PageHelper.startPage(pageNumber, pageSize); PageInfo<Emp> pageInfo = new PageInfo<>(empDao.select()); Map<String, Object> pageMap = new HashMap<>(); pageMap.put("list", pageInfo.getList()); pageMap.put("total", pageInfo.getTotal()); return pageMap; } @Override public Emp get(Integer id) { return empDao.selectById(id); } /** * springboot中事务管理 * 使用注解的方式来实现 */ @Override @Transactional(rollbackFor = Exception.class) public boolean save(Emp entity) { return empDao.insert(entity) > 0; } @Override @Transactional(rollbackFor = Exception.class) public boolean update(Emp entity) { return empDao.update(entity) > 0; } @Override @Transactional(rollbackFor = Exception.class) public boolean remove(Integer id) { return empDao.delete(id) > 0; } }
-
Controller
@RestController @RequestMapping("/emp") public class EmpController { @Autowired private EmpService empService; @GetMapping("/page") public Map<String, Object> page(Integer pageNumber, Integer pageSize){ Map<String, Object> pageMap = empService.page(pageNumber, pageSize); Map<String, Object> result = new HashMap<>(); result.put("code", 200); result.put("message", "ok"); result.put("data", pageMap); return result; } @GetMapping("/get/{id}") public Map<String, Object> get(@PathVariable("id") Integer id){ Map<String, Object> result = new HashMap<>(); result.put("code", 200); result.put("message", "ok"); result.put("data", empService.get(id)); return result; } @PostMapping("/save") public Map<String, Object> save(@RequestBody Emp emp){ Map<String, Object> result = new HashMap<>(); result.put("code", 200); result.put("message", "ok"); result.put("data", empService.save(emp)); return result; } @PutMapping("/edit") public Map<String, Object> edit(@RequestBody Emp emp){ Map<String, Object> result = new HashMap<>(); result.put("code", 200); result.put("message", "ok"); result.put("data", empService.update(emp)); return result; } @DeleteMapping("/remove/{id}") public Map<String, Object> remove(@PathVariable("id") Integer id){ Map<String, Object> result = new HashMap<>(); result.put("code", 200); result.put("message", "ok"); result.put("data", empService.remove(id)); return result; } }
Api接口文档 swagger
-
引入依赖
<!--swagger starter--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-boot-starter</artifactId> <version>3.0.0</version> </dependency>
-
配置文件
#swagger自定义配置 #测试访问文档页面:http://localhost:8080/swagger-ui/index.html swagger3: base-package: com.dx.controller name: xxx url: https://gitee.com/ email: 1233453534@qq.com version: 1.0 group-name: dx title: "标题 " description: "描述信息" terms-of-service-url: https://gitee.com/ license: cxs license-url: https://gitee.com/ #时间格式 spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 mvc: format: date: yyyy-MM-dd HH:mm:ss #解决swagger3.0和springboot整合的时问题 pathmatch: matching-strategy: ant_path_matcher
-
读取配置的类
@Data @ConfigurationProperties(prefix = "swagger3") public class SwaggerProperties { // 扫描的包 // 给这个包下面的接口创建文档 private String basePackage; // 作者姓名 private String name; // 作者主页链接 private String url; // 作者邮箱 private String email; // 版本号 private String version; // 分组名称 private String groupName; // 文档标题 private String title; //文档描述 private String description; // 组织地址 private String termsOfServiceUrl; // 许可证 private String license; // 许可链接 private String licenseUrl; }
-
注入配置类,配置swagger
@Configuration @EnableConfigurationProperties(SwaggerProperties.class) public class SwaggerConfig { @Autowired private SwaggerProperties swaggerProperties; /** * 配置swagger中的标题,描述,联系人,联系方法... */ public ApiInfo getApiInfo(){ //创建联系人对象 Contact contact = new Contact(swaggerProperties.getName(), swaggerProperties.getUrl(), swaggerProperties.getEmail()); return new ApiInfoBuilder() .contact(contact) .title(swaggerProperties.getTitle()) .description(swaggerProperties.getDescription()) .version(swaggerProperties.getVersion()) .license(swaggerProperties.getLicense()) .licenseUrl(swaggerProperties.getLicenseUrl()) .termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl()) .build(); } /** * swagger的配置,基础信息,设置接口路径等等 * RequestHandlerSelectors.basePackage()通过指定基础包来生成文档 * RequestHandlerSelectors.withMethodAnnotation()通过指定的方法上注解来实现生成文档 */ @Bean public Docket docket(){ return new Docket(DocumentationType.OAS_30) .apiInfo(getApiInfo()) .select() //文档的生成的位置,两种方式 // .apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage())) .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) .paths(PathSelectors.any()) .build(); } }
-
实体
@Data @ApiModel("学生实体") public class Student { @ApiModelProperty("学生编号") private Integer id; @ApiModelProperty("学生姓名") private String name; @ApiModelProperty("学生住址") private String address; @ApiModelProperty("出生日期") private Date birthday; }
-
Controller
-
配置对类的描述注解
-
配置对方法的描述的注解
-
对参数的注解
@RestController @RequestMapping("/student") public class StudentController { @ApiOperation("分页+条件查询 学生信息") @ApiImplicitParams({ @ApiImplicitParam(name = "pageNumber", value = "当前页码", required = false, dataType = "Integer",defaultValue = "1", paramType = "query"), @ApiImplicitParam(name = "pageSize", value = "每页条数", required = false, dataType = "Integer",defaultValue = "10", paramType = "query") }) @ApiResponses({ @ApiResponse(code = 0, message = "成功"), @ApiResponse(code = -1, message = "失败") }) @GetMapping("/page") public Result page(@RequestParam(value = "pageNumber",defaultValue = "1") Integer pagerNumber, @RequestParam(value = "pageSize",defaultValue = "5") Integer pageSize, Student student) { System.out.println(pagerNumber); System.out.println(pageSize); System.out.println(student); return Result.success(); } @GetMapping("/get/{id}") @ApiOperation("根据ID获取学生信息") @ApiImplicitParam(name="id",value = "学生编号",required = true, dataType="Integer", paramType = "path") public Result get(@PathVariable("id") Integer id) { System.out.println(id); return Result.success(); } @PutMapping("/edit") @ApiOperation("编辑学生信息") public Result edit(@RequestBody Student student){ System.out.println(student); return Result.success(); } @DeleteMapping("/remove/{id}") @ApiOperation("根据编号删除学生信息") @ApiImplicitParam(name = "id", value = "学生编号", required = true, dataType = "Integer", paramType = "path") public Result remove(@PathVariable("id") Integer id){ System.out.println(id); return Result.success(); } }
-
-
查看swagger页面
#测试访问文档页面:http://localhost:8080/swagger-ui/index.html
其他功能
spring boot 异步
-
异步调用需要配置注解
@Async
-
启动类 开启异步调用
//开启异步调用 @EnableAsync
-
案例:统计耗时
@Service public class AsyncService { /** * 异步调用注解 * 在处理请求的业务时,其中核心业务必须在同步处理中完成 * 辅助业务可以在异步处理中完成 */ @Async public void task1(){ try { long start = System.currentTimeMillis(); //模拟程序执行耗时 Thread.sleep(1000); long end = System.currentTimeMillis(); System.out.println("task1耗时:" + (end - start) + "毫秒"); } catch (InterruptedException e) { e.printStackTrace(); } } @Async public void task2(){ try { long start = System.currentTimeMillis(); //模拟程序执行耗时 Thread.sleep(2000); long end = System.currentTimeMillis(); System.out.println("task2耗时:" + (end - start) + "毫秒"); } catch (InterruptedException e) { e.printStackTrace(); } } @Async public void task3(){ try { long start = System.currentTimeMillis(); //模拟程序执行耗时 Thread.sleep(3000); long end = System.currentTimeMillis(); System.out.println("task3耗时:" + (end - start) + "毫秒"); } catch (InterruptedException e) { e.printStackTrace(); } } }
@RestController public class AsyncController { @Autowired private AsyncService asyncService; @GetMapping("/doAsync") public Map<String, Object> doAsync(){ long start = System.currentTimeMillis(); //调用service中的三个任务方法 //同步:第一个方法完成之后才能调用第二方法...耗时:三个方法时间相加 //异步:三个方法仅调用,不再登录方法执行完毕,即可向下执行 asyncService.task1(); asyncService.task2(); asyncService.task3(); Map<String, Object> map = new HashMap<>(); long end = System.currentTimeMillis(); map.put("code", 200); map.put("message", "调用方法成功,总耗时为" + (end-start) + "毫秒"); return map; } }
定时任务
-
在启动类上开启定时任务
//开启定时任务 @EnableScheduling
-
service 定时任务注解
-
定时表达式
@Service public class JobService { /** * @Scheduled * 定时任务注解 * cron配置项,为定时表达式 * 秒 分 时 日 月 周 年(可选) * *星号:表示每,每秒、每分、每时... * ?问好:只能在日和周两个位置出现,排除冲突 * -中划线:表示一个范围 * ,逗号:表示一个列表值,比如在星期中使用1,2,4 */ // @Scheduled(cron = "* * * ? * 1-5") @Scheduled(cron = "0/5 * * ? * 1-5") public void myJob(){ System.out.println("定时任务..."); } }
邮件
-
io中的 java mail … 依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency>
-
开启邮箱pop3的服务,写配置文件
spring: mail: #邮箱服务器地址 qq:smtp.qq.com 网易163:smtp.163.com host: smtp.qq.com #授权码,邮箱-》设置-》账户-》POP3/AMTP服务,开启服务后会获得授权码 password: xxxxxceoibdaaaaa username: 11118422@qq.com default-encoding: UTF-8
-
发送简单的内容
@Autowired private JavaMailSender javaMailSender; /** * 发送基本的内容(纯文本) */ @Test void testSend() { SimpleMailMessage simpleMailMessage = new SimpleMailMessage(); //发件人 simpleMailMessage.setFrom("11112342@qq.com"); //收件人 simpleMailMessage.setTo("22212342@qq.com"); //主题 simpleMailMessage.setSubject("这是一个测试邮件20220812"); //邮件内容 simpleMailMessage.setText("测试内容2022-08-12"); javaMailSender.send(simpleMailMessage); }
-
发送邮件工具类
- 添加文件
- 添加附件
/** * 测试发送复杂内容,例如图片和附件等 */ @Test void testSend2() throws MessagingException { MimeMessage mimeMessage = javaMailSender.createMimeMessage(); // 创建一个邮件工具,可以发送附件 MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage,true,"utf-8"); mimeMessageHelper.setFrom("28718422@qq.com"); mimeMessageHelper.setTo("28718422@qq.com"); mimeMessage.setSubject("这是一个携带了图片和附件的邮件20220812"); //拼接内容参数 StringBuilder sb = new StringBuilder(); sb.append("<html> <body> <h1 style='color:red'>springboot 测试邮件发送复杂格式o</h1>"); sb.append("<p style='color:blue;font-size:16px'>哈哈哈</p>"); sb.append("<p style='text-align:center'>居中</p>"); sb.append("<img src='cid:picture'/> </body></html>"); //如果要插入图片src='cid:picture' //设置内容,可以被html解析 mimeMessageHelper.setText(sb.toString(), true); // 从本地磁盘中读取到图片 站位到内容中去 mimeMessageHelper.addInline("picture",new File("C:\\Users\\NINGMEI\\Desktop\\aaa\\ddd.jpg")); // 添加附件 mimeMessageHelper.addAttachment("SpringBoot.doc",new File("D:\\course\\05-SpringBoot\\springboot\\document\\SpringBoot.doc")); javaMailSender.send(mimeMessage); }