springboot自定义配置MySQL_SpringBoot+MyBatis搭建SSM基本骨架(Redis + Swagger+自定义配置 +mysql )...

一些技巧

SSM架构

a88878ae1b41

image.png

表现层主要有controller作为外部接口访问和业务层service接口的连接;业务层处于中间层次,进行接口访问后的后续逻辑处理,并通过service调用持久层mybatis的mapper,而持久层的mapper具体的SQL操作在xml文件之中实现,最后操作mysql数据库。

a88878ae1b41

image.png

SSM程序执行流程

a88878ae1b41

yuque_diagram.jpg

业务逻辑:

Controller-->service接口-->serviceImpl-->mapper(dao)接口-->mapperImpl(daoImpl)-->mapper-->db

其中impl为模板代码,通过注解简化,在编译的时候自动生成注解实现类。

基本架构

application.yml 指定服务端口

server:

port: 5088

compression:

enabled: true

pom.xml基本配置

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

org.springframework.boot

spring-boot-starter-parent

2.3.5.RELEASE

com.springboot

demo

0.0.1-SNAPSHOT

demo

Demo project for Spring Boot

1.8

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-test

test

org.junit.vintage

junit-vintage-engine

org.springframework.boot

spring-boot-configuration-processor

true

org.springframework.boot

spring-boot-maven-plugin

一、Controller 外部层

controller层即控制层。

controller层的功能为请求和响应控制。

controller层负责前后端交互,接受前端请求,调用service层,接收service层返回的数据,最后返回具体的页面和数据到客户端。

RestController注解

RequestMapping注解

@RequestMapping可以作用在类上,也可以作用在方法上。

@GetMapping其实就是@RequestMapping和Get的集合:

@GetMapping(value = “hello”) 等价于@RequestMapping(value = “hello”, method = RequestMethod.GET)

RequestParam注解

@RequestParam:将请求参数绑定到你控制器的方法参数上(是springmvc中接收普通参数的注解)

语法:@RequestParam(value=”参数名”,required=”true/false”,defaultValue=””)

value:参数名

required:是否包含该参数,默认为true,表示该请求路径中必须包含该参数,如果不包含就报错。

defaultValue:默认参数值,如果设置了该值,required=true将失效,自动为false,如果没有传该参数,就使用默认值

例子代码:

@RestController

@RequestMapping(value = "/user")

public class UserController {

@Autowired

private UserService mUserService;

@RequestMapping(value = "/registration",method = RequestMethod.POST)

public Object registration(@RequestParam(value = "username") String username, @RequestParam(value = "password") String password, @RequestParam(value = "memberLevelId",defaultValue = "4") int memberLevelId, @RequestParam(value = "phone") String phone ){

System.out.printf("username:"+username+",password:"+password +",memberLevelID:"+memberLevelId+",phone:"+phone);

mUserService.addUser(username,password,memberLevelId,phone, DateUtil.currentDate());

return "registration success! ";

}

}

二、Service逻辑层

service层即业务逻辑层。

service层的作用为完成功能设计。

service层调用dao层接口,接收dao层返回的数据(封装为对象Model层),完成项目的基本功能设计。

存放业务逻辑处理,也是一些关于数据库处理的操作,但不是直接和数据库打交道,他有接口还有接口的实现方法,在接口的实现方法中需要导入mapper层,mapper层是直接跟数据库打交道的,他也是个接口,只有方法名字,具体实现在mapper.xml文件里,service是供我们使用的方法。

Repository注解

例子代码:

@Repository

public class UserService {

@Autowired

private UserMapper mUserMapper;

public void addUser(String username,String password,int memberLevelId,String phone,String createTime){

mUserMapper.addUser(username, password, memberLevelId, phone, createTime);

}

}

Autowired注解

SpringBoot @Autowired解析

autowired有4种模式,byName、byType、constructor、autodectect

@Autowired在何处使用

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})

CONSTRUCTOR:构造

METHOD:方法

PARAMETER:参数

FIELD:字段

ANNOTATION_TYPE:注解

@Autowired参数

Autowired注解,只有一个required元素,默认是true,也是就是说这个值能改为false。true和false的意义不同。

require=ture 时,表示解析被标记的字段或方法,一定有对应的bean存在。

require=false 时,表示解析被标记的字段或方法,没有对应的bean存在不会报错。

public @interface Autowired {

/**

* Declares whether the annotated dependency is required.

*

Defaults to {@code true}.

*/

boolean required() default true;

}

三、Mapper层(Dao层)

