文章目录
- 瑞吉外卖
- 1.技术栈
- 2.项目文件架构
- 3.业务功能模块(例子)
- 3.1管理员登录接口层(Controller)
- 3.2管理员登录实现层(ServiceImpl)
- 3.3管理员登录服务层(Service)
- 3.4管理员登录Mapper层
- 4.公共模块
- 4.1 BaseContext(保存获取登录用户id)
- 4.2CustomException
- 4.3GlobalExceptionHandler(全局异常)
- 4.4对象映射器
- 4.5公共字段填充
- 4.6封装返回结果类
- 5插件模块
- 5.1Redis序列化器
- 5.2Web MVC
- Riji涉及的基础知识
- DTO
- 启动类
- 注解开发
- @Slf4j
- @RestController
- 各个请求路径之间的区别(**http的get、post、put、delete**)
- @Autowired
- MP操作中的条件构造器
- MD5加密
- 传参的接收不同情况
- Spring引入的两个基本概念
- SpringBoot + MP中涉及的分页操作
- Session的基本用法和知识
- @Service & @Mapper
- @Transactional
- ThreadLocal基本概念和使用
- 自定义业务异常
- 全局异常
- 对象序列化
- MP中的公共填充
- 封装结果类
- 序列化和反序列化 以及 Redis的相关操作
- webMVC配置
瑞吉外卖
项目位置:https://gitee.com/xue-junbao/reji.git
1.技术栈
- 前端
HTML5+CSS+JS+node.js
- 后端
JavaSpring Boot + Mybaits Plus + Swagger
- 项目
**数据库:**MYSQL,Redis
**部署:**阿里云ECS服务器
2.项目文件架构
如图:
多看,基本项目就这几个层级
引入知识:
- DTO
- 启动类注解
3.业务功能模块(例子)
3.1管理员登录接口层(Controller)
package com.itheima.reggie.controller;
//依赖引入
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.reggie.common.R;
import com.itheima.reggie.entity.Employee;
import com.itheima.reggie.service.EmployeeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import static org.apache.commons.lang.StringUtils.isNotEmpty;
import static org.springframework.util.StringUtils.*;
@Slf4j
@RestController
@RequestMapping("/employee")
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
/**
* 员工登录
* @param request
* @param employee
* @return
*/
@PostMapping("/login")
public R<Employee> login(HttpServletRequest request, @RequestBody Employee employee) {
/**
* 1.对页面提交密码进行md5加密处理
* 2.根据用户名查询数据库
* 3.如果没有返回失败结果
* 4.比对密码
* 5.产看员工状态
* 6.登录成功,将员工id存入Session并返回登录成功结果
*/
//1
String password = employee.getPassword();
password = DigestUtils.md5DigestAsHex(password.getBytes());
//2
LambdaQueryWrapper<Employee> employeeLambdaQueryWrapper = new LambdaQueryWrapper<>();
employeeLambdaQueryWrapper.eq(Employee::getUsername,employee.getUsername());
Employee emp=employeeService.getOne(employeeLambdaQueryWrapper);
//3
if(emp == null){
return R.error("登录失败");
}
//4
if(!emp.getPassword().equals(password)){
return R.error("登录失败");
}
//5
if(emp.getStatus()==0){
return R.error("账号禁用!");
}
//6
request.getSession().setAttribute("employee",emp.getId());
return R.success(emp);
}
/**
* 员工退出
* @param request
* @return
*/
@PostMapping("/logout")
public R<String> logout(HttpServletRequest request){
//清理Session中保存的当前登录员工的id
request.getSession().removeAttribute("employee");
return R.success("退出成功");
}
/**
* 新增员工
* @param employee
* @return
*/
@PostMapping
public R<String> save(HttpServletRequest request,@RequestBody Employee employee){
log.info("新增员工,员工信息:{}",employee.toString());
//设置初始密码123456,md5加密处理
employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
// employee.setCreateTime(LocalDateTime.now());
// employee.setUpdateTime(LocalDateTime.now());
//
// //获得当前用户登录id
// Long empID=(Long)request.getSession().getAttribute("employee");
//
// employee.setCreateUser(empID);
// employee.setUpdateUser(empID);
employeeService.save(employee);
return R.success("新增员工成功");
}
/**
* 分页查询
*/
@GetMapping("/page")
public R<Page> page(int page,int pageSize,String name){
log.info("page={},pageSize={},name={}",page,pageSize,name);
//构造分页构造器
Page pageInfo=new Page(page,pageSize);
//构造条件构造器
LambdaQueryWrapper<Employee> queryWrapper=new LambdaQueryWrapper();
//添加过滤条件
queryWrapper.like(isNotEmpty(name),Employee::getName,name);
//添加排序条件
queryWrapper.orderByDesc(Employee::getUpdateTime);
//执行查询
employeeService.page(pageInfo,queryWrapper);
return R.success(pageInfo);
}
/**
* 根据id修改员工信息
* @param employee
* @return
*/
@PutMapping
public R<String> update(HttpServletRequest request, @RequestBody Employee employee){
log.info(employee.toString());
long id=Thread.currentThread().getId();
log.info("线程id为:{}",id);
employeeService.updateById(employee);
return R.success("员工信息修改成功!");
}
/**
* 根据id查询员工信息
* @param id
* @return
*/
@GetMapping("/{id}")
public R<Employee> getById(@PathVariable Long id){
log.info("根据id查询员工信息--");
Employee employee=employeeService.getById(id);
if(employee!=null)
return R.success(employee);
else
return R.error("没有查询到员工信息");
}
}
引入知识:
- 注解开发
- @Slf4j
- @RestController
- @Autowired
- 各个请求Mapper路径之间的区别
- MP操作中的条件构造器
- MD5加密
- 传参的接收不同情况
- Spring的两大基础概念
- SpringBoot + MP中涉及的分页操作
- Session的基本用法和知识
3.2管理员登录实现层(ServiceImpl)
package com.itheima.reggie.service.impl;
/**
* 实现类
*/
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.reggie.entity.Employee;
import com.itheima.reggie.mapper.EmployeeMapper;
import com.itheima.reggie.service.EmployeeService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements EmployeeService {
}
引入知识:
- @Service注解
- @Transactional
3.3管理员登录服务层(Service)
package com.itheima.reggie.service;
/**
* 接口
*/
import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.reggie.entity.Employee;
public interface EmployeeService extends IService<Employee> {
}
3.4管理员登录Mapper层
package com.itheima.reggie.mapper;
/**
*
*/
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.reggie.entity.Employee;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface EmployeeMapper extends BaseMapper<Employee> {
}
4.公共模块
4.1 BaseContext(保存获取登录用户id)
package com.itheima.reggie.common;
/**
* @Title: BaseContext
* @Author xjb
* @Package com.itheima.reggie.common
* @Date 2023/11/22 17:09
* @description: 基于ThreadLocal封装工具类,用户保存和获取当前登录用户id
*/
public class BaseContext {
private static ThreadLocal<Long> threadLocal =new ThreadLocal<>();
/**
* 设置值
* @param id
*/
public static void setCurrentId(Long id){
threadLocal.set(id);
}
/**
* 获取值
* @return
*/
public static Long getCurrentId(){
return threadLocal.get();
}
}
引入知识:
- ThreadLocal基本概念和使用
4.2CustomException
package com.itheima.reggie.common;
/**
* @Title: CustomException
* @Author xjb
* @Package com.itheima.reggie.common
* @Date 2023/11/30 16:16
* @description: 自定义业务异常
*/
public class CustomException extends RuntimeException{
public CustomException(String message){
super(message);
}
}
引入知识:
- 自定义业务异常
4.3GlobalExceptionHandler(全局异常)
package com.itheima.reggie.common;
import com.sun.org.glassfish.external.statistics.annotations.Reset;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.sql.SQLIntegrityConstraintViolationException;
/**
* 全局异常捕获
*/
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {
/**
* 异常处理方法
* @param ex
* @return
*/
@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
log.error(ex.getMessage());
if(ex.getMessage().contains("Duplicate entry")){
String[] split = ex.getMessage().split(" ");
String msg=split[2]+"已存在";
return R.error(msg);
}
return R.error("未知错误");
}
/**
* 异常处理方法(删除分类)
* @param ex
* @return
*/
@ExceptionHandler(CustomException.class)
public R<String> exceptionHandler(CustomException ex){
log.error(ex.getMessage());
return R.error(ex.getMessage());
}
}
引入知识:
- 全局异常的声明和定义
4.4对象映射器
package com.itheima.reggie.common;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
/**
* 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
* 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
* 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
*/
public class JacksonObjectMapper extends ObjectMapper {
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
public JacksonObjectMapper() {
super();
//收到未知属性时不报异常
this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
//反序列化时,属性不存在的兼容处理
this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
SimpleModule simpleModule = new SimpleModule()
.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
.addSerializer(BigInteger.class, ToStringSerializer.instance)
.addSerializer(Long.class, ToStringSerializer.instance)
.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
//注册功能模块 例如,可以添加自定义序列化器和反序列化器
this.registerModule(simpleModule);
}
}
引入知识:
- 对象序列化和反序列化
4.5公共字段填充
package com.itheima.reggie.common;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* @Title: MyMetaObjecthandler
* @Author xjb
* @Package com.itheima.reggie.common
* @Date 2023/11/22 16:07
* @description: 公共字段填充
*/
@Slf4j
@Component
public class MyMetaObjecthandler implements MetaObjectHandler {//原数据对象处理器
/**
* 插入自动填充
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject){
log.info("公共字段自动填充[insert]...");
log.info(metaObject.toString());
metaObject.setValue("createTime", LocalDateTime.now());
metaObject.setValue("updateTime", LocalDateTime.now());
metaObject.setValue("createUser", BaseContext.getCurrentId());
metaObject.setValue("updateUser", BaseContext.getCurrentId());
}
/**
* 更新自动填充
* @param metaObject
*/
@Override
public void updateFill(MetaObject metaObject){
log.info("公共字段自动填充[update]...");
log.info(metaObject.toString());
long id=Thread.currentThread().getId();
log.info("线程id为:{}",id);
metaObject.setValue("createTime", LocalDateTime.now());
metaObject.setValue("updateTime", LocalDateTime.now());
metaObject.setValue("updateUser", BaseContext.getCurrentId());
}
}
引入知识:
- MP中的公共填充
4.6封装返回结果类
package com.itheima.reggie.common;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
/**
* 通用返回结果类,服务器响应的数据最终都会封装成为此对象
* @param <T>
*/
@Data
public class R<T> {
private Integer code; //编码:1成功,0和其它数字为失败
private String msg; //错误信息
private T data; //数据
private Map map = new HashMap(); //动态数据
public static <T> R<T> success(T object) {
R<T> r = new R<T>();
r.data = object;
r.code = 1;
return r;
}
public static <T> R<T> error(String msg) {
R r = new R();
r.msg = msg;
r.code = 0;
return r;
}
public R<T> add(String key, Object value) {
this.map.put(key, value);
return this;
}
}
引入知识:
- 封装结果类
5插件模块
5.1Redis序列化器
package com.itheima.reggie.config;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
//默认的Key序列化器为:JdkSerializationRedisSerializer
redisTemplate.setKeySerializer(new StringRedisSerializer()); // key序列化
//redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); // value序列化
redisTemplate.setConnectionFactory(connectionFactory);
return redisTemplate;
}
}
引入知识:
- 序列化和反序列化的意义
- Redis的序列化和反序列化
5.2Web MVC
package com.itheima.reggie.config;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import com.itheima.reggie.common.JacksonObjectMapper;
import com.itheima.reggie.entity.Employee;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
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;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
@Slf4j
@Configuration
@EnableSwagger2
@EnableKnife4j
public class WebMvcConfig extends WebMvcConfigurationSupport {
/**
* 设置静态资源映射
* @param registry
*/
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
log.info("开始进行静态资源映射...");
registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");
registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
}
/**
* 扩展mvc框架的消息转换器
* @param converters
*/
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
log.info("扩展消息转换器...");
//创建消息转换器对象
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
//设置对象转换器,底层使用Jackson将Java对象转为json
messageConverter.setObjectMapper(new JacksonObjectMapper());
//将上面的消息转换器对象追加到mvc框架的转换器集合中
converters.add(0,messageConverter);
}
//扫描controller,生成接口文档
@Bean
public Docket createRestApi() {
// 文档类型
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.itheima.reggie.controller"))
.paths(PathSelectors.any())
.build();
}
//描述接口文档
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("瑞吉外卖")
.version("1.0")
.description("瑞吉外卖接口文档")
.build();
}
}
引入知识:
- webMVC配置
Riji涉及的基础知识
DTO
要理解DTO和VO这两个概念就行
- DTO可以理解为前端传入的数据过多,一个表塞不下,那么可以对这个表进行扩展然后做为DTO来传输数据
- VO则是反着的,后端返回前端数据多要整合成一个VO对象返回
DTO与VO-CSDN博客
启动类
知道注解意思就行
使用Spring Boot构建RESTful服务:项目启动类(瑞吉外卖)-CSDN博客
注解开发
- 什么是注解?
Annotion
(注解)是一个接口,程序可以通过反射来获取指定程序元素的Annotion
对象,然后通过Annotion
对象来获取注解里面的元数据。我们常常使用的注解,
@Data、@Controller
等等,这些都是注解,创建一个注解,也很简单,创建一个类,然后将class
改为@interface
就是一个注解啦。
官方解释:
Java
注解(Annotation
)用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。Java
注解是从 Java5 开始添加到 Java 的。–官方文档
- 关于注解的处理
我们一般将利用反射来处理注解的方式称之为运行时注解
。
另外一种则是编译时注解,如我们常常使用的 lombok 里的注解,@Data
,它能够帮我们省略set/get
方法,我们在Class
上加上这个注解后,在编译的时候,lombok
其实是修改了.class
文件的,将set/get
方法放进去了,不然的话,你可以看看编译完后的.class
文件。诸如这种,我们常称为编译时注解
,也就是使用javac
处理注解。
- 注解和反射
**反射定义:**反射(
Reflection
),Java 中的反射机制是指,Java 程序在运行期间可以获取到一个对象的全部信息。反射机制一般用来解决Java 程序运行期间,对某个实例对象一无所知的情况下,如何调用该对象内部的方法问题。
二者之间的关系:
- 互补:注解和反射不是相互替代的,而是可以一起使用的互补机制。注解通常用于添加元数据,而反射则用于在运行时处理这些元数据。
- 用途:注解通常用于标记代码中的特定部分,以便在编译时或运行时进行处理。反射则用于在运行时动态地检查和操作类、方法和字段。
- 结合使用:有时,你可以使用注解来存储有关类、方法和字段的信息,然后使用反射来读取和处理这些信息。例如,某些框架(如Spring)使用注解来配置组件,然后在运行时使用反射来识别和初始化这些组件。
**简单理解:**注解可以将一部分配置类操作封装到一个注解接口中,其中可以提供配置类也可以提供一些规则
Java注解详解和自定义注解实战,用代码讲解 - 掘金 (juejin.cn)
@Slf4j
这是一个日志注解,只需要知道怎么用就行
- 项目引用:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId:
<version>1.7.28</version>
</dependency>
- 使用
Slf4j日志级别,级别由低到高,设置的级别越低,打印的日志越多
①trace: 一般不会使用,在日志里边也不会打印出来,最低的一个日志级别。
②debug: 一般放于程序的某个关键点的地方,用于打印一个变量值或者一个方法返回的信息之类的信息。
③info: 一般处理业务逻辑的时候使用,就跟 system.err打印一样,用于说明此处是干什么的。
④warn:警告,不会影响程序的运行,但是值得注意。
⑤error: 用户程序报错,必须解决的时候使用此级别打印日志。
@RestController
- @RestController是一个组合注解,它包含了@Controller和@ResponseBody两个注解的功能。
用@RestController标记的类表示这是一个RESTful风格的控制器,它可以处理HTTP请求并返回JSON格式的响应。 @RestController注解在处理请求时,会自动将方法的返回值转换为JSON格式的响应体,并返回给客户端。
因此,使用@RestController可以省去在每个方法上都加@ResponseBody注解的麻烦。
@RestController也支持@RequestMapping注解,用于映射请求。
例如,可以在@RestController中定义一个处理GET请求的方法,并使用@RequestMapping注解指定请求的URL和请求方法,如下所示:
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
// 根据id查询用户信息
User user = userService.getUserById(id);
return user;
}
}
上述代码中,@GetMapping注解表示该方法处理GET请求,{@code /{id}}表示URL中的参数,@PathVariable注解用于获取参数值。方法的返回值会自动转换为JSON格式的响应体,返回给客户端。 需要注意的是,使用@RestController时需要确保Spring的Jackson或Gson库已经正确配置,否则无法将Java对象转换为JSON格式的响应
- 关于RESTful风格
RESTful风格是一种基于HTTP协议设计Web API的软件架构风格,由Roy Fielding在2000年提出。它强调使用HTTP动词来表示对资源的操作(GET、POST、PUT、PATCH、DELETE等),并通过URI表示资源的唯一标识符。
需要知道:
- 在实际应用中,RESTful风格的Web服务通常使用JSON或XML格式进行数据交换。相比于其他Web服务交互方案(如SOAP),RESTful风格更加轻量级和简单明了,因此得到了广泛的应用和支持.
@RestController详解 - 知乎 (zhihu.com)
各个请求路径之间的区别(http的get、post、put、delete)
只需要记住 post用于向服务器提交数据,get拉取数据,put更新数据,delete删除数据就可以了
Spring Boot中的RESTful API:@GetMapping, @PostMapping, @PutMapping, 和 @DeleteMapping详解-CSDN博客
@Autowired
@Autowired
注解能够自动装配 Spring 容器中的 bean,使得开发者无需手动通过new
关键字或者通过getBean()
方法来获取依赖对象。
简单说就是自动创建和配置对应type的对象,有点像工厂模式。
深入解析 Spring 的 @Autowired:自动装配的魔法与细节-CSDN博客
MP操作中的条件构造器
关于项目中的操作我们可以将步骤分为:
- 构造条件构造器
- 确定条件并且构造条件
- 封装条件构造器并查询
- 封装结果执行下一步操作
员工登录为例
//1构造条件选择器
LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
//2构造条件并且封装
queryWrapper.eq(Employee::getUsername,employee.getUsername());
//3开始查询数据,获取返回结果
Employee emp = employeeService.getOne(queryWrapper);
//4比对数据,看是否可以登录
if(emp == null){
return R.error("登录失败");
}
if(!emp.getPassword().equals(password)){
return R.error("登录失败");
}
if(emp.getStatus()==0){
return R.error("账号禁用!");
}
request.getSession().setAttribute("employee",emp.getId());
基础操作看链接:
MyBatis-Plus 基础:LambdaQueryWrapper详解与实例-CSDN博客
MD5加密
只需要学会直接调用Spring提供的DigestUtils.md5DigestAsHex
方法完成md5加密操作就可以了。
此项目管理员登录中:
//管理员登录
String password = employdee.getPassword();
password = DigestUtils.md5DigestAsHex(password.getBytes());//登录获取的密码通过加密和sql中的数据进行比对
//增加用户
//设置初始密码123456,md5加密处理
employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
Java中的MD5加密详解-CSDN博客
传参的接收不同情况
搞清楚各个注解处理的数据类型:
重点有
@RequestBody
处理的是JSON和XML类型的数据,@PathVariable
处理的是URL上绑定的参数,无注解时会自己寻找一个匹配的参数(要求名字对应)
Spring MVC注解详解与实战:从请求处理到数据绑定-CSDN博客
Spring引入的两个基本概念
简单了解一下就好了。
-
IOC控制反转,就是将类写成bean类让获取的时候无需关注创建和配置的过程
-
AOP切面编程,就是对程序进行横向切分,添加额外的行为或功能。AOP常用于实现日志记录、事务管理、安全检查等横切关注点,项目中涉及的公共字段填充就是用的AOP。可以理解为一个配置一次全局自动触发的工具。
控制反转(IoC)与面向切面编程(AOP)-CSDN博客
SpringBoot + MP中涉及的分页操作
项目中的使用:
- 构造分页器
- 构造查询条件
- 执行分页查询
例如:
//构造分页器
Page pageInfo = new Page(page,pageSize);
//构造条件构造器
LambdaQueryWrapper<Employee> wrapper = new LambdaQueryWrapper();
//添加过滤条件
wrapper.like(isNotEmpty(name),Employee::getName,name);//第一个参数是执行的条件
//添加排序条件
wrapper.orderByDesc(Employee::getUpdateTime);//降序排列
//执行查询
employeeService.page(pageInfo,wrapper);
return pageInfo;
SpringBoot + MyBatis-Plus 实现分页操作详解-CSDN博客
Session的基本用法和知识
项目中只需要会最基本的如何获取session,设置以及应用场景。
本项目中,例如:
request.getSession().setAttribute("employee",emp.getId());
就是设置一个session,存入了当前用户的权限信息和id
不要忘了传入的参数为public R<Employee> login(HttpServletRequest request, @RequestBody Employee employee)
里面应该有HttpServletRequest request
获取和删除session为:request.getSession().getAttribute("employee")
和request.getSession().removeAttribute("employee");
Java中的Session详解及示例代码-CSDN博客
@Service & @Mapper
简单理解,这个注解使得
当类上标注了
@Service
注解后,Spring容器会自动扫描并创建该类的一个实例(即Bean),这样我们就可以在其他地方通过自动装配(Autowired)的方式注入这个Bean。
Spring Boot实战:深入理解@Service与@Mapper注解-CSDN博客
@Transactional
这个注解可以保证在操作多个数据库表单时候,确保事物的一致性,也就是说,只有所有的操作都成功才会成功,否则就会回滚让所有操作都失败。
ThreadLocal基本概念和使用
简答说就是保存一个数据,方便同一线程内的其它地方调用数据,降低复杂程度
ThreadLocal和简单应用-CSDN博客
自定义业务异常
学会如何使用可以让项目更加规范!!!
Java中的自定义异常处理:业务异常类的创建与使用-CSDN博客
全局异常
统一管理全局异常抛出
深入理解Java Spring中的全局异常处理:以Reggie项目为例-CSDN博客
对象序列化
了解其原理和应用,这种代码都是写死的,不用记。
JacksonObjectMapper自定义配置详解-CSDN博客
MP中的公共填充
学明白,后续会有Mabatis的操作,同时应用AOP。
MyBatis-Plus中的公共字段自动填充策略-CSDN博客
封装结果类
Java通用返回结果类的设计与应用-CSDN博客
序列化和反序列化 以及 Redis的相关操作
- 序列化和反序列化详解-CSDN博客
- 自定义Redis配置以优化你的Spring应用-CSDN博客
webMVC配置
项目最基础的配置,需要指定资源路径和API文档
Spring Boot中WebMvcConfig配置详解及示例-CSDN博客