Spring Boot 集载MyBatis与JPA
什么是Spring Boot
Spring 特点
-
能创建独立的Spring Boot应用
-
嵌入Tomcat,Jetty Undertow而且不需要部署他们
-
提供的“starters” poms来简化Maven配置
https://www.cnblogs.com/qlqwjy/p/7979159.html
Spring将很多魔法带入了Spring应用程序的开发之中,其中最重要的是以下四个核心。
- 自动配置:针对很多Spring应用程序常见的应用功能,Spring Boot能自动提供相关配置
- 起步依赖:告诉Spring Boot需要什么功能,它就能引入需要的库。
- 命令行界面:这是Spring Boot的可选特性,借此你只需写代码就能完成完整的应用程序,无需传统项目构建。
- Actuator:让你能够深入运行中的Spring Boot应用程序,一探究竟。
-
尽可能自动配置Spring应用
-
提供生产指标,健壮检查和外部化配置
-
在spring boot出现之前,使用spring框架时需要大量的xml配置,现在则可以仅需要少量的配置或无需配置
Spring优点
Spring的目标不在于为己解决的问题领域提供新的解决方案,而是为平台带来一种新的开发体验,从而简化对这些已有技术的使用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PjiW834j-1638550633743)(SpringBoot.assets/image-20210420104137977.png)]
Spring Boot集成第三方类库的步骤
-
通过maven引入springboot-XXXX-starter
-
修改yml或properties全局统一配置文件
-
加入一个Java Config。这个属于个性化配置,如果使用通用配置,这一步不需要。
- 官方默认集成的类库参考:
- https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#using-boot-starter
什么是Spring Boot Starter
Spring Boot Starter是一组被依赖第三方类库的集合
-
如开发WEB应用,引入Spring-boot-starter-web即可,该starter会自动引入如下类库:
-
Spring ——spring核心,baens,context上下文,AOP面向切面
-
Web MVC ——Spring MVC
-
Jackson —— JSON 数据的序列化与反序列化
-
嵌入式Servlet Container ——Tomcat
-
日志框架Loggin——logback,slf4j
-
创建 Spring Boot工程
自行百度
也可以直接把web环境添加进去 ——自行百度
boot 自带LOG_PATH 日志
项目结构目录
目录位置 | 功能 |
---|---|
src / main / java | 项目的java文件存放位置,初始化包包含主程序入口 XXXApplication,可以通过直接运行该类来启动Srping Boot应用 |
src / main / resources | 存放静态资源,图片、CSS、javaScript、web页面模板文件等 |
src / test | 单元测试代码目录 |
.gitignore | git版本管理排除文件 |
target 文件夹 | 项目代码构建打包结果文件存放位置,不需要认为维护 |
pom.xml | maven项目配置文件 |
application.properties (application.yml) | 用于存放程序各种依赖模块的配置信息,比如服务器端口,数据库连接配置等 |
src / main / resources / static | 主要用于存放CSS、图片、js等开发用到的静态文件 |
src / main / resources / pulic | 用来存放可以直接用于访问的html文件 |
src / main / resources / templa | 用于存放web开发模板文件 |
热加载与热部署
### devtools实现热加载
-
依赖(创建工程时如没有勾选则加入如下依赖)
org.springframework.boot
spring-boot-devtools
true
设置IDEA
- 运行时编译配置:“shift + Ctrl + Alt + / ",选择 ”Registry“,选中打勾 compiler.automake.allow.when.app.running
代码修改后
- 使用 Ctrl + shift + F9对修改过后的类重新编译(设置 Running Application Update Policies也可到达效果)
Controller深入
一、Controller作用
-
处理用户请求
-
类似于Servlet,但比Servlet更加方便灵活,自动完成了很多工作
自动接收请求参数
类型转换
数据验证
二、常用注解
-
@Controller
将bean定义为控制器
-
@RestController
-
RestConntroller相当于@Controller与@ResponseBody结合
- Controller 将控制器类注入到Spring上下文环境中,该类RequestMapping标注为HTTP服务端点
- ResponseBody的作用,请求响应默认使用的序列化方式是JSON,而不是跳转到jsp或模板页面。
-
-
@RequestMapping注解
- value: 应用请求端点,最核心的属性,用于标志请求处理方法的唯一性;
- method: HTTP协议的method类型, 如:GET、POST、PUT、DELETE等;
- consumes: HTTP协议请求内容的数据类型(Content-Type),如application/json, text/html;
- produces: HTTP协议响应内容的数据类型。produces = “text/plain;charset=utf-8”
- params: HTTP请求中必须包含某些参数值的时候,才允许被注解标注的方法处理请求。
- headers: HTTP请求中必须包含某些指定的header值,才允许被注解标注的方法处理请求。
-
@RequestBody与@ResponseBody
-
@RequestBody修饰请求参数,注解用于接收HTTP的body,默认是使用JSON的格式,能够使用对象或者嵌套对象接收前端数据
-
@ResponseBody修饰返回值,注解用于在HTTP的body中携带响应数据,默认是使用JSON的格式。如果不加该注解,spring响应字符串类型,是跳转到模板页面或jsp页面的开发模式。即加上这个注解开发的是一个数据接口,不加这个注解开发的是一个页面跳转控制器。
@ResponseBody的作用其实是将java对象转为json格式的数据。当请求为PUT与DELETE时一定需要加上该注解。注意:在使用此注解之后不会再走视图处理器,而是直接将数据写入到输入流中,他的效果等同于通过response对象输出指定格式的数据。@GetMapping("/toIndex4") //加上@ResponseBody表示返回的是字符串或者json @ResponseBody public UserVo toIndex4(){ UserVo user = new UserVo(); user.setUid(11); user.setPwd("ss"); user.setUname("许仙"); return user; }
-
-
@ModelAttribute
@ModelAttribute主要的作用是将数据添加到模型对象中,用于视图页面显示。
-
@PathVariable 与@RequestParam
-
@PathVariable用于URI上的{参数},如下方法用于删除一篇文章,其中id为文章id。如:我们的请求URL为“/article/1”,那么将匹配DeleteMapping并且PathVariable接收参数id=1;@PathVariable用于将请求RESTFUL风格的URL中的模板变量映射到功能处理方法的参数上,即取出uri模板中的变量作为参数
-
而RequestParam用于接收普通表单方式或者ajax模拟表单提交的参数数据。
@DeleteMapping("/article/{id}") public @ResponseBody AjaxResponse deleteArticle(@PathVariable Long id) @PostMapping("/article") public @ResponseBody AjaxResponse deleteArticle(@RequestParam Long id) @GetMapping("/toIndex") // 将它接收到的un参数名称绑定到别名uname中去 public ModelAndView toIndex2(@RequestParam("un")String uname){ ModelAndView mv = new ModelAndView("index"); mv.addObject("uname",uname); return mv; }
-
-
RequestMapping
-
@PostMapping等同于@RequestMapping的method等于POST。
-
@GetMapping等同于@RequestMapping的method等于GET。
-
@PutMapping等同于@RequestMapping的method等于PUT。
-
@DeleteMapping等同于@RequestMapping的method等于DELETE。
-
8.@RequestBody与@PathVariable
相当于get或者post等传过来的参数,
相当于路径上的值
/**
* @author XuLuBao
* @version V1.0
* @Package com.trkj.boot1.controller
* @date 2021/4/20 16:11
*/
@Log4j2
@RestController
//相当于这两个的作用 @Controller + @ResponseBody
public class RestFulController {
@GetMapping("/user/{id}")
public UserVo findUser(@PathVariable("id")int id){
UserVo vo = new UserVo();
vo.setUname("许仙");
vo.setUid(1);
log.debug("获取成功");
return vo;
}
@DeleteMapping("/user/{uid}/{na}")
public String delUser(@PathVariable("uid")int id,@PathVariable("na")String na){
System.out.println(id+na);
log.debug("删除成功");
return "成功删除!";
}
@PutMapping("/user")
// 对应起来
public String alertUser( @RequestBody UserVo vo){
log.debug("修改成功");
return "成功修改";
}
@PostMapping("/user")
public UserVo addUser(@RequestBody UserVo vo){
vo.setPwd("ss");
log.debug("添加成功");
return vo;
}
}
三、Controller常用向客户端返回值方法
1. 返回String,用于表示视图名称,如需要向客户端返回数据,可配合方法参数中的Model对象来使用。或者使用@ModelAttribute(“xx”)可直接针对参数给定别名后向Model添加对应的值。
2. 返回String,用于表示直接向客户端返回字符串结果,此时并不是返回视图,而是直接返回数 据。该字符串可以是普通字符串,也可以Json格式字符串。需要在方法上使用```<@ResponseBody>```注解。
3. 返回ModelAndView,视图加数据,如需要向客户端返回数据时,用```ModelAndView```将会比较方便。
四、Controller常用传参方式
-
普通参数
-
参数为JavaBean对象
-
接收Date类型的参数,默认情况下只支持yyyy/dd/mm格式的日期
-
RESTFul风格的方式参数
五、重定向
默认情况下是转发,如果是重定向,返回结果添加 redirect前缀
@RequestMapping("redirect")
public String redirect(){
return "redirect:/hello.jsp";
}
案列
@PostMapping("/show")
// 也会帮我封装好了,添加@ModelAttribute("user")只是往作用域里面添加了数据用于访问
public String toShowUser(UserVo user, Model model){
user.setPwd("15111666207");
// 相当于自动往作用域里面存放值,boot自动帮我做了这些事
//
model.addAttribute("vo",user);
return "show";
}
遇到的问题
@SessionAttributes(value = {“user”},types = {User.class})
SessionAttributes 在运行之前会对参数名称为“user”的参数进行存储到session当中去
在需要对他进行清楚的的时候直接在方法中添加:SessionStatus,在方法体中调用 SessionStatus.setComplete();进行清除,但是它只能清除在SessionAttributes中存储的值
判断对象是否为空
@GetMapping(value = "/indexU" )
public String chkLong(User user,SessionStatus status) {
if (user!=null&&user.getName()!=null) {
if (user.getName().length()>=6) {
return "index";
}else {
status.setComplete();
return "login";
}
} else {
status.setComplete();
return "login";
}
}
在以上由于在类上添加了,得到的user是已经创建过的对象,但是它里面的值不是空,需要判断对象之后也还要判断属性。
- List 在SessionAttributes中报错,建议使用Model进行值的存储,一直报错,拿不到值
Spring Boot 添加日志
在resources目录中添加logback-spring.xml日志文件,添加相应的日志代码
Spring Boot 整合 Swagger
添加 Maven 相关依赖,这里需要添加上WEB和SWAGGER依赖。
- WEB依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
- swagger依赖,这里选择 2.9.2 版本。
<!-- swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
添加配置类
-
添加一个swagger 配置类,在工程下新建 config 包并添加一个 SwaggerConfig 配置类。
-
SwaggerConfig.java
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; @Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket createRestApi(){ return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.any()) .paths(PathSelectors.any()).build(); } private ApiInfo apiInfo(){ return new ApiInfoBuilder() .title("Kitty API Doc") .description("This is a restful api document of Kitty.") .version("1.0") .build(); } }
-
-
添加一个控制类
/** * @author XuLuBao * @version V1.0 * @Package com.trkj.boot1test1.controller * @date 2021/4/21 15:12 */ /*方法注解*/ @Api(value = "desc of class") @RestController public class TestController { /*方法注解*/ @ApiOperation(value="desc of method",notes="notes") @GetMapping public Object hello(/*参数注解*/@ApiParam(value = "desc of param",required = true)@RequestParam String name){ return "Hello" + name + "!"; } }
Spring 跨域
-
添加配置类
@Configuration @Slf4j public class AppCorsConfiguration { private CorsConfiguration buildConfig() { log.debug("开始设置"); CorsConfiguration appCorsConfiguration = new CorsConfiguration(); appCorsConfiguration.addAllowedOrigin("http://localhost:8081"); appCorsConfiguration.addAllowedOrigin("http://127.0.0.1:8081"); appCorsConfiguration.addAllowedHeader("*"); // 2允许任何头 appCorsConfiguration.addAllowedMethod("OPTIONS"); appCorsConfiguration.addAllowedMethod("HEAD"); appCorsConfiguration.addAllowedMethod("GET"); appCorsConfiguration.addAllowedMethod("PUT"); appCorsConfiguration.addAllowedMethod("POST"); appCorsConfiguration.addAllowedMethod("DELETE"); appCorsConfiguration.addAllowedMethod("PATCH"); appCorsConfiguration.setAllowCredentials(true);//这两句不加不能跨域上传文件, appCorsConfiguration.setMaxAge(3600L);//加上去就可 return appCorsConfiguration; } @Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", buildConfig()); // 4 return new CorsFilter(source); } }
-
或者添加
@CrossOrigin
-
Spring Boot+ JWT
什么是jwt
Json Web Token(JWT):JSON网络令牌,是为了在网络应用环境间传递声明而制定的一种基于JSON的开放标准((RFC 7519)。JWT是一个轻便的安全跨平台传输格式,定义了一个紧凑的自包含的方式用于通信双方之间以 JSON 对象行使安全的传递信息。因为数字签名的存在,这些信息是可信的。
-
Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。
-
JWT是由三段信息构成的,将这三段信息文本用.链接一起就构成了JWT字符串。
+ >eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
jwt的组成
jwt由三部分组成
-
头部 header
- 头部一般有两部分信息:token类型和采用的加密算法
{
“alg”: “HS256”,
“typ”: “JWT”
}
-
载荷 plyload
-
该部分一般存放一些有效的信息.有效信息包含了三个部分,标准中注册的声明,共有的声明,私有的声明(即自定义字段)
注册声明是指声明名称已在IANA JSON Web Token注册表中注册并在规范中定义的声明,JWT标准中注册的声明 (建议但不强制使用) 包含七个字段:
- iss (issuer):签发人
- exp (expiration time):过期时间
- sub (subject):主题
- aud (audience):受众(接收对象)
- nbf (Not Before):生效时间
- iat (Issued At):签发时间
- jti (JWT ID):编号
-
公有声明是指未在规范中定义但已在IANA JSON Web Token注册表中注册的声明,或者使用防冲突名称命名(例如,包含命名空间)的声明。
-
私有声明是指没有注册而是由JWT的消费者和生产者自定义的声明。正因为如此,存在名称冲突的可能性。
{
“sub”: “1234567890”,
“name”: “John Doe”,
“iat”: 1516239022
}
-
-
签证 signature
-
签证 JWT最后一个部分.该部分是使用了HS256加密后的数据;包含了三个部分:
-
hander(base64后的)
-
payload(base64后的)
-
secret 私钥
secret是保存在服务器端的,JWT的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,他就是你服务端的密钥,在任何场景都不应该泄露出去.一旦客户端得知了这个secret,那就意味这客户端可以自我签发JWT了.
-
-
传统Cookid+Session与JWT对比
-
cookie+session这种模式通常是保存在内存中,而且服务从单服务到多服务会面临的session共享问题,随着用户量的增多,开销就会越大。
-
cookie+session限制了应用的扩展能力,特别是分布式应用中各服务器如何共享session是一个非常头疼的问题。
-
JWT只需要服务端生成token,客户端保存这个token,每次请求携带这个token,服务端认证解析就可,减轻了服务器压力。也非常适合分布式应用开发。
-
JWT是无状态,一次性的。想修改里面的内容,就必须签发一个新的JWT。即缺陷是一旦下发,服务后台无法拒绝携带该jwt的请求(如踢除用户)
- 如果API设计成规范的无状态RESTful,建议使用token
- 如果API被不同终端消费,Cookie受限,建议使用token
- 如果前后端分离应用,建议使用token
- 如果没有使用以上方式开发,建议选择Cookie Session
JWT访问流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6W5Lp87Z-1638550633746)(SpringBoot.assets/image-20210425094857539.png)]
Spring boot JWT工程
-
Maven依赖配置
io.jsonwebtoken
jjwt
0.9.1
-
application.yml配置
jwt: #密钥key secret: JWTSecret #HeaderKEY header: JWThEADERnAME #过期时间毫秒 expire: 100
server: port: 8089 #连接池配置 servlet: context-path: /jwttest spring: datasource: url: jdbc:mysql://127.0.0.1:3308/mydb?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8 username: root password: root2020 driver-class-name: com.mysql.cj.jdbc.Driver # 使用druid数据源 type: com.alibaba.druid.pool.DruidDataSource druid: # 配置测试查询语句 validationQuery: SELECT 'x' # 初始化大小,最小,最大 initialSize: 10 minIdle: 10 maxActive: 200 # 配置一个连接在池中最小生存的时间,单位是毫秒 minEvictableIdleTimeMillis: 180000 testOnBorrow: false testWhileIdle: true removeAbandoned: true removeAbandonedTimeout: 1800 logAbandoned: true # 打开PSCache,并且指定每个连接上PSCache的大小 poolPreparedStatements: true maxOpenPreparedStatements: 100 # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 filters: stat,wall,log4j2 # 通过connectProperties属性来打开mergeSql功能;慢SQL记录 connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 redis: database: 0 # Redis 数据库索引(默认为 0) host: 127.0.0.1 # Redis 服务器地址 port: 6379 # Redis 服务器连接端口 password: redis2020 # Redis 服务器连接密码(默认为空) lettuce: pool: max-active: 8 # 连接池最大连接数(使用负值表示没有限制) 默认 8 max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1 max-idle: 8 # 连接池中的最大空闲连接 默认 8 min-idle: 0 # 连接池中的最小空闲连接 默认 0 # JWT配置 jwt: # 密匙KEY secret: JWTSecret # HeaderKEY header: JWTHeaderName # Token前缀字符 tokenPrefix: Security-c # 过期时间毫秒 expire: 100000 mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.trkj.jwtdemo.entity pagehelper: helperDialect: mysql reasonable: true supportMethodsArguments: true params: count=countS
-
编写jwtTokenUtil工具类
@Slf4j @Data @ConfigurationProperties(prefix = "jwt") @Component public class JwtTokenUtil { private String secret; private Long expire; private String header; /** * 生成token令牌 * * @param userName 用户 * @return 令token牌 */ public String generateToken(String userName) { Map<String, Object> claims = new HashMap<>(2); claims.put("sub", userName); //claims.put("id",user.getId()); claims.put("created", new Date()); return generateToken(claims); } /** * 从令牌中获取用户名 * * @param token 令牌 * @return 用户名 */ public String getUsernameFromToken(String token) { String username; try { Claims claims = getClaimsFromToken(token); username = claims.getSubject(); } catch (Exception e) { username = null; } return username; } /** * 判断令牌是否过期 * * @param token 令牌 * @return 是否过期 */ public Boolean isTokenExpired(String token) { try { Claims claims = getClaimsFromToken(token); Date expiration = claims.getExpiration(); log.info("in isTokenExpired date:{}",expiration.toLocaleString()); Date date=new Date(); log.info("in isTokenExpired now:{},exp:{} ,{}",date.toLocaleString(),expiration.toLocaleString(),expiration.before(date)); return expiration.before(date); } catch (Exception e) { e.printStackTrace(); return true; } } /** * 得到token的过期时间,测试用,可删除 * @param token * @return */ public Date expirationDate(String token){ Claims claims = getClaimsFromToken(token); Date expiration = claims.getExpiration(); return expiration; } /** * 刷新令牌 * @param token 原令牌 * @return 新令牌 */ public String refreshToken(String token) { String refreshedToken; try { Claims claims = getClaimsFromToken(token); claims.put("created", new Date()); refreshedToken = generateToken(claims); } catch (Exception e) { e.printStackTrace(); refreshedToken = null; } return refreshedToken; } /** * 验证令牌 * * @param token 令牌 * @param userName 用户 * @return 是否有效 */ public Boolean validateToken(String token, String userName) { String uname = getUsernameFromToken(token); return (uname.equals(userName) && !isTokenExpired(token)); } /** * 从claims生成令牌 * @param claims 数据声明 * @return 令牌 */ private String generateToken(Map<String, Object> claims) { Date expirationDate = new Date(System.currentTimeMillis() + expire); /** * signWith() 签名方法。两个参数分别是签名算法和自定义的签名Key(盐)。 * 签名key可以byte[] 、String及Key的形式传入。 * 前两种形式均存入builder的keyBytes属性,后一种形式存入builder的key属性。 * 如果是第二种(及String类型)的key,则将其进行base64解码获得byte[] 。 * compact() 生成JWT */ return Jwts.builder() .setHeaderParam("typ", "JWT") .setClaims(claims) .setExpiration(expirationDate) .setIssuedAt((Date)claims.get("created")) .signWith(SignatureAlgorithm.HS512, secret) .compact(); } /** * 从令牌中获取数据声明 * * @param token 令牌 * @return 数据声明 */ private Claims getClaimsFromToken(String token) { Claims claims; try { claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); } catch (Exception e) { claims = null; } return claims; } }
-
编写拦截器TokenInterceptor类
@Slf4j @Component public class TokenInterceptor extends HandlerInterceptorAdapter { @Resource private JwtTokenUtil jwtTokenUtil; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws SignatureException { /** 地址过滤 */ String uri = request.getRequestURI() ; System.out.println(uri); if (uri.contains("/login")){ return true ; } /** Token 验证 */ String token = request.getHeader(jwtTokenUtil.getHeader()); if(StringUtils.isEmpty(token)){ token = request.getParameter(jwtTokenUtil.getHeader()); } if(StringUtils.isEmpty(token)){ throw new SignatureException(jwtTokenUtil.getHeader()+ "不能为空"); } if(jwtTokenUtil.isTokenExpired(token)) throw new SignatureException(jwtTokenUtil.getHeader() + "失效,请重新登录。"); /** 设置 identityId 用户身份ID */ // request.setAttribute("identityId", jwtTokenUtil.getUsernameFromToken(token)); return true; } }
-
注册拦截器
@Slf4j @Configuration public class WebConfig implements WebMvcConfigurer { @Resource private TokenInterceptor tokenInterceptor ; public void addInterceptors(InterceptorRegistry registry) { log.debug("注册TokenInterceptor"); registry.addInterceptor(tokenInterceptor).addPathPatterns("/**"); } }
-
编写控制器
@Slf4j @RestController public class IndexController { @Autowired private JwtTokenUtil jwtTokenUtil; @PostMapping("/login") public AjaxResponse login(@RequestBody User user){ log.info(user.toString()); String token=jwtTokenUtil.generateToken(user.getName(),user.getId()+""); return AjaxResponse.success(token); } @GetMapping("/user/{id}") public AjaxResponse findUser(@PathVariable("id") int id){ log.info("FindUser........................"); User user=new User(); user.setId(id); user.setName("TestName"); user.setPwd("testPwd"); return AjaxResponse.success(user); } }
-
PostMan测试与VUE测试
文件上传
整合Mybatis
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-292RcHSC-1638550633747)(SpringBoot.assets/image-20210429142405443.png)]
依赖
基本依赖、数据库依赖、数据源依赖
......
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<!-- 分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.13</version>
</dependency>
<!-- orable编码方面-->
<dependency>
<groupId>cn.easyproject</groupId>
<artifactId>orai18n</artifactId>
<version>12.1.0.2.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.21</version>
</dependency>
.yml配置
server:
port: 8088
servlet:
context-path: /oraclemybatis
#连接池配置
spring:
datasource:
url: jdbc:oracle:thin:@127.0.0.1:1521:orcl
username: scott
password: tiger
driver-class-name: oracle.jdbc.driver.OracleDriver
#mysql配置如下:
#url: jdbc:mysql://127.0.0.1:3308/mydb?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
#username: root
#password: root2020
#driver-class-name: com.mysql.cj.jdbc.Driver
# 使用druid数据源
#type: com.alibaba.druid.pool.DruidDataSource
# 使用druid数据源
type: com.alibaba.druid.pool.DruidDataSource
druid:
# 配置测试查询语句
validationQuery: SELECT 1 FROM DUAL
# 初始化大小,最小,最大
initialSize: 10
minIdle: 10
maxActive: 200
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 180000
testOnBorrow: false
testWhileIdle: true
removeAbandoned: true
removeAbandonedTimeout: 1800
logAbandoned: true
# 打开PSCache,并且指定每个连接上PSCache的大小
poolPreparedStatements: true
maxOpenPreparedStatements: 100
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall
mybatis:
# 映射文件配置
mapper-locations: classpath:mapper/*.xml
# 实体类的配置,主要改位置
type-aliases-package: com.trkj.mybatisboot.entity
pagehelper:
# 指明这是oracle配置
helperDialect: oracle
reasonable: true
supportMethodsArguments: true
params: count=countS
配置druid连接池、数据源监控
- 添加druid StatViewServlet用于展示Druid的统计信息
- Spring boot想用使用 其它Servlet或fiter组件的功能,就必须要借用 Spring Boot 提供的 ServletRegistrationBean 接口
- 配置Druid服务过滤器
- 访问地址:http://localhost:8088/druid/login.html
// config 目录下
package com.trkj.oraclemybatis.config;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration /*声明这是一个配置类*/
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource") /*读取前缀是这个的后续东西,构建出Datasource!如果没有这一步就生成的不是我们想要的东西*/
/*不写也可以用,但是不是自己想要的,*/
public DataSource dataSource() {
return DruidDataSourceBuilder.create().build();
}
/**
* 配置监控服务器
* @return 返回监控注册的servlet对象
* @author SimpleWu
*/
@Bean
public ServletRegistrationBean statViewServlet() {
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
// 添加IP白名单
servletRegistrationBean.addInitParameter("allow", "127.0.0.1");
// 添加IP黑名单,当白名单和黑名单重复时,黑名单优先级更高
//servletRegistrationBean.addInitParameter("deny", "127.0.0.1");
// 添加控制台管理用户
servletRegistrationBean.addInitParameter("loginUsername", "scott");
servletRegistrationBean.addInitParameter("loginPassword", "tiger");
// 是否能够重置数据
servletRegistrationBean.addInitParameter("resetEnable", "false");
return servletRegistrationBean;
}
/**
* 配置Druid服务过滤器
* @return 返回过滤器配置对象
*/
@Bean
public FilterRegistrationBean statFilter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
// 添加过滤规则
filterRegistrationBean.addUrlPatterns("/*");
// 忽略过滤格式
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*,");
return filterRegistrationBean;
}
}
创建相关page
-
dao
- DAO必须通过@Mapper才能被Spring创建
package com.trkj.oraclemybatis.dao; import com.trkj.oraclemybatis.entity.Dept; import org.apache.ibatis.annotations.Mapper; @Mapper public interface DeptDao { int deleteByPrimaryKey(Short deptno); int insert(Dept record); int insertSelective(Dept record); Dept selectByPrimaryKey(Short deptno); int updateByPrimaryKeySelective(Dept record); int updateByPrimaryKey(Dept record); }
-
entity
package com.trkj.oraclemybatis.entity; import java.io.Serializable; import lombok.Data; /** * DEPT * @author */ @Data public class Dept implements Serializable { private Short deptno; private String dname; private String loc; private static final long serialVersionUID = 1L; }
-
service
public interface DeptService { public Dept findDeptById(Short deptno); } @Service public class DeptServiceImpl implements DeptService { @Resource private DeptDao deptDao; @Override public Dept findDeptById(Short deptno) { return deptDao.selectByPrimaryKey(deptno); } }
-
controller
@RestController @Slf4j public class DeptController { @Autowired private DeptService deptService; @GetMapping("/dept/{id}") public Dept selectFindById(@PathVariable("id") Short deptno){ return deptService.findDeptById(deptno); } }
整合JPA
导入JPA Starter依赖库
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IwJwdbKW-1638550633747)(SpringBoot.assets/image-20210429162318432.png)]
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.7.RELEASE</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>cn.easyproject</groupId>
<artifactId>orai18n</artifactId>
<version>12.1.0.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.21</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
</dependencies>
.yml
server:
port: 8088
#连接池配置
servlet:
context-path: /jpaboot
spring:
datasource:
url: jdbc:oracle:thin:@127.0.0.1:1521:orcl
username: scott
password: tiger
driver-class-name: oracle.jdbc.driver.OracleDriver
#mysql配置如下:
#url: jdbc:mysql://127.0.0.1:3308/mydb?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
#username: root
#password: root2020
#driver-class-name: com.mysql.cj.jdbc.Driver
# 使用druid数据源
#type: com.alibaba.druid.pool.DruidDataSource
# 使用druid数据源
type: com.alibaba.druid.pool.DruidDataSource
druid:
# 配置测试查询语句
validationQuery: SELECT 1 FROM DUAL
# 初始化大小,最小,最大
initialSize: 10
minIdle: 10
maxActive: 200
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 180000
testOnBorrow: false
testWhileIdle: true
removeAbandoned: true
removeAbandonedTimeout: 1800
logAbandoned: true
# 打开PSCache,并且指定每个连接上PSCache的大小
poolPreparedStatements: true
maxOpenPreparedStatements: 100
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall
jpa:
database: ORACLE
database-platform: org.hibernate.dialect.OracleDialect
show-sql: true
format-sql: true
hibernate:
ddl-auto: update
如果是Mysql则
jpa:
database: mysql
database-platform: org.hibernate.dialect.MySQL5Dialect
show-sql: true
hibernate:
ddl-auto: update
配置druid连接池、数据源监控
定义配置类(见备注,与mybaits时一致)
添加druid StatViewServlet用于展示Druid的统计信息
Spring boot想用使用 其它Servlet或fiter组件的功能,就必须要借用 Spring Boot 提供的 ServletRegistrationBean 接口
配置Druid服务过滤器
访问地址:http://localhost:8088/druid/login.html
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DruidDataSourceBuilder.create().build();
}
/**
* 配置监控服务器
* @return 返回监控注册的servlet对象
* @author SimpleWu
*/
@Bean
public ServletRegistrationBean statViewServlet() {
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
// 添加IP白名单
servletRegistrationBean.addInitParameter("allow", "127.0.0.1");
// 添加IP黑名单,当白名单和黑名单重复时,黑名单优先级更高
//servletRegistrationBean.addInitParameter("deny", "127.0.0.1");
// 添加控制台管理用户
servletRegistrationBean.addInitParameter("loginUsername", "root");
servletRegistrationBean.addInitParameter("loginPassword", "root2020");
// 是否能够重置数据
servletRegistrationBean.addInitParameter("resetEnable", "false");
return servletRegistrationBean;
}
/**
* 配置Druid服务过滤器
* @return 返回过滤器配置对象
*/
@Bean
public FilterRegistrationBean statFilter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
// 添加过滤规则
filterRegistrationBean.addUrlPatterns("/*");
// 忽略过滤格式
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*,");
return filterRegistrationBean;
}
}
配置JPA
- 可单独配置jpa的配置类,无特殊要求时可直接使用yml中的配置,无需该配置类
- 注释该类的注解,程序依旧可执行
- 该类可用在多数据源配置
@Slf4j
@Configuration
@EnableTransactionManagement // 开启注解事务管理,等同于xml配置文件中的 <tx:annotation-driven />
@EnableJpaRepositories(
// 实体管理工厂引用名称,对应到@Bean注解对应的方法
entityManagerFactoryRef="entityManagerFactory",
//事务管理工厂引用名称,对应到@Bean注解对应的方法
transactionManagerRef="transactionManager",
basePackages= { "com.trkj.jpaboot.dao" }) //设置Repository所在位置
public class JpaDataConfig {
@Resource
@Qualifier("japDataSource")
private DataSource japDataSource; //primary数据源注入
@Value("${spring.jpa.database-platform}")
private String primaryDialect;
@Value("${spring.jpa.database}")
private String dataBase;
@Value("${spring.jpa.show-sql}")
private String showSql;
@Value("${spring.jpa.hibernate.ddl-auto}")
private String ddlAuto;
@Value("${spring.jpa.format-sql}")
private String formatSql;
@Bean(name = "entityManager") //primary实体管理器
public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
return entityManagerFactory(builder).getObject().createEntityManager();
}
@Bean(name = "entityManagerFactory") //primary实体工厂
public LocalContainerEntityManagerFactoryBean entityManagerFactory (EntityManagerFactoryBuilder builder) {
return builder
.dataSource(japDataSource)
.properties(getVendorProperties())
.packages("com.trkj.jpaboot.entity") //设置实体类所在位置
.persistenceUnit("primaryPersistenceUnit")
.build();
}
@Autowired
private HibernateProperties hibernateProperties;
@Autowired
private JpaProperties jpaProperties;
private Map getVendorProperties() {
jpaProperties.getProperties().put("hibernate.show_sql",showSql);
jpaProperties.getProperties().put("hibernate.format_sql",formatSql);
//jpaProperties.getProperties().put("hibernate.dialect","org.hibernate.dialect.Oracle10gDialect");
jpaProperties.getProperties().put("database-platform",primaryDialect);
jpaProperties.setDatabase(Database.valueOf(dataBase));
hibernateProperties.setDdlAuto(ddlAuto);
return hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(),new HibernateSettings());
}
@Bean(name = "transactionManager") //事务管理器
public PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactory(builder).getObject());
}
}
通过JPA工具生成POJO
@Data
@Validated
public class DeptVo {
@Max(value = 10000,message = "编号不能超过10000")
private long deptno;
@NotEmpty(message = "姓名不能为空")
private String dname;
private String loc;
}
@Entity
@Table(name = "DEPT", schema = "SCOTT", catalog = "")
public class DeptEntity {
private long deptno;
private String dname;
private String loc;
@Id
@Column(name = "DEPTNO")
public long getDeptno() {
return deptno;
}
public void setDeptno(long deptno) {
this.deptno = deptno;
}
@Basic
@Column(name = "DNAME")
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
@Basic
@Column(name = "LOC")
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DeptEntity that = (DeptEntity) o;
return deptno == that.deptno &&
Objects.equals(dname, that.dname) &&
Objects.equals(loc, that.loc);
}
@Override
public String toString() {
return "DeptEntity{" +
"deptno=" + deptno +
", dname='" + dname + '\'' +
", loc='" + loc + '\'' +
'}';
}
@Override
public int hashCode() {
return Objects.hash(deptno, dname, loc);
}
}
定义DAO
import com.trkj.jpaboot.entity.DeptEntity;
import org.springframework.data.repository.CrudRepository;
public interface DeptDao extends CrudRepository<DeptEntity,Integer> {
}
定义Service
public interface DeptService {
public DeptEntity addDeptEntity(DeptEntity dept);
public DeptEntity editDept(DeptEntity dept);
public DeptEntity findDept(DeptEntity dept);
}
@Service
@Slf4j
public class DeptServiceImpl implements DeptService {
@Autowired
private DeptDao deptDao;
@Transactional(transactionManager = "transactionManager")
public DeptEntity addDeptEntity(DeptEntity dept) {
log.info("addDeptEntity insert data..................");
log.info(dept.toString());
deptDao.save(dept);
log.info("addDeptEntity insert data over..................");
return dept;
}
@Override
@Transactional(transactionManager = "transactionManager")
public DeptEntity editDept(DeptEntity dept) {
return this.deptDao.save(dept);
}
@Override
public DeptEntity findDept(DeptEntity dept) {
long id=dept.getDeptno();
DeptEntity entity=this.deptDao.findById((int)id).orElse(null);
return entity ;
}
}
定义Controller
@RestController
@Slf4j
public class IndexController {
@Autowired
private DeptService deptService;
@PostMapping("/dept")
public AjaxResponse addDept(@RequestBody @Valid DeptVo detpVo){
log.info("开始插入数据。。。");
DeptEntity dept=new DeptEntity();
BeanUtils.copyProperties(detpVo,dept);
deptService.addDeptEntity(dept);
AjaxResponse response=AjaxResponse.success(dept);
return response;
}
@PutMapping("/dept")
public AjaxResponse editDept(@RequestBody @Valid DeptVo detpVo){
log.info("开始修改。。。");
DeptEntity dept=new DeptEntity();
BeanUtils.copyProperties(detpVo,dept);
deptService.editDept(dept);
AjaxResponse response=AjaxResponse.success(dept);
return response;
}
}
整合Mybatis与JPA
- 同时整合JPA与MyBatis
- 导入Maven依赖库
- 定义数据源类(同上例)
- 配置druid连接池(同上例)、MyBatis、jpa
- 定义jpa相关实体\DAO\配置文件
- 定义MyBatis相关实体\DAO\配置文件
- 定义Service业务类
- 定义Controller控制器
跨域配置类
@Configuration
@Slf4j
public class AppCorsConfiguration {
private CorsConfiguration buildConfig() {
log.debug("开始设置");
CorsConfiguration appCorsConfiguration = new CorsConfiguration();
appCorsConfiguration.addAllowedOrigin("http://localhost:8082");
appCorsConfiguration.addAllowedOrigin("http://127.0.0.1:8081");
appCorsConfiguration.addAllowedOrigin("http://localhost:8082");
appCorsConfiguration.addAllowedOrigin("http://127.0.0.1:8082");
appCorsConfiguration.addAllowedHeader("*"); // 2允许任何头
appCorsConfiguration.addAllowedMethod("OPTIONS");
appCorsConfiguration.addAllowedMethod("HEAD");
appCorsConfiguration.addAllowedMethod("GET");
appCorsConfiguration.addAllowedMethod("PUT");
appCorsConfiguration.addAllowedMethod("POST");
appCorsConfiguration.addAllowedMethod("DELETE");
appCorsConfiguration.addAllowedMethod("PATCH");
appCorsConfiguration.setAllowCredentials(true);//这两句不加不能跨域上传文件,
appCorsConfiguration.setMaxAge(3600L);//加上去就可
return appCorsConfiguration;
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig()); // 4
return new CorsFilter(source);
}
}
停用端口
1. > netstat -ano|findstr 8080
2. > >taskkill /pid 15184 /F
小技巧
序列化时改变名称
//在显示的json格式时,以name显示
@JsonProperty("name")
private String dname;
ss AppCorsConfiguration {
private CorsConfiguration buildConfig() {
log.debug(“开始设置”);
CorsConfiguration appCorsConfiguration = new CorsConfiguration();
appCorsConfiguration.addAllowedOrigin(“http://localhost:8082”);
appCorsConfiguration.addAllowedOrigin(“http://127.0.0.1:8081”);
appCorsConfiguration.addAllowedOrigin(“http://localhost:8082”);
appCorsConfiguration.addAllowedOrigin(“http://127.0.0.1:8082”);
appCorsConfiguration.addAllowedHeader("*"); // 2允许任何头
appCorsConfiguration.addAllowedMethod(“OPTIONS”);
appCorsConfiguration.addAllowedMethod(“HEAD”);
appCorsConfiguration.addAllowedMethod(“GET”);
appCorsConfiguration.addAllowedMethod(“PUT”);
appCorsConfiguration.addAllowedMethod(“POST”);
appCorsConfiguration.addAllowedMethod(“DELETE”);
appCorsConfiguration.addAllowedMethod(“PATCH”);
appCorsConfiguration.setAllowCredentials(true);//这两句不加不能跨域上传文件,
appCorsConfiguration.setMaxAge(3600L);//加上去就可
return appCorsConfiguration;
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig()); // 4
return new CorsFilter(source);
}
}
# 停用端口
```cmake
1. > netstat -ano|findstr 8080
2. > >taskkill /pid 15184 /F
小技巧
序列化时改变名称
//在显示的json格式时,以name显示
@JsonProperty("name")
private String dname;