不管是什么框架,我们很多时候都会与数据库进行交互。如果遇到一个场景我们都要去写SQL语句,那么我们的代码就会很冗余。所以,我们就想到了把数据库封装一下,让我们的数据库的交道看起来像和一个对象打交道,这个对象通常就是DAO。当我们操作这个对象的时候,这个对象会自动产生SQL语句来和数据库进行交互,我们就只需要使用DAO就行了。

我们经常说的DAO = Data Access Object = 数据存取对象。通常网络上我们说的Dao不仅包含了数据库的Model数据结构还包含了对应的数据操作方法,但是有一些却把它当成纯粹的model,所以更倾向于称为Mapper层。

通常我们在Mapper层里面写接口(Mapper层包括了数据对象既model),里面有与数据打交道的方法。SQL语句通常写在mapper文件里面的。

优点:结构清晰,Dao层的数据源配置以及相关的有关数据库连接的参数都在Spring配置文件中进行配置

总结:

mapper层即数据持久层,也被称为dao层。

mapper层的作用为访问数据库,向数据库发送sql语句,完成数据的增删改查任务。

现在用mybatis逆向工程生成的mapper层。对数据库进行数据持久化操作,他的方法语句是直接针对数据库操作的,而service层是针对我们controller,也就是针对我们使用者。service的impl是把mapper和service进行整合的文件。

集成MySQL

MySQL配置

pom.xml添加

org.springframework.boot

spring-boot-starter-data-jpa

mysql

mysql-connector-java

runtime

在application.yml中添加:

spring:

#配置数据源

datasource:

url: jdbc:mysql://localhost:3306/mytest?characterEncoding=utf8&useSSL=true&serverTimezone=UTC

username: root #数据库用户名

password: root #数据库密码

mysql相关工具使用

集成MyBatis

mybatis配置

在application.yml中添加:

mybatis:

mapper-locations: classpath:mybatis/*.xml # 注意:一定要对应mapper映射xml文件的所在路径

type-aliases-package: com.springboot.demo.entity # 注意:对应实体类的路径

configuration:

map-underscore-to-camel-case: true # 数据库字段下划线自动转驼峰

在pom.xml依赖中添加

org.mybatis.spring.boot

mybatis-spring-boot-starter

2.1.3

DemoApplication添加MapperScan注解

@MapperScan("com.springboot.demo")

@SpringBootApplication

public class DemoApplication {

public static void main(String[] args) {

SpringApplication.run(DemoApplication.class, args);

}

}

UserMapper 接口添加数据库操作外部方法

@Repository

public interface UserMapper {

/**

* @param username 用户名

* @param password 密码

* @param memberLevelId 会员级别

* @param phone 手机号码

*/

void addUser(String username,String password,int memberLevelId,String phone,String createTime);

}

Mapper创建xml,实现底层SQL的执行操作

/p>

"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

INSERT INTO t_user ( user_name, pwd, imooc_id, order_id, create_time )

VALUES

( #{userName}, #{password}, #{imoocId}, #{orderId}, #{createTime} )

ON DUPLICATE KEY UPDATE

pwd=VALUES(pwd)

,user_name=VALUES(user_name);

四、Model层

model层即数据库实体层,也被称为entity层,pojo层。存放我们的实体类,与数据库中的属性值基本保持一致。比如service层返回controller层的数据,封装为对象model,更加符合面向对象编程的习惯。

乱码错误

之前建立项目是utf8,但是不知道怎么操作的application.yml有一次变成乱码,于是用GBK重载,注释的中文恢复了,但是执行代码发现报错

Caused by: org.yaml.snakeyaml.error.YAMLException: java.nio.charset.MalformedInputException: Input l

其实应该是文件的格式问题造成的解析错误,改变改文件的解析格式为utf8,或者新建一个application.yml文件覆盖,里面乱码的中文,重新输入即可。

对于PostMan的使用教程,可以查看postman使用教程

a88878ae1b41

image.png

报错,500, "error": "Internal Server Error",,查看服务端log,create_time字段出问题。

a88878ae1b41

image.png

UserService的addUser 调用UserMapper

a88878ae1b41

image.png

而UserMapper在UserMapper.xml使用了create_time对应不上UserMapper.java的createTime报错,将UserMapper.xml的create_time改为createTime即可

a88878ae1b41

image.png

重新请求postman

a88878ae1b41

image.png

更改请求时的密码,发现并没有新注册一个账户,而是直接update数据库,证明我们的mapper的SQL语句是正确的

自定义配置

@Configuration底层是含有@Component ,所以@Configuration 具有和 @Component 的作用。

@Configuration可理解为用spring的时候xml里面的标签。

@Configuration注解可以达到在Spring中使用xml配置文件的作用。

@Bean可理解为用spring的时候xml里面的标签

@Configuration注解

@Bean注解

BCrypt实现用户信息加密

密码的存储,传输使用明文是比较不安全的做法,所以我们通过集成BCrypt实现用户信息加密。

pom.xml添加配置

org.springframework.boot

spring-boot-starter-security

添加SecurityConfig

@Configuration

public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Override

protected void configure(HttpSecurity http) throws Exception {

http.authorizeRequests()

//定义所有要控制的路径

.antMatchers("/**")

//允许所有人访问上述路径

.permitAll()

.and().csrf().disable();

super.configure(http);

}

@Bean

public BCryptPasswordEncoder bCryptPasswordEncoder(){

return new BCryptPasswordEncoder();

}

}

