文章目录
一SpringBoot返回Json数据及数据封装
项目开发中,接口和接口之间,前后端之间数据传输使用Json格式,
在SpringBoot中的Controller使用@RestController
注解即可返回Json格式的数据,
点击进入@RestController
注解
可以看出@RestController
注解包含了原来的@Controller
和@ResponseBody
注解
@ResponseBody
将返回的数据结构转换为json格式
打开idea右侧的maven栏
发现在此版本中SpringBoot的默认json解析框架是jackson
1.1Jackson对null的处理
比如期望所有的null在转json时变成"""这样的空字符串,
在SpringBoot中做以下配置即可
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import java.io.IOException;
/**
* 使用阿里巴巴的fastJson时,把jackson的配置注释掉
*/
@Configuration
public class JacksonConfig {
@Bean
@Primary
@ConditionalOnMissingBean(ObjectMapper.class)
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString("");
}
});
return objectMapper;
}
}
发现null成功替换成了""
1.2fastJson对null的处理
继承WebMVCConfigurationSupport
,覆盖其configureMessageConverters
方法
配置如下:
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class fastJsonConfig extends WebMvcConfigurationSupport {
/**
* 使用阿里 FastJson 作为JSON MessageConverter
* @param converters
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
FastJsonConfig config = new FastJsonConfig();
config.setSerializerFeatures(
// 保留map空的字段
SerializerFeature.WriteMapNullValue,
// 将String类型的null转成""
SerializerFeature.WriteNullStringAsEmpty,
// 将Number类型的null转成0
SerializerFeature.WriteNullNumberAsZero,
// 将List类型的null转成[]
SerializerFeature.WriteNullListAsEmpty,
// 将Boolean类型的null转成false
SerializerFeature.WriteNullBooleanAsFalse,
// 避免循环引用
SerializerFeature.DisableCircularReferenceDetect);
converter.setFastJsonConfig(config);
converter.setDefaultCharset(Charset.forName("UTF-8"));
List<MediaType> mediaTypeList = new ArrayList<>();
// 解决中文乱码问题,相当于在Controller上的@RequestMapping中加了个属性produces = "application/json"
mediaTypeList.add(MediaType.APPLICATION_JSON);
converter.setSupportedMediaTypes(mediaTypeList);
converters.add(converter);
//Long转json精度丢失的配置
MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new
MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
objectMapper.registerModule(simpleModule);
jackson2HttpMessageConverter.setObjectMapper(objectMapper);
converters.add(jackson2HttpMessageConverter);
}
}
1.3封装统一返回的数据结构
在实际项目中,除了要封装数据之外,需要在返回的json中添加例如状态码code,一些msg给调用者
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.HashMap;
import java.util.Map;
@Data
@Accessors(chain = true)//开启链式操作,R.ok().setData(map);
public class R {
private Integer code;
private String message;
private Map<String,Object> data = new HashMap<>();
public static R ok(){
R r = new R();
r.setCode(0);
r.setMessage("成功");
return r;
}
public static R error(){
R r = new R();
r.setCode(-1);
r.setMessage("失败");
return r;
}
public R data(String key,Object value){
this.data.put(key,value);
return this;
}
}
二SpringBoot使用是slf4j进行日志记录
在application.yml
中写入如下
logging:
config: course03使用slf4j进行日志记录/logback.xml # 指定项目启动时,读取哪个配置文件, 这里指的是 根目录下的logback.xml
level:
com.itcodai.course03.dao: trace # 此包下的日志级别为 trace
根目录下写入slf4j的配置
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 格式化输出:%date表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符-->
<property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />
<!-- 定义日志存储的路径,不要配置相对路径 -->
<property name="FILE_PATH" value="D:/logs/course03/demo.%d{yyyy-MM-dd}.%i.log" />
<!-- 控制台输出日志 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 按照上面配置的LOG_PATTERN来打印日志 -->
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>
<!--每天生成一个日志文件,保存15天的日志文件。rollingFile是用来切分文件的 -->
<appender name="FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${FILE_PATH}</fileNamePattern>
<!-- keep 15 days' worth of history -->
<maxHistory>15</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- 日志文件的最大大小 -->
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- project default level -->
<!-- 常用的日志级别按照从高到低依次为:ERROR、WARN、INFO、DEBUG。 -->
<logger name="com.itcodai.course03" level="DEBUG" />
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
</configuration>
心态泵了我写到第7课了中午睡觉,下午没了
三SpringBoot项目属性配置
3.1读取配置信息
@value(value="")
获取指定配置文件信息
@ConfigurationProperties(prefix="")
获取以指定前置开头的配置信息
使用@ConfigurationProperties(prefix="")
1.写application.yml配置
# 配置多个微服务的地址
url:
# 订单微服务的地址
orderUrl: http://localhost:8002
# 用户微服务的地址
userUrl: http://localhost:8003
# 购物车微服务的地址
shoppingUrl: http://localhost:8004
2.定义接收实体类
@Component
@ConfigurationProperties(prefix = "url")
public class MicroServiceUrl {
private String orderUrl;
private String userUrl;
private String shoppingUrl;
}
3.引入maven坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
4.编写controller读取并使用配置信息
@RestController
@RequestMapping("/test")
public class ConfigController {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigController.class);
@Value("${url.orderUrl}")
private String orderUrl;
@Resource
private MicroServiceUrl microServiceUrl;
@RequestMapping("/config")
public String testConfig() {
LOGGER.info("=====获取的订单服务地址为:{}", orderUrl);
// 使用配置类来获取
LOGGER.info("=====获取的订单服务地址为:{}", microServiceUrl.getOrderUrl());
LOGGER.info("=====获取的用户服务地址为:{}", microServiceUrl.getUserUrl());
LOGGER.info("=====获取的购物车服务地址为:{}", microServiceUrl.getShoppingUrl());
return "success";
}
}
5.控制台打印结果
3.2指定配置文件
例如我们有两个配置文件 application-dev.yml
和 application-pro.yml
对应开发环境和生产环境
在application.yml
中指定读取哪个配置文件的信息
四SpringBoot中的MVC支持
4.1@RestController
@RestController
可以看做是@Controller
和@ResponseBody
的结合体
注意:
前后端分离时,可以用@RestController
返回json数据给前端
不是前后端分离,由于@RestControlle
r将user当做字符串返回所以不可以使用,
只能使用@Controller
来跳转页面
4.2@RequestMapping
处理请求地址的注解,可以用在类上或方法上
常用属性
- value:指定请求的实际地址
- method:指定请求的类型,主要有 GET、PUT、POST、DELETE,默认为 GET
- produces:指定返回内容类型,如 produces = “application/json; charset=UTF-8”
4.3@PathVariable
获取url参数,当url中的参数和方法接收的参数不一样时需要通过value属性来指定对应关系
4.4@RequestParam
获取url参数
@RequestParam
获取http://localhost:8080/user?id=1中的参数
@PathVariable
获取ttp://localhost:8080/user/{id} 中的参数
- value指定url参数和方法接收参数的对应关系
- required为true表示该参数比传,否则抛出404
- defaultValue默认值,表示如果请求中没有同名参数时的默认值
4.5@RequestBody
接收前端传递的实体对象
五SpringBoot集成swagger
六SpringBoot全局异常处理
6.1定义返回统一json结构
@Data
//开启链式操作
@Accessors(chain = true)
public class R {
private Integer code;
private String message;
private Map<String,Object> data = new HashMap<>();
public static R ok(){
R r = new R();
r.setCode(0);
r.setMessage("成功");
return r;
}
public static R error(){
R r = new R();
r.setCode(-1);
r.setMessage("失败");
return r;
}
public static R come(){
R r = new R();
r.setCode(-1);
r.setMessage("失败");
return r;
}
public R data(String key,Object value){
this.data.put(key,value);
return this;
}
}
6.2处理系统异常
新建一个GlobalExceptionHandler全局异常处理类
加上@ControllerAdvice
注解即可拦截项目中抛出的异常
@ControllerAdvice
注解包含了@Component
注解
说明SpringBoot启动时,该类会交给Spring管理
- basePackages属性指定拦截哪个包下的异常信息,默认
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
}
在方法上加@ExceptionHandler
指定具体的异常
在方法中处理该异常,
通过统一的json结构体返回给调用者
/**
* 空指针异常
* @param ex NullPointerException
* @return
*/
@ExceptionHandler(NullPointerException.class)
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public JsonResult handleTypeMismatchException(NullPointerException ex) {
logger.error("空指针异常,{}", ex.getMessage());
return new JsonResult("500", "空指针异常了");
}
七SpringBoot中的AOP处理
面向切面编程
低耦合,易扩展,避免重复
1.引入坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.实现AOP切面
@Aspect
声明一个类为切面类,作用于类上
@Aspect
@Component
public class LogAspectHandler {
}
3.@Pointcut
@Pointcut
定义一个切面
- execution属性 ,拦截某包下的方法
@Aspect
@Component
public class LogAspectHandler {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 定义一个切面,拦截com.itcodai.course09.controller包下的所有方法
*/
@Pointcut("execution(* com.itcodai.course09.controller..*.*(..))")
public void pointCut() {}
- @annontation属性,针对某个注解定义切面
//针对@GetMapping方法做切面
@Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
public void annotationCut() {}
4.其它注解
- @Before 在做某件事之前做的事。
- @After:在做某件事之后做的事。
- @AfterReturning:在做某件事之后,对其返回值做增强处理。
- @AfterThrowing:在做某件事抛出异常时,处理。
八SpringBoot集成MyBatis
1.引入坐标
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
2.properties.yml配置
# 服务端口号
server:
port: 8080
# 数据库地址
datasource:
url: localhost:3306/blog_test
spring:
datasource: # 数据库配置
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://${datasource.url}?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&autoReconnect=true&failOverReadOnly=false&maxReconnects=10
username: root
password: 123456
hikari: # SpringBoot的默认数据库连接池
maximum-pool-size: 10 # 最大连接池数
max-lifetime: 1770000
mybatis:
# 指定别名设置的包为所有entity
type-aliases-package: com.itcodai.course10.entity
configuration:
map-underscore-to-camel-case: true # 驼峰命名规范
mapper-locations: # mapper映射文件位置
- classpath:mapper/*.xml
3.注解
@Mapper
放在Mapper类上
@MapperScan
放在启动类上,value属性指定扫描的包位置
@Param
指定参数
@Result
指定实体类属性和数据库表的对应关系
@Select("select * from user where id = #{id}")
@Results({
@Result(property = "username", column = "user_name"),
@Result(property = "password", column = "password")
})
User getUser(Long id);
九SpringBoot事务配置管理
1.导入坐标
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
2.编写mapper
public interface UserMapper {
@Insert("insert into user (user_name, password) values (#{username}, #{password})")
Integer insertUser(User user);
}
3.编写Server
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;
@Override
@Transactional
public void isertUser(User user) {
// 插入用户信息
userMapper.insertUser(user);
// 手动抛出异常
throw new RuntimeException();
}
}
4.编写controller
@RestController
public class TestController {
@Resource
private UserService userService;
/**
* 正常测试
* @param user
* @return
*/
@PostMapping("/adduser")
public String addUser(@RequestBody User user) throws Exception {
if (null != user) {
userService.isertUser(user);
return "success";
} else {
return "false";
}
}
}
5.注解
@Transactional
开启事务管理
注意
SpringBoot默认事务规则是遇到 运行时异常和错误才会回滚
针对非运行时异常,需要属性rollbackFor
@Transactional(rollbackFor = Execption.class)
当我们使用try…catch时,事务是不会回滚的,尽量上抛异常
在使用synchronized是,需要保证锁的范围和事务的范围大,即
在调用该Server的地方加锁,保证锁的范围大于事务的范围
九SpringBoot监听器
web监听器是一种Servlet的特殊类
用来监听特定事件
十SpringBoot拦截器
十一SpringBoot集成Redis
1.引入坐标
<dependencies>
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--阿里巴巴fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.35</version>
</dependency>
2.redis配置
server:
port: 8080
# 数据库地址
datasource:
url: localhost:3306/blog_test
spring:
datasource: # 数据库配置
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://${datasource.url}?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&autoReconnect=true&failOverReadOnly=false&maxReconnects=10
username: root
password: 123456
hikari:
maximum-pool-size: 10 # 最大连接池数
max-lifetime: 1770000
mybatis:
# 指定别名设置的包为所有entity
type-aliases-package: com.itcodai.course16.entity
configuration:
map-underscore-to-camel-case: true # 驼峰命名规范
mapper-locations: # mapper映射文件位置
- classpath:mapper/*.xml
SpringBoot对redis有两个模板:RedisTemplate和StringRedisTemplate
RedisTemplate
RedisTemplate<K,V>
模板类在操作redis时默认使用JdkSerializationRedisSerializer
序列化
所以在存储时会出现乱码
点击进入乱码的原因
所以需要修改默认的序列化方式
private RedisTemplate redisTemplate;
@Autowired(required = false)
public void setRedisTemplate(RedisTemplate redisTemplate) {
RedisSerializer stringSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringSerializer);
redisTemplate.setValueSerializer(stringSerializer);
redisTemplate.setHashKeySerializer(stringSerializer);
redisTemplate.setHashValueSerializer(stringSerializer);
this.redisTemplate = redisTemplate;
}
StringRedisTemplate
StringRedisTemplate
不会出现中文乱码,直接注入使用即可
十二SpringBoot集成MQ
十三SpringBoot集成Shiro
Shiro是解决 认证,授权,加密,会话管理,与Web集成,缓存
图为: Shiro的组件图
13.1Shiro的工作流程
一个Shiro应用的工作流程:
- 在Subject的login(token)方法里传入代表用户身份和凭证的AuthenticationToken实例
- 而Subject又委托给SecurityManager;
-
-
- 我们给Shiro的安全管理器注入域,从而让安全管理器得到合法的用户及其权限来判断
-
Shiro不提供维护用户/权限,而是需要开发人员注入 到域中
图为: Shiro 的工作流程
13.2权限认证
权限认证的核心三要素
权限
角色
用户
图为:权限用户角色的关系图