当前位置: 首页 > news >正文

【苍穹外卖】总结

1 pom 依赖

1.1 MyBatis Spring

        用于简化 MyBatis 与 Spring Boot 的集成,提供了对 MyBatis 框架的自动配置支持,简化了数据访问层的开发

       

1.2 Lombok

        Lombok 是一个 Java 库,能够通过注解自动生成常见的代码(如 gettersettertoString 等),减少了代码冗余,提升了开发效率

1. @Data

  • 作用: 这是一个综合性注解,包含了 @Getter@Setter@ToString@EqualsAndHashCode@RequiredArgsConstructor,也就是说,@Data 会为所有字段自动生成 gettersetter,同时生成 toString()equals()hashCode() 方法,并且为 final 字段生成构造函数。

2. @Builder

  • 作用: 生成建造者模式(Builder Pattern)代码,可以通过流式调用来构建对象。它允许更灵活、清晰地创建对象,尤其适合包含多个属性的复杂对象创建。

3. @NoArgsConstructor

  • 作用: 生成无参数的构造函数。适用于需要创建空对象的情况,比如从数据库反序列化时,或者某些框架(如 JPA)需要一个无参构造函数。

4. @AllArgsConstructor

  • 作用: 生成包含所有字段的全参数构造函数。适用于在创建对象时直接通过构造函数初始化所有字段。

1.3 FastJSON

        FastJSON 是阿里巴巴开源的一个高性能 JSON 序列化/反序列化库,主要用于将 Java 对象转换为 JSON 字符串或将 JSON 字符串转换为 Java 对象

1.4 Commons Lang

        提供了一些常用的 Java 工具类,扩展了 Java 核心库中的 java.lang 包,提供了字符串操作、数字处理等实用功能

 

1.5 Druid

        Druid 是阿里巴巴开源的高效数据库连接池,集成了数据库监控和性能优化功能。druid-spring-boot-starter 提供了与 Spring Boot 的自动集成

1.6 PageHelper

        PageHelper 是一个 MyBatis 分页插件,简化了分页操作,支持多种数据库并能自动分页

1.7 Knife4j

        Knife4j 是基于 Swagger 的增强 UI,提供了更丰富的文档功能,便于 API 文档的生成与查看

1.8 AspectJ

        AspectJ 是一个面向切面编程(AOP)框架,aspectjrt 是其运行时库,用于在运行时处理切面相关的逻辑

        AspectJjweaver 是用于支持 AspectJ 切面的织入器(weaver),允许在编译期、类加载期或运行期插入切面代码

1.9 JSON Web Token (JJWT)

        JJWT 是用于生成和验证 JSON Web Tokens (JWT) 的库,主要用于处理身份验证和安全

1.10 Aliyun OSS

        阿里云对象存储(OSS)SDK,用于与阿里云 OSS 进行集成,实现文件的上传、下载和管理

1.11 JAXB API

        JAXB(Java Architecture for XML Binding)是用于将 Java 对象转换为 XML 以及将 XML 转换为 Java 对象的 API

2 项目结构

common:相当于 util,放置自定义的方法

pojo:Plain Old Java Object,放置对象

server:服务端代码

2.1 common

2.1.1 constant

常量类

public class JwtClaimsConstant {public static final String EMP_ID = "empId";public static final String USER_ID = "userId";public static final String PHONE = "phone";public static final String USERNAME = "username";public static final String NAME = "name";}

2.1.2 context

上下文

public class BaseContext {public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();public static void setCurrentId(Long id) {threadLocal.set(id);}public static Long getCurrentId() {return threadLocal.get();}public static void removeCurrentId() {threadLocal.remove();}}

        管理当前线程中的上下文信息,特别是与线程相关的 Long 类型的 id。通过使用 ThreadLocal,它允许每个线程独立存储和访问自己的 id,从而确保在多线程环境中不会出现线程间数据冲突的问题

        这样可以 在任何时候获取用户 ID

2.1.3 exception

异常类

/*** 登录失败*/
public class LoginFailedException extends BaseException {public LoginFailedException(String msg) {super(msg);}
}

2.1.4 json

将 Java 对象与 JSON 之间进行序列化和反序列化

