Spring Boot
介绍
简化Spring应用开发的一个框架,整个Spring技术栈的一个大整合。
官方网站:https://spring.io/projects
优点:
- 快速创建Spring项目,使用嵌入式的服务,内置了Tomcat
- starters(启动器)自动依赖与版本控制
- 无需配置XML,开箱即用
系统要求:
Java 8、Maven 3.3+
maven配置:
给maven 的settings.xml配置文件的profiles标签添加
<mirrors>
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
</mirrors>
<profiles>
<profile>
<id>jdk-1.8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>
</profiles>
特点:
-
依赖管理 父项目做依赖管理、开发导入starter场景启动类、无需关注版本号 -
自动配置 自动配置了tomcat、配置SpringMVC、Web常见功能 常用注解: @Configuration //告诉SpringBoot这是一个配置类 == 配置文件
@Bean //给容器中添加组件。
@ComponentScan //包扫描
@Import({User.class, User2.class}) // 给容器中自动创建出这两个类型的组件
@Conditional //条件装配 ,注入
@ImportResource //原生配置文件引入
@Component //实现Bean的注入
@ConfigurationProperties //配置绑定 取到properties文件中的内容,封装到Bean中 配合@Component注解
@EnableConfigurationProperties 同上,就不需要加 @Component
Spring自动配置:
启动类 SpringBootApplication
1.@SpringBootConfiguration - @Configuration //配置类
2.@EnableAutoConfiguration
@AutoConfigurationPackage 指定了默认的包规则,将指定的一个包下的所有组件导入进来
@Import() 给容器中批量导入一些组件
按需开启自动配置项 xxxxAutoConfiguration ,按照条件装配规则
3.@ComponentScan() //包扫描
总结:
SpringBoot先加载所有的自动配置类,每个自动动,按配置类条件去生效,生效后就给容器配置很多的组件,容器有这些组件,就有了这些功能。
如果我们定制配置,可以直接@Bean替换底层默认的组件。
核心功能
yaml配置文件
person:
userName: zhangsan
birth: 2019/12/12 20:12:33
arr:
- jpg
- png
allPets:
sick:
- {name: tom}
- {name: jerry,weight: 47}
health: [{name: mario,weight: 47}]
@ConfigurationProperties(prefix = "mongodb")
@Component
public class Person {
private String userName;
}
简化开发
-
lombok <dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
-
dev-tools <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
静态资源访问
静态目录:/static /public /resources /META-INF/resources
改变默认的静态资源路径
spring:
mvc:
static-path-pattern: /res/**
resources:
static-locations: [classpath:/st1/]
add-mappings: false
RESTful
@RequestMapping(value = "/user",method = RequestMethod.GET) ---- @GetMapping()
@RequestMapping(value = "/user",method = RequestMethod.POST) ---- @POSTMapping()
@RequestMapping(value = "/user",method = RequestMethod.PUT) ---- @PUTMapping()
@RequestMapping(value = "/user",method = RequestMethod.DELETE) ---- @DELETEMapping()
# yml文件开启页面表单的Rest功能
spring:
mvc:
hiddenmethod:
filter:
enabled: true
常用注解
@PathVariable 获取路径变量
@RequestHeader 获取消息头信息
@RequestParam 请求消息参数
@CookieValue 获取Cookie值
@RequestBody 请求体信息 - 只有post请求需要
@RequestAttrbute 获取请求域中的值
@ModelAttribute 矩阵变量,默认禁用
例子:
@RestController
public class TestController {
@GetMapping("/car/{id}")
public String getCar(@PathVariable("id") Integer id,
@RequestHeader("User-Agent") String userAgent,
@RequestHeader Map<String,String> header,
@RequestParam("age") Integer age,
@RequestParam("inters") List<String> arrs,
@CookieValue("_ga") String _ga,
@CookieValue("_ga") Cookie cookie){
return null;
}
@PostMapping("/save")
public Map postMethod(@RequestBody String content){
Map<String,Object> map = new HashMap<>();
map.put("content",content);
return map;
}
}
复杂参数:
Map、Model(放在request的请求域 request.setAttribute)、Errors/BindingResult、RedirectAttributes( 重定向携带数据)、ServletResponse(response)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder
内容协商:
根据客户端接收能力不同,返回不同媒体类型的数据。
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
# yml文件开启请求参数内容协商模式
spring:
contentnegotiation:
favor-parameter: true #开启请求参数内容协商模式
只需要改变请求头中Accept字段。Http协议中规定的,告诉服务器本客户端可以接收的数据类型。
模板引擎
Thymeleaf使用:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
基本语法:
表达式名字 | 语法 | 用途 |
---|
变量取值 | ${…} | 获取请求域、session域、对象等值 | 选择变量 | *{…} | 获取上下文对象值 | 消息 | #{…} | 获取国际化等值 | 链接 | @{…} | 生成链接 | 片段表达式 | ~{…} | jsp:include 作用,引入公共页面片段 |
拦截器
1.继承HandlerInterceptor 接口
package com.good.yan.config.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
log.info("preHandle拦截的请求路径是{}",requestURI);
HttpSession session = request.getSession();
Object loginUser = session.getAttribute("LoginState");
if(loginUser != null){
return true;
}
request.setAttribute("msg","请先登录");
request.getRequestDispatcher("/login").forward(request,response);
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandle执行{}",modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("afterCompletion执行异常{}",ex);
}
}
2.配置拦截器
package com.good.yan.config.interceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home/list");
registry.addViewController("/index.html").setViewName("home/list");
registry.addViewController("/index").setViewName("home/list");
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/login","/doLogin","/asserts/**");
}
}
文件上传
@PostMapping("/upload")
public String upload(@RequestPart("headerImg") MultipartFile headerImg,
@RequestPart("photos") MultipartFile[] photos) throws IOException {
if(!headerImg.isEmpty()){
headerImg.transferTo(new File("H:\\cache\\"+headerImg.getOriginalFilename()));
}
if(photos.length > 0){
for (MultipartFile photo : photos) {
if(!photo.isEmpty()){
photo.transferTo(new File("H:\\cache\\"+photo.getOriginalFilename()));
}
}
}
return "上传ok";
}
异常处理
自定义错误页:在templates目录下寻找error目录下的404.html 、5xx.html的页面。
定义全局异常:
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(value = Exception.class)
public Map<String, Object> defaultErrorHandler(Exception e) {
log.error("Exception", e);
Map<String, Object> map = new HashMap<String, Object>();
map.put("code", FIVE);
map.put("message", e.getMessage());
return map;
}
}
数据访问
SpringBoot连接Mysql
# 1.引入包
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
# 2.修改yml配置文件
spring:
datasource:
username: 用户名
password: 密码
# mysql8版本以上的驱动包,需要指定以下时区
url: jdbc:mysql://127.0.0.1:3306/数据库?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
driver-class-name: com.mysql.cj.jdbc.Driver
使用Druid数据源
官网地址:https://github.com/alibaba/druid
# 1.引入包
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.17</version>
</dependency>
# 2.修改yml配置文件
spring:
datasource:
url: jdbc:mysql://localhost:3306/db
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
druid:
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 60000
#...
# 监控哪些包
aop-patterns: com.good.yan.modules.*
# 底层开启功能,stat(sql监控),wall(防火墙)
filters: stat,wall
stat-view-servlet: # 配置监控页功能
enabled: true # 开启维护功能
login-username: admin # 访问的用户名和密码
login-password: admin
resetEnable: false # 是否重置按钮启用
web-stat-filter: # 监控web
enabled: true
urlPattern:
详情配置地址:https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter
整合MyBatis操作
官网地址:https://github.com/mybatis
# 1.引入包
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
# 2.修改yml配置文件
mybatis:
# 映射配置文件路径
mapper-locations: classpath:mybatis/mapper
配置文件配置:https://mybatis.org/mybatis-3/zh/configuration.html
指标监控
每一个微服务在云上部署以后,我们都需要对其进行监控、追踪、审计、控制等。
# 1.引入依赖包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
# 2.修改配置文件
management:
endpoints:
enabled-by-default: true #暴露所有端点信息
web:
exposure:
include: '*' #以web方式暴露,暴露所有
# 3.访问测试
http://localhost:8080/actuator/beans
http://localhost:8080/actuator/configprops
http://localhost:8080/actuator/metrics
http://localhost:8080/actuator/metrics/jvm.gc.pause
http://localhost:8080/actuator/endpointName/detailPath
可视化
地址: https://github.com/codecentric/spring-boot-admin
详细配置:https://www.yuque.com/atguigu/springboot/sgpvgn
快速开始:https://codecentric.github.io/spring-boot-admin/2.5.1/#getting-started
# 1.服务端配置-引入依赖包
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>2.3.1</version>
</dependency>
# 2.服务端配置-启动类增加 @EnableAdminServer注解
# 3.客户端配置-引入包
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>2.3.1</version>
</dependency>
# 4.客户端配置-更改配置文件
spring:
boot:
admin:
client:
url: http://localhost:8888
instance:
prefer-ip: true # 使用ip进行注册
# 5.访问服务端,可以看到可视化界面
Mybatis
是一个基于Java的持久层框架。
下载地址:https://github.com/mybatis/mybatis-3
关于环境搭建在我的码云SpringBoot案例有:
详细配置:https://mybatis.org/mybatis-3/zh/configuration.html
默认的类型别名
alias | mapped type |
---|
_byte | byte | _long | long | _short | short | _int | int | _integer | int | _double | double | _boolean | boolean | string | String | byte | Byte | long | Long | short | Short | int | Integer | integer | Integer | double | Double | float | Float | boolean | Boolean | date | Date | decimal | BigDecimal | digdecimal | BigDecimal | object | Object | map | Map | hashmap | HashMap | list | List | arraylist | ArrayList | collection | Collection | iterator | iterator |
增删改查案例
-
添加 <insert id="insertUser">
insert into t_user values(null,'admin','123456')
</insert>
-
删除 <delete id="deleteUser">
delete from t_user where id = 6
</delete>
-
修改 <update id="updateUser">
update t_user set username = '张三' where id = 5
</update>
-
查询一个实体类对象 <select id="getUserById" resultType="com.xxx.User">
select * from t_user where id = 2
</select>
-
模糊查询 <select id="getUserByLike" resultType="com.xxx.User">
select * from t_user where username like "%"#{mohu}"%"
</select>
-
自定义映射resultMap
<resultMap id="ContentResultMap" type="Content">
<id property="id" column="id"></id>
<result property="content" column="content"></result>
<collection property="bills" ofType="Bill">
<id property="bid" column="bid"></id>
<result property="billCode" column="bill_code"></result>
<result property="billName" column="bill_name"></result>
</collection>
</resultMap>
<select id="findAll" resultMap="ContentResultMap" >
select * from content c left join bill b on c.id = b.pid
</select>
<resultMap id="BillResultMap" type="Bill">
<id property="bid" column="bid"></id>
<result property="billCode" column="bill_code"></result>
<result property="billName" column="bill_name"></result>
<association property="content" javaType="Content">
<id property="id" column="id"></id>
<result property="content" column="content"></result>
</association>
</resultMap>
<select id="findBillAll" resultMap="BillResultMap">
select * from bill b left join content c on b.pid = c.id
</select>
-
动态SQL
<select id="getEmp" resultType="Emp">
select * from t_emp
<where>
<if test="empName != null and empName !=''">
emp_name = #{empName}
</if>
</where>
</select>
<select id="getEmp" resultType="Emp">
select * from content
<trim prefix="where" suffixOverrides="and|or">
<if test="nodeCode != null and nodeCode != ''">
node_Code = #{nodeCode} and
</if>
</trim>
</select>
<select id="findAllChoose" resultMap="CResultMap" >
select * from content c left join bill b on c.id = b.pid
<where>
<choose>
<when test="bill.billCode != null and bill.billCode != ''">
b.bill_code like concat('%',#{bill.billCode} ,'%') escape '/'
</when>
<when test="bill.billName != null and bill.billName != ''">
b.bill_name like concat('%',#{bill.billName} ,'%') escape '/'
</when>
<otherwise>
b.pay = 1
</otherwise>
</choose>
</where>
</select>
<delete id="deleteByAll">
delete from content where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
延迟加载
# 1. 配置文件:mybatis-config.xml
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
# 2. 此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。
# association和collection中的fetchType属性进行设置,fetchType="lazy(延迟加载)|eager(立即加载)"
<resultMap id="BillResultMOne" type="Bill">
<id property="bid" column="bid"></id>
<result property="billCode" column="bill_code"></result>
<result property="billName" column="bill_name"></result>
<association property="content"
select="com.xxx.dao.ContentMapper.getContent"
column="id"
fetchType="lazy"></association>
</resultMap>
MyBatis的缓存
一级缓存
一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,默认是开启的。
失效情况:
- 不同的SqlSession下有不同的一级缓存
- 查询条件不同
- 同一个SqlSession两次查询期间执行了任何一次增删改操作
- 同一个SqlSession两次查询期间手动清空了缓存
二级缓存
二级缓存是SqlSessionFactory级别。
开启条件:
- 在核心配置文件中,设置全局配置属性cacheEnabled=“true”,默认为true,不需要设置
- 在映射文件中设置标签
- 二级缓存必须在SqlSession关闭或提交之后有效
- 查询的数据所转换的实体类类型必须实现序列化的接口
相关配置:
-
在mapper配置文件中添加的cache标签可以设置一些属性 -
eviction属性:缓存回收策略 -
LRU 最近最少使用的:移除最长时间不被使用的对象,默认的。 FIFO 先进先出:按对象进入缓存的顺序来移除它们。 SOFT 软引用:移除基于垃圾回收器状态和软引用规则的对象。 WEAK 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。 -
flushInterval属性:刷新间隔,单位毫秒 -
默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句(增删改)时刷新 -
size属性:引用数目,正整数,代表缓存最多可以存储多少个对象,太大容易导致内存溢出 -
readOnly属性:只读,true/false true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。 false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false
缓存EHCache
# 1.引入包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
# 2.创建ehcache.xml配置文件
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</defaultCache>
<cache name="Bill"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</cache>
</ehcache>
# 3.配置文件添加这行
spring.cache.ehcache.config=ehcache.xml
# 4.启动类添加这个注解
@EnableCaching
# 5.实体类实现可序列化接口Serializable
# 6. 使用
// 进行添加缓存
@Cacheable(value = "Bill",key = "'bill:'+#bid")
@Override
public List<Bill> findAll() {
return billMapper.findAll();
}
// 删除缓存
@CacheEvict(value="Bill", allEntries=true)
public void save(Bill bill) {
//...
}
逆向工程
-
新建一个springboot 工程 -
修改pom.xml <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.7</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.7</version>
<configuration>
<verbose>true</verbose>
<overwrite>true</overwrite
<configurationFile>
src/main/resources/generatorConfig.xml
</configurationFile>
</configuration>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
</dependencies>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
-
创建generator.xml配置文件 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="default" targetRuntime="MyBatis3Simple">
<commentGenerator>
<property name="suppressDate" value="true"/>
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://10.238.233.180:3306/b?useUnicode=true&characeterEncoding=utf-8&serverTimezone=UTC"
userId="root"
password="xxx"></jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<javaModelGenerator targetPackage="com.good.yan.modules.entities"
targetProject="src/main/java">
<property name="enableSubPackages" value="false"/>
<property name="constructorBased" value="true"/>
<property name="trimStrings" value="true"/>
<property name="immutable" value="false"/>
</javaModelGenerator>
<sqlMapGenerator targetPackage="com.good.yan.modules.mapper"
targetProject="src/main/java">
<property name="enableSubPackages" value="false"/>
</sqlMapGenerator>
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.good.yan.modules.dao"
targetProject="src/main/java">
<property name="enableSubPackages" value="false"/>
</javaClientGenerator>
<table tableName="t_md_org" domainObjectName="Org"
enableCountByExample="false" enableUpdateByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false"></table>
</context>
</generatorConfiguration>
-
运行,点击maven中的generate进行运行。
分页插件
# 1.引包
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
# 2.配置分页插件
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="reasonable" value="true"/>
</plugin>
</plugins>
# 3.案例
public void testPageHelper() throws IOException {
PageHelper.startPage(1, 2);
List<Bill> all = billMapper.findAll();
// 参数:数据 、 页码 尽量是奇数 1,2,3,4,5
PageInfo<Bill> pageInfo = new PageInfo<>(all,5);
}
常用数据:
- pageNum:当前页的页码
- pageSize:每页显示的条数
- size:当前页显示的真实条数
- total:总记录数
- pages:总页数
- prePage:上一页的页码
- nextPage:下一页的页码
- isFirstPage/isLastPage:是否为第一页/最后一页
- hasPreviousPage/hasNextPage:是否存在上一页/下一页
- navigatePages:导航分页的页码数
- navigatepageNums:导航分页的页码,[1,2,3,4,5]
|