a88878ae1b41

image.png

自定义配置类入口WebSecurityConfigurerAdapter

HttpSecurity 使用了builder 的构建方式来灵活制定访问策略。最早基于 XML 标签对 HttpSecurity 进行配置。现在大部分使用 javaConfig方式。常用的方法解读如下:

方法

说明

openidLogin()

用于基于 OpenId 的验证

headers()

将安全标头添加到响应,比如说简单的 XSS 保护

cors()

配置跨域资源共享( CORS )

sessionManagement()

允许配置会话管理

portMapper()

允许配置一个PortMapper(HttpSecurity#(getSharedObject(class))),其他提供SecurityConfigurer的对象使用 PortMapper 从 HTTP 重定向到 HTTPS 或者从 HTTPS 重定向到 HTTP。默认情况下,Spring Security使用一个PortMapperImpl映射 HTTP 端口8080到 HTTPS 端口8443,HTTP 端口80到 HTTPS 端口443

jee()

配置基于容器的预认证。 在这种情况下,认证由Servlet容器管理

x509()

配置基于x509的认证

rememberMe

允许配置“记住我”的验证

authorizeRequests()

允许基于使用HttpServletRequest限制访问

requestCache()

允许配置请求缓存

exceptionHandling()

允许配置错误处理

securityContext()

在HttpServletRequests之间的SecurityContextHolder上设置SecurityContext的管理。 当使用WebSecurityConfigurerAdapter时,这将自动应用

servletApi()

将HttpServletRequest方法与在其上找到的值集成到SecurityContext中。 当使用WebSecurityConfigurerAdapter时,这将自动应用

csrf()

添加 CSRF 支持,使用WebSecurityConfigurerAdapter时,默认启用

logout()

添加退出登录支持。当使用WebSecurityConfigurerAdapter时,这将自动应用。默认情况是,访问URL”/ logout”,使HTTP Session无效来清除用户,清除已配置的任何#rememberMe()身份验证,清除SecurityContextHolder,然后重定向到”/login?success”

anonymous()

允许配置匿名用户的表示方法。 当与WebSecurityConfigurerAdapter结合使用时,这将自动应用。 默认情况下,匿名用户将使用org.springframework.security.authentication.AnonymousAuthenticationToken表示,并包含角色 “ROLE_ANONYMOUS”

formLogin()

指定支持基于表单的身份验证。如果未指定FormLoginConfigurer#loginPage(String),则将生成默认登录页面

oauth2Login()

根据外部OAuth 2.0或OpenID Connect 1.0提供程序配置身份验证

requiresChannel()

配置通道安全。为了使该配置有用,必须提供至少一个到所需信道的映射

httpBasic()

配置 Http Basic 验证

addFilterBefore()

在指定的Filter类之前添加过滤器

addFilterAt()

在指定的Filter类的位置添加过滤器

addFilterAfter()

在指定的Filter类的之后添加过滤器

and()

连接以上策略的连接器,用来组合安全策略。实际上就是"而且"的意思

我们的项目

authorizeRequests():允许基于使用HttpServletRequest限制访问

antMatchers("/**"):定义所有要控制的路径

/* 是拦截所有的文件夹,不包含子文件夹

/** 是拦截所有的文件夹及里面的子文件夹

也能够对不同路径设置不同的配置

.antMatchers("/resources/**", "/signup", "/about").permitAll()

.antMatchers("/admin/**").hasRole("ADMIN")

.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")

antMatchers("/**").permitAll() 表示项目根目录以及子目录全部都 permitAll 允许所有人访问上述路径

在项目中添加了Security模块后,在发送post请求时会失败,出现以下日志:

Invalid CSRF token found for ..

在Security的默认拦截器里,默认会开启CSRF处理,判断请求是否携带了token,如果没有就拒绝访问。并且,在请求为(GET|HEAD|TRACE|OPTIONS)时,则不会开启,可参考如下源码:

// 先从tokenRepository中加载token

CsrfToken csrfToken = tokenRepository.loadToken(request);

final boolean missingToken = csrfToken == null;

// 如果为空,则tokenRepository生成新的token,并保存到tokenRepository中

if(missingToken) {

CsrfToken generatedToken = tokenRepository.generateToken(request);

// 默认的SaveOnAccessCsrfToken方法,记录tokenRepository,

// tokenRepository,response,获取token时先将token同步保存到tokenRepository中

csrfToken = new SaveOnAccessCsrfToken(tokenRepository, request, response, generatedToken);

}

// 将token写入request的attribute中,方便页面上使用

request.setAttribute(CsrfToken.class.getName(), csrfToken);

request.setAttribute(csrfToken.getParameterName(), csrfToken);

// 如果不需要csrf验证的请求,则直接下传请求(requireCsrfProtectionMatcher是默认的对象,对符合^(GET|HEAD|TRACE|OPTIONS)$的请求不验证)

if(!requireCsrfProtectionMatcher.matches(request)) {

filterChain.doFilter(request, response);

return;

}

// 从用户请求中获取token信息

String actualToken = request.getHeader(csrfToken.getHeaderName());

if(actualToken == null) {

actualToken = request.getParameter(csrfToken.getParameterName());

}

// 验证,如果相同,则下传请求,如果不同,则抛出异常

if(!csrfToken.getToken().equals(actualToken)) {

if(logger.isDebugEnabled()) {

logger.debug("Invalid CSRF token found for " + UrlUtils.buildFullRequestUrl(request));

}

if(missingToken) {

accessDeniedHandler.handle(request, response, new MissingCsrfTokenException(actualToken));

} else {

accessDeniedHandler.handle(request, response, new InvalidCsrfTokenException(csrfToken, actualToken));

}

return;

}

filterChain.doFilter(request, response);

所以 csrf().disable()关掉csrf

整合Swagger文档

io.springfox

springfox-swagger2

2.9.2

io.springfox

springfox-swagger-ui

2.9.2

application

@MapperScan("com.springboot.demo")

@SpringBootApplication

@EnableSwagger2

public class DemoApplication {

public static void main(String[] args) {

SpringApplication.run(DemoApplication.class, args);

}

}

添加配置类

添加一个swagger 配置类,在工程下新建 config 包并添加一个 SwaggerConfig 配置类。

@Configuration

@EnableSwagger2

public class SwaggerConfig {

public static final String VERSION ="1.0.1";

public static final String AUTHOR="spring boot demo";

public Docket createRestApi(){

return new Docket(DocumentationType.SWAGGER_2)

.apiInfo(apiInfo())

.select()

.apis(RequestHandlerSelectors.basePackage("com\\springboot\\demo\\controller"))

.paths(PathSelectors.any())

.build()

.ignoredParameterTypes(ApiIgnore.class);

}

private ApiInfo apiInfo() {

return new ApiInfoBuilder()

.title("API 文档")

.description("SpringBoot demo API 文档")

.version(VERSION)

.contact(new Contact(AUTHOR,"https://www.baidu.com","XXXX@gmail.com"))

.build();

}

}

Swagger 注解

@Api:用在请求的类上,表示对类的说明

tags="说明该类的作用,可以在UI界面上看到的注解"

value="该参数没什么意义,在UI界面上也看到,所以不需要配置"

@ApiOperation:用在请求的方法上,说明方法的用途、作用

value="说明方法的用途、作用"

notes="方法的备注说明"

@ApiImplicitParams:用在请求的方法上,表示一组参数说明

@ApiImplicitParam:用在@ApiImplicitParams注解中,指定一个请求参数的各个方面

name:参数名

value:参数的汉字说明、解释

required:参数是否必须传

paramType:参数放在哪个地方

· header --> 请求参数的获取:@RequestHeader

· query --> 请求参数的获取:@RequestParam

· path(用于restful接口)--> 请求参数的获取:@PathVariable

· body(不常用)

· form(不常用)

dataType:参数类型,默认String,其它值dataType="Integer"

defaultValue:参数的默认值

@ApiResponses:用在请求的方法上,表示一组响应

@ApiResponse:用在@ApiResponses中,一般用于表达一个错误的响应信息

code:数字,例如400

message:信息,例如"请求参数没填好"

response:抛出异常的类

@ApiModel:用于响应类上,表示一个返回响应数据的信息

(这种一般用在post创建的时候,使用@RequestBody这样的场景,

请求参数无法使用@ApiImplicitParam注解进行描述的时候)

@ApiModelProperty:用在属性上,描述响应类的属性

示例

@Api:用在请求的类上,说明该类的作用

@RestController

@RequestMapping(value = "/user")

@Api(tags = {"Member"})

public class UserController {

@ApiOperation(value = "登录")

@ApiParam("登录账号")

@ApiOperation(value = "注册")

@RequestMapping(value = "/registration",method = RequestMethod.POST)

public ResponseEntity registration(@RequestParam(value = "username") @ApiParam("用户名") String username,

@RequestParam(value = "password") @ApiParam("密码") String password,

@RequestParam(value = "memberLevelId",defaultValue = "4") @ApiParam("会员等级") int memberLevelId,

@RequestParam(value = "phone") @ApiParam("手机号码") String phone ){

// System.out.printf("username:"+username+",password:"+password +",memberLevelID:"+memberLevelId+",phone:"+phone);

mUserService.addUser(username, bCryptPasswordEncoder.encode(password),memberLevelId,phone, DateUtil.currentDate());

return ResponseEntity.successMessage("registration success! ");

}

@ApiOperation(value="用户注册",notes="手机号、密码都是必输项,年龄随边填,但必须是数字")

@ApiImplicitParams:用在请求的方法上,包含一组参数说明

@ApiImplicitParams({

@ApiImplicitParam(name="mobile",value="手机号",required=true,paramType="form"),

@ApiImplicitParam(name="password",value="密码",required=true,paramType="form"),

@ApiImplicitParam(name="age",value="年龄",required=true,paramType="form",dataType="Integer")

})

@ApiResponses:用于请求的方法上,表示一组响应

@ApiOperation(value = "select1请求",notes = "多个参数,多种的查询参数类型")

@ApiResponses({

@ApiResponse(code=400,message="请求参数没填好"),

@ApiResponse(code=404,message="请求路径没有或页面跳转路径不对")

})

@ApiModel:用于响应类上,表示一个返回响应数据的信息

import io.swagger.annotations.ApiModel;

import io.swagger.annotations.ApiModelProperty;

import java.io.Serializable;

@ApiModel(description= "返回响应数据")

public class RestMessage implements Serializable{

@ApiModelProperty(value = "是否成功")

private boolean success=true;

@ApiModelProperty(value = "返回对象")

private Object data;

@ApiModelProperty(value = "错误编号")

private Integer errCode;

@ApiModelProperty(value = "错误信息")

private String message;

/* getter/setter */

}