通过这个类,可以将 Java 对象转换为 JSON,或者将 JSON 转换为 Java 对象

/*** 对象映射器:基于 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_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm";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(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);}
}

2.1.5 properties

属性类

@Component
@ConfigurationProperties(prefix = "sky.jwt") // 配置属性类
@Data
public class JwtProperties {/*** 管理端员工生成jwt令牌相关配置*/private String adminSecretKey;private long adminTtl;private String adminTokenName;/*** 用户端微信用户生成jwt令牌相关配置*/private String userSecretKey;private long userTtl;private String userTokenName;}

        这里有个 配置属性类 的注解 @ConfigurationProperties(prefix = "sky.jwt"),将配置文件 application.yml 中的配置属性 映射 到 Java 类,前缀 prefix 指定了配置属性:

# application.ymlsky:jwt:# 设置jwt签名加密时使用的秘钥admin-secret-key: itcast# 设置jwt过期时间admin-ttl: 72000000000# 设置前端传递过来的令牌名称admin-token-name: token# 设置jwt签名加密时使用的秘钥user-secret-key: userKey# 设置jwt过期时间user-ttl: 72000000000# 设置前端传递过来的令牌名称 与 前端 一致,不可更改user-token-name: authentication

2.1.6 result

结果类

/*** 封装分页查询结果*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PageResult implements Serializable {private long total; //总记录数private List records; //当前页数据集合}
/*** 后端统一返回结果** @param <T>*/
@Data
public class Result<T> implements Serializable {private Integer code; //编码:1成功,0和其它数字为失败private String msg; //错误信息private T data; //数据public static <T> Result<T> success() {Result<T> result = new Result<T>();result.code = 1;return result;}public static <T> Result<T> success(T object) {Result<T> result = new Result<T>();result.data = object;result.code = 1;return result;}public static <T> Result<T> error(String msg) {Result result = new Result();result.msg = msg;result.code = 0;return result;}}

2.1.7 utils

实用类,包括AliOss对象文件上传,jwt令牌

@Data
@AllArgsConstructor
@Slf4j
public class AliOssUtil {private String endpoint;private String accessKeyId;private String accessKeySecret;private String bucketName;/*** 文件上传** @param bytes* @param objectName* @return*/public String upload(byte[] bytes, String objectName) {// 创建OSSClient实例。OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);try {// 创建PutObject请求。ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(bytes));} catch (OSSException oe) {System.out.println("Caught an OSSException, which means your request made it to OSS, "+ "but was rejected with an error response for some reason.");System.out.println("Error Message:" + oe.getErrorMessage());System.out.println("Error Code:" + oe.getErrorCode());System.out.println("Request ID:" + oe.getRequestId());System.out.println("Host ID:" + oe.getHostId());} catch (ClientException ce) {System.out.println("Caught an ClientException, which means the client encountered "+ "a serious internal problem while trying to communicate with OSS, "+ "such as not being able to access the network.");System.out.println("Error Message:" + ce.getMessage());} finally {if (ossClient != null) {ossClient.shutdown();}}//文件访问路径规则 https://BucketName.Endpoint/ObjectNameStringBuilder stringBuilder = new StringBuilder("https://");stringBuilder.append(bucketName).append(".").append(endpoint).append("/").append(objectName);log.info("文件上传到:{}", stringBuilder.toString());return stringBuilder.toString();}
}
public class JwtUtil {/*** 生成jwt* 使用Hs256算法, 私匙使用固定秘钥** @param secretKey jwt秘钥* @param ttlMillis jwt过期时间(毫秒)* @param claims    设置的信息* @return*/public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {// 指定签名的时候使用的签名算法,也就是header那部分SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;// 生成JWT的时间long expMillis = System.currentTimeMillis() + ttlMillis;Date exp = new Date(expMillis);// 设置jwt的bodyJwtBuilder builder = Jwts.builder()// 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的.setClaims(claims)// 设置签名使用的签名算法和签名使用的秘钥.signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))// 设置过期时间.setExpiration(exp);return builder.compact();}/*** Token解密** @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个* @param token     加密后的token* @return*/public static Claims parseJWT(String secretKey, String token) {// 得到DefaultJwtParserClaims claims = Jwts.parser()// 设置签名的秘钥.setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))// 设置需要解析的jwt.parseClaimsJws(token).getBody();return claims;}}

2.2 pojo

分为

entity(实体对象)

vo(View Object,视图对象)

dto(Data Transfer Object,数据传输对象)

2.2.1 entity

  • 以 Entity 结尾
  • 数据对象名 与 数据库表名 一致
  • 字段 与 数据库字段 一致

2.2.2 vo

用于 展示层,作用是把 某个指定页面(或组件)的所有数据封装

  • 不可继承自 Entity
  • vo 可以继承、组合其他 DTO、VO、BO 等对象
  • vo 只能用于返回前端

2.2.3 dto

用于从数据库中检索数据

2.3 server

除了 controller、service、mapper 三个层,还有其他一些类

2.3.1 config

配置类

@Configuration
@Slf4j
public class RedisConfiguration {@Beanpublic RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {log.info("开始创建redis模板类");
//        RedisTemplate redisTemplate = new RedisTemplate();
//        redisTemplate.setConnectionFactory(redisConnectionFactory);
//redisTemplate.setKeySerializer(new StringRedisSerializer());
//
//        return redisTemplate;RedisTemplate redisTemplate = new RedisTemplate<>();redisTemplate.setConnectionFactory(redisConnectionFactory);// 设置Key的序列化器为StringredisTemplate.setKeySerializer(new StringRedisSerializer());// 设置Value的序列化器为StringredisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
//        // 设置Hash Key的序列化器为String
//        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//
//        // 设置Hash Value的序列化器为String
//        redisTemplate.setHashValueSerializer(new StringRedisSerializer());return redisTemplate;}
}

 redis 数据库

web 层配置

/*** 配置类,注册web层相关组件*/
@Configuration
@Slf4j
public class WebMvcConfiguration extends WebMvcConfigurationSupport {@Autowiredprivate JwtTokenAdminInterceptor jwtTokenAdminInterceptor;@Autowiredprivate JwtTokenUserInterceptor jwtTokenUserInterceptor;/*** 注册自定义拦截器** @param registry*/protected void addInterceptors(InterceptorRegistry registry) {log.info("开始注册自定义拦截器...");registry.addInterceptor(jwtTokenAdminInterceptor).addPathPatterns("/admin/**") // 添加拦截路径.excludePathPatterns("/admin/employee/login"); // 排除拦截路径(登录不需要拦截)registry.addInterceptor(jwtTokenUserInterceptor).addPathPatterns("/user/**").excludePathPatterns("/user/user/login").excludePathPatterns("/user/shop/status");}/*** 通过 knife4j 生成接口文档** @return*/@Beanpublic Docket docket1() {ApiInfo apiInfo = new ApiInfoBuilder().title("苍穹外卖项目接口文档").version("2.0").description("苍穹外卖项目接口文档").build();Docket docket = new Docket(DocumentationType.SWAGGER_2).groupName("管理端接口").apiInfo(apiInfo).select().apis(RequestHandlerSelectors.basePackage("com.sky.controller.admin")).paths(PathSelectors.any()).build();return docket;}@Beanpublic Docket docket2() {ApiInfo apiInfo = new ApiInfoBuilder().title("苍穹外卖项目接口文档").version("2.0").description("苍穹外卖项目接口文档").build();Docket docket = new Docket(DocumentationType.SWAGGER_2).groupName("用户端接口").apiInfo(apiInfo).select().apis(RequestHandlerSelectors.basePackage("com.sky.controller.user")).paths(PathSelectors.any()).build();return docket;}/*** 设置静态资源映射** @param registry*/protected void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");}/*** 扩展 Spring MVC 的消息转换器** @param converters*/@Overrideprotected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {// 创建一个 消息转换器 对象MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();// 为 消息转换器 设置一个 对象转换器,对象转换器可以将 Java 对象序列化为 Json 数据converter.setObjectMapper(new JacksonObjectMapper());// 将自己的 消息转换器 加入到 容器 中,放置在第一位converters.add(0, converter);}
}

2.3.2 handler