访问swagger接口文档界面

a88878ae1b41

image.png

测试接口

展开member的/user/login登录接口,点击Try it out,就可以输入参数并点击执行。

a88878ae1b41

image.png

输入参数并点击执行

a88878ae1b41

image.png

看到返回信息,提示密码错误,程序正常

a88878ae1b41

image.png

整合 Redis

pom.xml 集成服务

org.springframework.boot

spring-boot-starter-data-redis

Redis 工具类

public class UserRedisUtil {

public static String BOARDING_PASS = "boarding-pass";

/**

* 将用户信息保存到redis

* @param redisTemplate

* @param session

* @param userEntity

*/

public static void addUser(StringRedisTemplate redisTemplate, HttpSession session, UserEntity userEntity){

//用户session写入redis

redisTemplate.opsForValue().set(getKey(session),JsonUtil.toJsonString(userEntity));

}

/**

* 将用户信息从redis中移除

* @param redisTemplate

* @param session

*/

public static void removeUser(StringRedisTemplate redisTemplate,HttpSession session){

session.invalidate();

//将用户从redis中移除

redisTemplate.delete(getKey(session));

}

public static UserEntity getUser(StringRedisTemplate redisTemplate, HttpServletRequest request){

//检测redis是否含有session

UserEntity userEntity = null;

String userJson = redisTemplate.opsForValue().get(getBoardingPass(request));

if(userJson != null){

userEntity = JsonUtil.fromJson(userJson,UserEntity.class);

}

return userEntity;

}

/**

* 获取redis存储的key

* @param session

* @return

*/

public static String getKey(HttpSession session){

return session.getId();

}

public static String getBoardingPass(HttpServletRequest request){

String pass =request.getHeader(BOARDING_PASS);

return pass != null ?pass : "no-pass";

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值