        @RestControllerAdvice 可以捕获并处理控制器层抛出的异常,从而统一管理异常处理逻辑,发生异常时,sping自动调用 handler 方法

/*** 全局异常处理器,处理项目中抛出的业务异常*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {/*** 捕获业务异常** @param ex* @return*/@ExceptionHandlerpublic Result exceptionHandler(BaseException ex) {log.error("异常信息:{}", ex.getMessage());return Result.error(ex.getMessage());}/*** 处理 SQL 异常** @param ex* @return*/@ExceptionHandlerpublic Result exceptionHandler(SQLIntegrityConstraintViolationException ex) {// Duplicate entry 'ada' for key 'employee.idx_username'if (ex.getMessage().contains("Duplicate entry")) {String[] s = ex.getMessage().split(" ");String username = s[2]; // 拿到用户名String msg = username + MessageConstant.ALREADY_EXIST;return Result.error(msg);} else {return Result.error(MessageConstant.UNKNOWN_ERROR);}}
}

2.3.3 interceptor

拦截器

/*** jwt令牌校验的拦截器*/
@Component
@Slf4j
public class JwtTokenAdminInterceptor implements HandlerInterceptor {@Autowiredprivate JwtProperties jwtProperties;/*** 校验jwt** @param request* @param response* @param handler* @return* @throws Exception*/public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//判断当前拦截到的是 Controller 的方法还是其他资源if (!(handler instanceof HandlerMethod)) {//当前拦截到的不是动态方法,直接放行return true;}//1、从请求头中获取令牌String token = request.getHeader(jwtProperties.getAdminTokenName());//2、校验令牌try {log.info("jwt 校验:{}", token);Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());log.info("当前员工id:", empId);BaseContext.setCurrentId(empId);//3、通过,放行return true;} catch (Exception ex) {//4、不通过,响应 401 状态码response.setStatus(401);return false;}}
}

2.3.3 启动类

@SpringBootApplication
@EnableTransactionManagement //开启注解方式的事务管理
@Slf4j
@EnableCaching // 开启缓存注解
@EnableScheduling // 开启任务调度
public class SkyApplication {public static void main(String[] args) {SpringApplication.run(SkyApplication.class, args);log.info("server started");}
}

2.3.4 配置

服务器端口号

server:port: 8080

dev:开发环境

spring:profiles:active: devmain:allow-circular-references: truedatasource:druid:driver-class-name: ${sky.datasource.driver-class-name}url: jdbc:mysql://${sky.datasource.host}:${sky.datasource.port}/${sky.datasource.database}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=trueusername: ${sky.datasource.username}password: ${sky.datasource.password}redis:host: localhostport: 6379password: 123456

mybatis 配置

        type-aliases-package: MyBatis 的类型别名包路径。MyBatis 提供了类型别名功能,可以为 Java 类型指定简短的别名,便于在 Mapper XML 文件中使用

开发环境下

数据库连接配置 和 阿里云oss


http://www.mrgr.cn/news/28299.html

相关文章:

  • C/C++语言基础--initializer_list表达式、tuple元组、pair对组简介
  • Android 无签名系统 debug 版本APK push到设备引起的开机异常问题分析(zygote进程)
  • 图形几何之美系列:法向量计算之轮廓有向面积辅助法
  • SpringCloud核心组件(五)
  • 13.观察者模式设计思想
  • UVa 11855 Buzzwords
  • Leetcode 找到字符串中所有字母异位词
  • 研究生招生宣传(2024秋)
  • 12 数组——27. 移除元素 ★
  • 1. TypeScript基本语法
  • Autosar BswM配置-手动建立Swc Port实现自定义模式切换
  • Anaconda安装并配置Python环境
  • STM32外设之LTDC/DMA2D—液晶显示(野火)
  • Zookeeper学习
  • java实现系统文件管理
  • 鸿蒙媒体开发系列01——资源分类访问
  • 深入剖析:C++类对象的内存布局与优化
  • 【C++】——list
  • OJ题-反转链表
  • 利士策分享,家和万事兴:幸福生活的基石
  • Linux 开发工具(vim、gcc/g++、make/Makefile)+【小程序:进度条】-- 详解
  • JVM HotSpot 虚拟机: 对象的创建, 内存布局和访问定位
  • [Golang] Sync
  • 【多线程】深入剖析线程池的应用
  • docker发布redis容器
  • 【 html+css 绚丽Loading 】000050 乾坤合璧轮