From 0ef71681c9f8490abc97dbc64569f23b7952ab79 Mon Sep 17 00:00:00 2001 From: litingting <3316043814@qq.com> Date: Mon, 16 Dec 2024 23:30:59 +0800 Subject: [PATCH] 111 --- .../swg/SnailmallApiGatewayApplication.java | 24 +++- .../com/njupt/swg/cache/JedisPoolWrapper.java | 33 ++++- .../java/com/njupt/swg/common/Parameters.java | 33 ++++- .../java/com/njupt/swg/config/CorsConfig.java | 23 +++- .../com/njupt/swg/constants/Constants.java | 56 ++++++-- .../swg/controller/ErrorHandleController.java | 17 ++- .../main/java/com/njupt/swg/entity/User.java | 18 ++- .../swg/exception/SnailmallException.java | 25 +++- .../com/njupt/swg/filter/AdminUserFilter.java | 78 +++++++---- .../com/njupt/swg/filter/RateLimitFilter.java | 35 ++++- .../java/com/njupt/swg/resp/ResponseEnum.java | 27 +++- .../com/njupt/swg/resp/ServerResponse.java | 89 +++++++++---- .../java/com/njupt/swg/utils/CookieUtil.java | 78 +++++++---- .../com/njupt/swg/utils/DateTimeUtil.java | 86 +++++++++--- .../java/com/njupt/swg/utils/JsonUtil.java | 123 +++++++++++------- 15 files changed, 568 insertions(+), 177 deletions(-) diff --git a/snailmall-api-gateway/src/main/java/com/njupt/swg/SnailmallApiGatewayApplication.java b/snailmall-api-gateway/src/main/java/com/njupt/swg/SnailmallApiGatewayApplication.java index 6a6fdfc..e84b276 100644 --- a/snailmall-api-gateway/src/main/java/com/njupt/swg/SnailmallApiGatewayApplication.java +++ b/snailmall-api-gateway/src/main/java/com/njupt/swg/SnailmallApiGatewayApplication.java @@ -8,20 +8,38 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.PropertySource; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; +// 这是一个Spring Boot应用的主启动类,使用了多个注解来配置应用的相关特性和功能。 @SpringBootApplication +// @SpringBootApplication注解是一个复合注解,它包含了多个用于开启Spring Boot自动配置、组件扫描等功能的注解, +// 表明这是一个Spring Boot应用的入口点,会自动配置很多默认的Spring组件和行为,方便快速搭建项目框架。 + @EnableDiscoveryClient +// @EnableDiscoveryClient注解用于启用服务发现功能,使得该应用能够注册到服务注册中心(如Eureka、Consul等), +// 并且可以从注册中心发现其他服务,方便在微服务架构中实现服务之间的相互调用和协作。 + @EnableZuulProxy -@PropertySource(value="classpath:parameter.properties") +// @EnableZuulProxy注解用于启用Zuul作为微服务架构中的API网关代理,Zuul可以对外部请求进行路由转发、过滤等操作, +// 例如根据不同的请求路径将请求转发到相应的后端微服务上,还能实现诸如限流、权限校验等功能。 + +@PropertySource(value = "classpath:parameter.properties") +// @PropertySource注解用于指定外部属性文件的位置,这里表示从类路径下的parameter.properties文件中加载配置属性, +// 可以在该文件中定义各种项目中需要用到的配置项,如数据库连接信息、服务端口等,Spring会读取并注入这些配置到相应的组件中使用。 public class SnailmallApiGatewayApplication { + // 应用的主方法,是Java应用程序的入口点,用于启动Spring Boot应用。 public static void main(String[] args) { + // 调用SpringApplication的静态run方法来启动Spring Boot应用,传入当前主启动类的Class对象以及命令行参数, + // Spring Boot会根据配置自动完成一系列的初始化、组件加载、配置注入等操作,然后启动应用并监听相应的端口,等待外部请求。 SpringApplication.run(SnailmallApiGatewayApplication.class, args); } + // 使用@Bean注解定义一个Spring Bean,该方法返回的对象会被Spring容器管理,可在其他组件中通过依赖注入的方式使用。 @Bean public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() { + // 创建并返回一个PropertySourcesPlaceholderConfigurer对象,它主要用于处理属性占位符的替换功能, + // 在Spring应用中,如果配置文件中的属性使用了占位符(例如${property.name}这种形式),该配置器会帮助将占位符替换为实际配置的值, + // 确保应用能够正确使用配置文件中的各项配置属性。 return new PropertySourcesPlaceholderConfigurer(); } -} - +} \ No newline at end of file diff --git a/snailmall-api-gateway/src/main/java/com/njupt/swg/cache/JedisPoolWrapper.java b/snailmall-api-gateway/src/main/java/com/njupt/swg/cache/JedisPoolWrapper.java index 52cd0f7..c7fc709 100644 --- a/snailmall-api-gateway/src/main/java/com/njupt/swg/cache/JedisPoolWrapper.java +++ b/snailmall-api-gateway/src/main/java/com/njupt/swg/cache/JedisPoolWrapper.java @@ -14,30 +14,53 @@ import javax.annotation.PostConstruct; * @Date 2019/1/1 15:00 * @CONTACT 317758022@qq.com * @DESC 只做了单个redis,但是课程中实现的redis客户端集群,要掌握一致性hash算法 + * 该类主要用于对JedisPool进行封装和初始化操作,目的是方便获取JedisPool实例来操作Redis。 + * 目前仅针对单个Redis进行配置,而课程中还涉及到了Redis客户端集群相关内容以及一致性hash算法,这里需要后续进一步掌握拓展相关知识。 */ @Component @Slf4j public class JedisPoolWrapper { + + // 通过Spring的依赖注入,获取Parameters对象,该对象应该包含了Redis相关的配置参数 @Autowired private Parameters parameters; + // 用于存放JedisPool实例,初始化为null,后续在初始化方法中进行实例化赋值 private JedisPool jedisPool = null; + /** + * @PostConstruct注解表示该方法会在对象实例化后、依赖注入完成后自动被调用, + * 用于完成一些初始化的操作,在这里主要是对JedisPool进行初始化配置并创建实例。 + */ @PostConstruct - public void init(){ + public void init() { try { + // 创建JedisPoolConfig对象,用于配置JedisPool的各项参数 JedisPoolConfig config = new JedisPoolConfig(); + // 设置JedisPool中最大连接数,参数值从Parameters对象中获取 config.setMaxTotal(parameters.getRedisMaxTotal()); + // 设置JedisPool中空闲连接的最大数量,同样从Parameters对象获取对应参数值 config.setMaxIdle(parameters.getRedisMaxIdle()); + // 设置获取连接时的最大等待时间(单位为毫秒),也是从Parameters对象获取对应参数值 config.setMaxWaitMillis(parameters.getRedisMaxWaitMillis()); - jedisPool = new JedisPool(config,parameters.getRedisHost(),parameters.getRedisPort(),2000,"xxx"); + // 创建JedisPool实例,传入配置对象、Redis服务器主机地址、端口号、超时时间以及密码等信息 + // 这里密码写为"xxx",实际应用中应替换为真实的Redis访问密码 + jedisPool = new JedisPool(config, parameters.getRedisHost(), parameters.getRedisPort(), 2000, "xxx"); + // 如果初始化成功,记录日志信息表示初始化Redis连接池成功 log.info("【初始化redis连接池成功】"); - }catch (Exception e){ - log.error("【初始化redis连接池失败】",e); + } catch (Exception e) { + // 如果在初始化过程中出现异常,记录详细的错误日志,日志内容为"【初始化redis连接池失败】"以及异常信息 + log.error("【初始化redis连接池失败】", e); } } + /** + * 该方法用于对外提供获取JedisPool实例的功能, + * 外部类可以通过调用此方法来获取已经初始化好的JedisPool对象,进而操作Redis。 + * + * @return 返回已经初始化或者创建好的JedisPool实例,如果初始化失败则可能返回null + */ public JedisPool getJedisPool() { return jedisPool; } -} +} \ No newline at end of file diff --git a/snailmall-api-gateway/src/main/java/com/njupt/swg/common/Parameters.java b/snailmall-api-gateway/src/main/java/com/njupt/swg/common/Parameters.java index f147b5c..c7c3db7 100644 --- a/snailmall-api-gateway/src/main/java/com/njupt/swg/common/Parameters.java +++ b/snailmall-api-gateway/src/main/java/com/njupt/swg/common/Parameters.java @@ -11,23 +11,54 @@ import java.util.List; * @Date 2019/1/1 14:27 * @CONTACT 317758022@qq.com * @DESC + * 该类主要用于存储系统中一些配置参数,通过Spring的相关注解来实现配置值的注入, + * 并使用了Lombok的@Data注解来自动生成常见的Getter、Setter、ToString等方法,方便对参数的访问和操作。 */ @Component @Data public class Parameters { /*****redis config start*******/ + /** + * 通过@Value注解,从配置文件(通常是application.properties或application.yml等)中读取名为"redis.host"的配置项的值, + * 并将其注入到该成员变量中,用于表示Redis服务器的主机地址。 + */ @Value("${redis.host}") private String redisHost; + + /** + * 同样使用@Value注解,从配置文件中获取名为"redis.port"的配置项的值, + * 注入到该变量,用于表示Redis服务器的端口号,类型为整型。 + */ @Value("${redis.port}") private int redisPort; + + /** + * 此处原代码中变量名可能有误(从语义看应该是max-idle对应maxIdle,max-total对应maxTotal更合理), + * 不过按现有代码逻辑,它是从配置文件读取名为"redis.max-idle"的配置项的值,注入进来,用于表示某个与Redis相关的最大空闲数量相关的配置参数,类型为整型。 + */ @Value("${redis.max-idle}") private int redisMaxTotal; + + /** + * 类似地,通过@Value注解从配置文件获取"redis.max-total"配置项的值,注入到该变量, + * 按推测应该是用于表示Redis连接池的最大连接数之类的参数,类型为整型。 + */ @Value("${redis.max-total}") private int redisMaxIdle; + + /** + * 从配置文件读取"redis.max-wait-millis"配置项的值,注入到该变量, + * 用于表示获取Redis连接时的最大等待时间(单位为毫秒),类型为整型。 + */ @Value("${redis.max-wait-millis}") private int redisMaxWaitMillis; /*****redis config end*******/ + /** + * 使用@Value注解结合SpEL(Spring表达式语言),从配置文件中读取名为"security.noneSecurityAdminPaths"的配置项的值, + * 并将其按照逗号进行分割,最终注入到该List类型的变量中, + * 推测其可能用于存放一些不需要安全验证的管理员相关路径列表。 + */ @Value("#{'${security.noneSecurityAdminPaths}'.split(',')}") private List noneSecurityAdminPaths; -} +} \ No newline at end of file diff --git a/snailmall-api-gateway/src/main/java/com/njupt/swg/config/CorsConfig.java b/snailmall-api-gateway/src/main/java/com/njupt/swg/config/CorsConfig.java index 0acfa03..67038c6 100644 --- a/snailmall-api-gateway/src/main/java/com/njupt/swg/config/CorsConfig.java +++ b/snailmall-api-gateway/src/main/java/com/njupt/swg/config/CorsConfig.java @@ -11,22 +11,41 @@ import java.util.Arrays; /** * 跨域配置 允许所有的接口都可以跨域 * C - Cross O - Origin R - Resource S - Sharing + * 该类是Spring框架中的配置类,用于配置跨域相关设置,使得项目中的所有接口都能支持跨域访问, + * 通过定义相关的跨域规则来控制不同源之间的资源共享情况。 */ @Configuration public class CorsConfig { + /** + * @Bean注解用于将该方法返回的对象作为一个Bean注册到Spring容器中, + * 在这里返回的是一个CorsFilter类型的Bean,它是Spring处理跨域请求的核心过滤器, + * 通过配置该过滤器来实现具体的跨域策略。 + */ @Bean public CorsFilter corsFilter() { + // 创建一个基于URL的跨域配置源对象,用于根据不同的URL路径来应用不同的跨域配置规则 final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + // 创建一个CorsConfiguration对象,用于具体设置跨域相关的各种配置参数 final CorsConfiguration config = new CorsConfiguration(); + // 设置是否允许携带凭证(例如Cookie等)进行跨域请求,这里设置为true,表示允许 config.setAllowCredentials(true); - config.setAllowedOrigins(Arrays.asList("*")); //http:www.a.com + // 设置允许跨域访问的源(Origin)列表,这里使用Arrays.asList("*")表示允许所有来源的请求跨域访问, + // 在实际应用中,更安全的做法是明确指定具体的源地址,例如http://www.a.com这种形式 + config.setAllowedOrigins(Arrays.asList("*")); + // 设置允许跨域请求包含的头部信息列表,使用Arrays.asList("*")表示允许所有头部信息, + // 但在更严格的安全场景下,应该明确列出允许的头部字段 config.setAllowedHeaders(Arrays.asList("*")); + // 设置允许的跨域请求方法列表,如GET、POST等,这里使用Arrays.asList("*")表示允许所有请求方法跨域访问 config.setAllowedMethods(Arrays.asList("*")); + // 设置浏览器对预检请求(OPTIONS请求)的缓存时间(单位为秒),这里设置为300秒, + // 即在这个时间范围内,对于相同源的相同请求,浏览器无需再次发送预检请求 config.setMaxAge(300l); + // 将上述配置应用到所有路径("/**"表示匹配所有路径)上,这样所有接口都会应用此跨域配置 source.registerCorsConfiguration("/**", config); + // 返回创建好的CorsFilter对象,该过滤器会在Spring处理Web请求时根据配置拦截并处理跨域相关情况 return new CorsFilter(source); } -} +} \ No newline at end of file diff --git a/snailmall-api-gateway/src/main/java/com/njupt/swg/constants/Constants.java b/snailmall-api-gateway/src/main/java/com/njupt/swg/constants/Constants.java index f721714..5ce3e8c 100644 --- a/snailmall-api-gateway/src/main/java/com/njupt/swg/constants/Constants.java +++ b/snailmall-api-gateway/src/main/java/com/njupt/swg/constants/Constants.java @@ -5,42 +5,72 @@ package com.njupt.swg.constants; * @Date 2019/1/1 13:19 * @CONTACT 317758022@qq.com * @DESC + * 该类用于定义项目中的各种常量,将一些常用的、固定不变的值集中管理在此处,方便在整个项目中统一引用,提高代码的可维护性和可读性。 */ public class Constants { - /**自定义状态码 start**/ + + /** + * 自定义状态码 start + * 以下定义了一系列项目中自定义的状态码,用于在不同的业务场景下表示请求的处理结果状态,方便统一规范响应情况。 + */ + // 表示请求成功的状态码,对应HTTP状态码中的200,表示操作成功完成 public static final int RESP_STATUS_OK = 200; + // 表示未授权的状态码,对应HTTP状态码中的401,通常用于用户未通过认证或授权时返回的状态 public static final int RESP_STATUS_NOAUTH = 401; + // 表示服务器内部错误的状态码,对应HTTP状态码中的500,用于在服务器端出现异常等内部问题时返回该状态告知客户端 public static final int RESP_STATUS_INTERNAL_ERROR = 500; + // 表示请求参数错误的状态码,对应HTTP状态码中的400,当客户端发送的请求参数不符合要求时返回此状态 public static final int RESP_STATUS_BADREQUEST = 400; + /** + * 自定义状态码 end + */ - /**自定义状态码 end**/ - - /***redis user相关的key以这个打头**/ + /** + * *redis user相关的key以这个打头 + * 用于定义在Redis中存储用户相关数据时,键(key)的前缀,通过统一前缀方便对用户相关缓存数据进行区分和管理。 + */ public static final String TOKEN_PREFIX = "user_"; /** * 用户登陆redis的过期时间 + * 定义了一个内部接口用于存放用户登录信息在Redis中的过期时间相关常量,这样的接口形式便于在代码中清晰地表示这一组相关的常量定义。 */ public interface RedisCacheExtime{ - int REDIS_SESSION_EXTIME = 60* 30;//30分钟 + // 定义用户登录信息在Redis中的过期时间,以秒为单位,这里设置为30分钟(60秒 * 30),方便后续统一管理和修改该时间设置 + int REDIS_SESSION_EXTIME = 60 * 30; //30分钟 } - /** 用户注册判断重复的参数类型 start **/ + /** + * 用户注册判断重复的参数类型 start + * 以下定义了在用户注册过程中,用于判断是否重复的参数类型对应的常量字符串,方便在验证逻辑中统一使用和判断。 + */ + // 表示注册时判断邮箱是否重复的参数类型对应的常量字符串,用于标识邮箱字段 public static final String EMAIL = "email"; + // 表示注册时判断用户名是否重复的参数类型对应的常量字符串,用于标识用户名字段 public static final String USERNAME = "username"; - /** 用户注册判断重复的参数类型 end **/ + /** + * 用户注册判断重复的参数类型 end + */ - /** 用户角色 **/ + /** + * 用户角色 + * 定义了一个内部接口用于存放项目中不同用户角色对应的整数值常量,清晰地划分和表示不同用户角色,便于在权限等相关逻辑中使用。 + */ public interface Role{ - int ROLE_CUSTOME = 0;//普通用户 - int ROLE_ADMIN = 1;//管理员用户 + // 表示普通用户角色对应的整数值,赋值为0,方便在代码中通过该值来判断用户是否为普通用户角色 + int ROLE_CUSTOME = 0; //普通用户 + + // 表示管理员用户角色对应的整数值,赋值为1,用于判断用户是否为管理员角色 + int ROLE_ADMIN = 1; //管理员用户 } - /**用户注册分布式锁路径***/ + /** + * 用户注册分布式锁路径 + * 定义了在用户注册过程中使用分布式锁时对应的路径字符串常量,方便在分布式锁相关逻辑中准确指定操作路径。 + */ public static final String USER_REGISTER_DISTRIBUTE_LOCK_PATH = "/user_reg"; - -} +} \ No newline at end of file diff --git a/snailmall-api-gateway/src/main/java/com/njupt/swg/controller/ErrorHandleController.java b/snailmall-api-gateway/src/main/java/com/njupt/swg/controller/ErrorHandleController.java index fdac6bd..c81d4a1 100644 --- a/snailmall-api-gateway/src/main/java/com/njupt/swg/controller/ErrorHandleController.java +++ b/snailmall-api-gateway/src/main/java/com/njupt/swg/controller/ErrorHandleController.java @@ -11,17 +11,30 @@ import org.springframework.web.bind.annotation.RestController; * @Date 2019/1/2 21:21 * @CONTACT 317758022@qq.com * @DESC + * 该类是一个Spring MVC中的控制器类,主要用于处理系统中的错误情况,实现了Spring Boot提供的ErrorController接口, + * 目的是自定义当出现错误(比如未登录、权限不足等情况)时返回给客户端的响应内容。 */ @RestController public class ErrorHandleController implements ErrorController { + + /** + * 实现ErrorController接口的方法,用于指定处理错误的路径, + * 返回值"/error"表示当系统出现错误时,会将请求转发到"/error"这个路径进行后续处理。 + */ @Override public String getErrorPath() { return "/error"; } + /** + * 使用@RequestMapping注解将该方法映射到"/error"路径上, + * 也就是当请求到达"/error"路径时,会执行这个方法来生成并返回相应的错误响应信息给客户端。 + * 这里返回的是一个ServerResponse类型的响应对象,通过调用ServerResponse的静态方法创建, + * 携带了对应错误的状态码(从ResponseEnum中获取未登录或权限不足对应的状态码)以及相应的错误提示信息,告知客户端用户未登陆或者权限不足的情况。 + */ @RequestMapping("/error") public ServerResponse error() { - return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登陆或者权限不足"); + return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(), "用户未登陆或者权限不足"); } -} +} \ No newline at end of file diff --git a/snailmall-api-gateway/src/main/java/com/njupt/swg/entity/User.java b/snailmall-api-gateway/src/main/java/com/njupt/swg/entity/User.java index 4354b81..b36f1e0 100644 --- a/snailmall-api-gateway/src/main/java/com/njupt/swg/entity/User.java +++ b/snailmall-api-gateway/src/main/java/com/njupt/swg/entity/User.java @@ -13,31 +13,45 @@ import java.util.Date; * @Date 2018/12/31 21:01 * @CONTACT 317758022@qq.com * @DESC 用户实体类 + * 该类用于定义用户相关的属性,代表了系统中用户的基本信息模型,通过使用Lombok的相关注解来简化代码编写, + * 避免手动编写一些常规的方法(如Getter、Setter、构造函数、ToString方法等),实现了Java对象与数据库等存储介质中用户数据的映射。 */ @Data +// 使用@Data注解,由Lombok自动为类中的所有非final字段生成Getter、Setter方法,同时还生成了equals、hashCode和toString方法,方便对对象属性的访问和操作。 @NoArgsConstructor +// 使用@NoArgsConstructor注解,由Lombok自动生成一个无参构造函数,方便在某些情况下(如通过反射等方式创建对象实例)对对象进行初始化。 @AllArgsConstructor +// 使用@AllArgsConstructor注解,由Lombok自动生成一个包含所有参数的构造函数,可用于在创建对象时一次性传入所有必要的属性值来初始化对象。 @ToString +// 使用@ToString注解,由Lombok自动为该类生成一个toString方法,便于在调试等场景下直观地查看对象的属性值信息。 public class User implements Serializable { + // 用户的唯一标识符,通常对应数据库表中的主键,用于区分不同的用户记录,类型为整数类型。 private Integer id; + // 用户的用户名,用于用户登录等场景下的身份标识,是一个字符串类型的属性。 private String username; + // 用户的登录密码,存储用户登录时需要验证的密码信息,为字符串类型,通常会进行加密存储以保证安全性。 private String password; + // 用户的电子邮箱地址,可用于找回密码、接收系统通知等功能,是字符串类型的属性。 private String email; + // 用户的电话号码,可能用于联系用户、短信验证等相关业务场景,同样为字符串类型。 private String phone; + // 用户设置的密保问题,用于在忘记密码等情况下辅助验证用户身份,属于字符串类型的属性。 private String question; + // 用户对密保问题设置的答案,与question属性对应,用于验证用户身份,也是字符串类型。 private String answer; - //角色0-管理员,1-普通用户 + // 用户角色,通过整数值来表示不同的角色类型,0代表管理员,1代表普通用户,用于权限控制等相关业务逻辑。 private Integer role; + // 用户账号创建的时间,记录用户首次注册创建账号的具体日期和时间,类型为Java中的Date类型。 private Date createTime; + // 用户信息最后更新的时间,每次用户修改自身信息等操作后会更新该时间,同样是Date类型的属性。 private Date updateTime; - } \ No newline at end of file diff --git a/snailmall-api-gateway/src/main/java/com/njupt/swg/exception/SnailmallException.java b/snailmall-api-gateway/src/main/java/com/njupt/swg/exception/SnailmallException.java index af7918a..a2e718e 100644 --- a/snailmall-api-gateway/src/main/java/com/njupt/swg/exception/SnailmallException.java +++ b/snailmall-api-gateway/src/main/java/com/njupt/swg/exception/SnailmallException.java @@ -8,18 +8,33 @@ import lombok.Getter; * @Date 2019/1/1 13:18 * @CONTACT 317758022@qq.com * @DESC + * 该类是一个自定义的异常类,继承自Java的RuntimeException,用于在项目中抛出特定的运行时异常情况, + * 通过添加自定义的属性和构造函数,可以携带更多与异常相关的信息,方便统一处理和向客户端返回合适的错误响应。 */ @Getter -public class SnailmallException extends RuntimeException{ +// 使用Lombok的@Getter注解,自动为类中的私有属性生成对应的Getter方法,使得外部可以获取这些属性的值。 +public class SnailmallException extends RuntimeException { + // 用于存储异常对应的状态码,初始化为ResponseEnum.ERROR.getCode(),即默认的错误状态码,后续可通过构造函数进行修改。 private int exceptionStatus = ResponseEnum.ERROR.getCode(); - public SnailmallException(String msg){ + /** + * 构造函数,接收一个字符串类型的参数msg,用于创建一个只携带错误消息的异常实例, + * 调用父类(RuntimeException)的构造函数将错误消息传递上去,以便在异常抛出时可以展示相应的错误提示信息。 + * @param msg 异常的错误消息内容,用于描述异常发生的原因等情况。 + */ + public SnailmallException(String msg) { super(msg); } - public SnailmallException(int code,String msg){ + /** + * 重载的构造函数,接收一个整型的状态码参数code和一个字符串类型的错误消息参数msg, + * 不仅会调用父类构造函数传递错误消息,还会将传入的状态码赋值给exceptionStatus属性, + * 这样就可以创建一个携带特定状态码和错误消息的异常实例,方便根据不同情况进行更细致的异常处理和响应。 + * @param code 异常对应的状态码,用于表示特定的错误类型等情况,可对应项目中定义的不同错误状态码枚举值。 + * @param msg 异常的错误消息内容,同上面构造函数中的msg参数含义。 + */ + public SnailmallException(int code, String msg) { super(msg); exceptionStatus = code; } - -} +} \ No newline at end of file diff --git a/snailmall-api-gateway/src/main/java/com/njupt/swg/filter/AdminUserFilter.java b/snailmall-api-gateway/src/main/java/com/njupt/swg/filter/AdminUserFilter.java index 037213b..7bc36b1 100644 --- a/snailmall-api-gateway/src/main/java/com/njupt/swg/filter/AdminUserFilter.java +++ b/snailmall-api-gateway/src/main/java/com/njupt/swg/filter/AdminUserFilter.java @@ -27,26 +27,43 @@ import static org.springframework.cloud.netflix.zuul.filters.support.FilterConst * @Date 2019/1/3 10:21 * @CONTACT 317758022@qq.com * @DESC 关于后台管理系统,登陆不需要拦截,对于需要认证的富文本上传,需要特定的格式,所以,这里先对富文本放开,到controller里面进行特别处理 - * 前台,就不再在这里进行处理了,因为前台需要防止用户同级的攻击,所以需要userID,放到这里,controller那边不好处理 + * 前台,就不再在这里进行处理了,因为前台需要防止用户同级的攻击,所以需要userID,放到这里,controller那边不好处理 + * 该类是一个ZuulFilter的实现类,用于在Zuul网关中对请求进行过滤处理,主要针对后台管理系统的请求进行权限校验等相关操作, + * 根据不同的请求路径和用户身份等情况决定是否放行请求,或者返回相应的错误提示信息。 */ @Slf4j @Component public class AdminUserFilter extends ZuulFilter { + // 通过依赖注入获取CommonCacheUtil实例,用于后续从缓存(Redis)中获取用户相关信息 @Autowired private CommonCacheUtil commonCacheUtil; + // 通过依赖注入获取Parameters实例,该实例可能包含了一些系统配置参数,比如不需要安全校验的路径等信息 @Autowired private Parameters parameters; + /** + * 该方法用于指定过滤器的类型,返回值表明该过滤器属于前置过滤器(在请求路由之前执行), + * 对应的值取自Spring Cloud Zuul定义的常量PRE_TYPE,表示在请求被路由之前进行相关处理。 + */ @Override public String filterType() { return PRE_TYPE; } + /** + * 该方法用于指定过滤器的执行顺序,返回值表示该过滤器在前置过滤器中的执行顺序, + * 通过取PRE_DECORATION_FILTER_ORDER减1的值来确定其相对顺序,使得该过滤器能在特定的顺序下执行,先于某些其他前置过滤器操作。 + */ @Override public int filterOrder() { - return PRE_DECORATION_FILTER_ORDER-1; + return PRE_DECORATION_FILTER_ORDER - 1; } + /** + * 该方法用于判断当前请求是否需要经过该过滤器进行处理,根据请求的URL等条件来决定。 + * 首先获取当前请求的上下文以及对应的HttpServletRequest对象,然后获取请求的URL, + * 根据URL的内容判断是否需要进行权限校验,如果不符合校验条件则直接返回false表示不需要该过滤器处理,否则返回true。 + */ @Override public boolean shouldFilter() { RequestContext requestContext = RequestContext.getCurrentContext(); @@ -54,63 +71,68 @@ public class AdminUserFilter extends ZuulFilter { //获取当前请求的url String url = request.getRequestURI(); //前端的路径不在这里校验,直接放过 - if (!url.contains("manage")){ - log.info("【{}不需要进行权限校验】",url); + if (!url.contains("manage")) { + log.info("【{}不需要进行权限校验】", url); return false; } - if(url.contains("upload")){ - log.info("【{}不需要进行权限校验】",url); + if (url.contains("upload")) { + log.info("【{}不需要进行权限校验】", url); return false; } //从配置文件获取所有门户需要校验的路径 // String[] passUrls = parameters.getNoneSecurityAdminPaths().toArray(new String[parameters.getNoneSecurityAdminPaths().size()]); -// for(String str:passUrls){ +// for (String str : passUrls) { // //指定的路径比较特殊,也不在这里校验 -// if(url.contains("manage") && url.contains(str)){ -// log.info("【{}不需要进行权限校验】",url); +// if (url.contains("manage") && url.contains(str)) { +// log.info("【{}不需要进行权限校验】", url); // return false; // } // } - log.info("【{}----需要进行权限校验,必须是管理员身份才可以进入】",url); + log.info("【{}----需要进行权限校验,必须是管理员身份才可以进入】", url); return true; } + /** + * 该方法是过滤器的核心执行逻辑所在,当shouldFilter方法返回true时会执行该方法, + * 在这里主要进行用户登录状态以及管理员权限的校验等操作,如果校验不通过则进行相应的错误处理, + * 如阻止请求路由、设置错误响应状态码、返回错误提示信息等,若校验通过则允许请求继续路由转发。 + */ @Override public ServerResponse run() throws ZuulException { RequestContext requestContext = RequestContext.getCurrentContext(); HttpServletRequest request = requestContext.getRequest(); //校验是否为管理员身份 String loginToken = CookieUtil.readLoginToken(request); - log.info("【获取cookie----{}】",loginToken); - if(StringUtils.isEmpty(loginToken)){ + log.info("【获取cookie----{}】", loginToken); + if (StringUtils.isEmpty(loginToken)) { // // 过滤该请求,不对其进行路由 // requestContext.setSendZuulResponse(false); // //返回错误代码 // requestContext.setResponseStatusCode(Constants.RESP_STATUS_NOAUTH); // return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息"); this.returnMsg(requestContext); - return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息"); + return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(), "用户未登录,无法获取当前用户信息"); //throw new SnailmallException("用户未登录,无法获取当前用户信息"); } //2.从redis中获取用户信息 String userStr = commonCacheUtil.getCacheValue(loginToken); - log.info("【从redis中获取用户信息:{}】",userStr); - if(userStr == null){ + log.info("【从redis中获取用户信息:{}】", userStr); + if (userStr == null) { // // 过滤该请求,不对其进行路由 // requestContext.setSendZuulResponse(false); // //返回错误代码 // requestContext.setResponseStatusCode(Constants.RESP_STATUS_NOAUTH); // return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息"); //SnailmallException(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息"); this.returnMsg(requestContext); - return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息"); + return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(), "用户未登录,无法获取当前用户信息"); } String url = request.getRequestURI(); - log.info("【获取当前url:{}】",url); - if(url.contains("manage")){ + log.info("【获取当前url:{}】", url); + if (url.contains("manage")) { log.info("【来到了管理后台,需要校验权限】"); - User currentUser = JsonUtil.Str2Obj(userStr,User.class); - log.info("【当前登陆的用户为:{}】",currentUser); - if(!currentUser.getRole().equals(Constants.Role.ROLE_ADMIN)){ + User currentUser = JsonUtil.Str2Obj(userStr, User.class); + log.info("【当前登陆的用户为:{}】", currentUser); + if (!currentUser.getRole().equals(Constants.Role.ROLE_ADMIN)) { //不是管理员报错 log.error("【当前登陆用户不是管理员身份】"); // 过滤该请求,不对其进行路由 @@ -119,7 +141,7 @@ public class AdminUserFilter extends ZuulFilter { // requestContext.setResponseStatusCode(Constants.RESP_STATUS_NOAUTH); // return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息"); this.returnMsg(requestContext); - return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息"); + return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(), "用户未登录,无法获取当前用户信息"); //throw new SnailmallException("用户权限不够"); } } @@ -127,10 +149,14 @@ public class AdminUserFilter extends ZuulFilter { } //返回没权限消息 - private void returnMsg(RequestContext ctx){ + private void returnMsg(RequestContext ctx) { + // 设置响应的内容类型为JSON格式,字符编码为utf-8,确保返回的错误信息能被正确解析 ctx.getResponse().setContentType("application/json; charset=utf-8"); - ctx.setSendZuulResponse(false); //令zuul过滤该请求,不对其进行路由 + // 令zuul过滤该请求,不对其进行路由,即阻止请求继续往后端服务转发 + ctx.setSendZuulResponse(false); + // 设置响应的状态码,这里设置为Constants.RESP_STATUS_OK,可能需要根据实际情况确认是否合适(一般权限相关错误可以考虑用对应的错误状态码) ctx.setResponseStatusCode(Constants.RESP_STATUS_OK); - ctx.setResponseBody(JsonUtil.obj2String(ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息"))); + // 将错误提示信息转换为JSON字符串,并设置为响应体的内容,以便客户端能获取到具体的错误提示信息 + ctx.setResponseBody(JsonUtil.obj2String(ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(), "用户未登录,无法获取当前用户信息"))); } -} +} \ No newline at end of file diff --git a/snailmall-api-gateway/src/main/java/com/njupt/swg/filter/RateLimitFilter.java b/snailmall-api-gateway/src/main/java/com/njupt/swg/filter/RateLimitFilter.java index 4f1944b..3393ac6 100644 --- a/snailmall-api-gateway/src/main/java/com/njupt/swg/filter/RateLimitFilter.java +++ b/snailmall-api-gateway/src/main/java/com/njupt/swg/filter/RateLimitFilter.java @@ -16,37 +16,64 @@ import static org.springframework.cloud.netflix.zuul.filters.support.FilterConst * @Date 2019/1/3 11:21 * @CONTACT 317758022@qq.com * @DESC 令牌桶法限流 具体测试没做 + * 该类是一个ZuulFilter的实现类,用于在Zuul网关中实现基于令牌桶算法的限流功能, + * 通过控制请求获取令牌的情况来决定是否允许请求继续往后端服务转发,以此限制单位时间内的请求数量。 */ @Component public class RateLimitFilter extends ZuulFilter { //放100个令牌 + // 使用RateLimiter类(来自Google Guava库)创建一个令牌桶限流器,设置初始令牌数量为100个, + // 意味着令牌桶一开始有100个可用令牌,后续请求需要获取令牌才能通过该过滤器继续路由。 private static final RateLimiter RATE_LIMITER = RateLimiter.create(100); + /** + * 该方法用于指定过滤器的类型,返回值表明该过滤器属于前置过滤器(在请求路由之前执行), + * 对应的值取自Spring Cloud Zuul定义的常量PRE_TYPE,表示在请求被路由之前进行相关处理,这里在前置阶段进行限流判断。 + */ @Override public String filterType() { return PRE_TYPE; } + /** + * 该方法用于指定过滤器的执行顺序,返回值表示该过滤器在前置过滤器中的执行顺序, + * 通过取SERVLET_DETECTION_FILTER_ORDER减1的值来确定其相对顺序,使得该过滤器能在特定的顺序下执行,先于某些其他前置过滤器操作。 + */ @Override public int filterOrder() { return SERVLET_DETECTION_FILTER_ORDER - 1; } + /** + * 该方法用于判断当前请求是否需要经过该过滤器进行处理,这里直接返回true, + * 意味着所有的请求都会经过该过滤器进行限流相关的判断操作,不会有请求被直接跳过该限流判断环节。 + */ @Override public boolean shouldFilter() { return true; } + /** + * 该方法是过滤器的核心执行逻辑所在,当shouldFilter方法返回true时会执行该方法, + * 在这里主要进行令牌获取的操作,如果请求能够成功获取到一个令牌(即令牌桶中有可用令牌),则允许请求继续路由转发, + * 若没有取到令牌,则进行相应的错误处理,比如设置一些错误相关的信息到请求上下文中(这里简单示例了设置状态码和错误消息)。 + */ @Override public Object run() throws ZuulException { RequestContext context = RequestContext.getCurrentContext(); HttpServletRequest request = context.getRequest(); - if(!RATE_LIMITER.tryAcquire()){ + // 使用tryAcquire方法尝试从令牌桶中获取一个令牌,该方法会立即返回获取结果, + // 如果获取成功(即令牌桶中有可用令牌)则返回true,请求可以继续往后执行;如果获取失败(令牌桶中无可用令牌)则返回false。 + if (!RATE_LIMITER.tryAcquire()) { //没有取到一个令牌的话,可以这样返回信息给前端 - context.set("状态码",401); - context.set("error.message","用户没有获取到令牌"); + // 在请求上下文中设置一个名为"状态码"的属性,值为401,表示未授权状态(这里只是简单示例设置状态码,实际应用可能需遵循规范的状态码使用方式), + // 用于后续可能的错误处理或者向客户端返回相应的提示信息,告知客户端请求因未获取到令牌而被限制。 + context.set("状态码", 401); + // 在请求上下文中设置一个名为"error.message"的属性,值为"用户没有获取到令牌", + // 用于更详细地向客户端说明请求被限制的原因,方便客户端知晓具体的错误情况。 + context.set("error.message", "用户没有获取到令牌"); } return null; } -} +} \ No newline at end of file diff --git a/snailmall-api-gateway/src/main/java/com/njupt/swg/resp/ResponseEnum.java b/snailmall-api-gateway/src/main/java/com/njupt/swg/resp/ResponseEnum.java index 428850a..6e07b1c 100644 --- a/snailmall-api-gateway/src/main/java/com/njupt/swg/resp/ResponseEnum.java +++ b/snailmall-api-gateway/src/main/java/com/njupt/swg/resp/ResponseEnum.java @@ -7,19 +7,34 @@ import lombok.Getter; * @Date 2018/12/31 20:15 * @CONTACT 317758022@qq.com * @DESC 基本的返回状态描述 + * 该类是一个枚举类型,用于定义项目中基本的响应状态相关的常量值, + * 通过枚举的形式可以清晰地列举出不同的返回状态及其对应的代码和描述信息,方便在整个项目中统一使用和进行状态判断。 */ @Getter +// 使用Lombok的@Getter注解,会自动为枚举中的code和desc属性生成对应的Getter方法,便于外部获取这些属性的值。 public enum ResponseEnum { - SUCCESS(0,"SUCCESS"), - ERROR(1,"ERROR"), - ILLEGAL_ARGUMENTS(2,"ILLEGAL_ARGUMENTS"), - NEED_LOGIN(10,"NEED_LOGIN"); + // 定义一个名为SUCCESS的枚举常量,表示操作成功的响应状态,其对应的状态码为0,描述信息为"SUCCESS", + // 可用于在返回响应给客户端等场景下表示请求处理成功的情况。 + SUCCESS(0, "SUCCESS"), + // 定义一个名为ERROR的枚举常量,表示操作出现错误的响应状态,状态码为1,描述信息为"ERROR", + // 当系统出现一般性错误时可使用该枚举值来构建相应的错误响应返回给客户端。 + ERROR(1, "ERROR"), + // 定义一个名为ILLEGAL_ARGUMENTS的枚举常量,用于表示请求参数不合法的响应状态,状态码为2,描述信息为"ILLEGAL_ARGUMENTS", + // 当客户端发送的请求参数不符合要求时,可以用此枚举值来构造响应告知客户端参数非法情况。 + ILLEGAL_ARGUMENTS(2, "ILLEGAL_ARGUMENTS"), + // 定义一个名为NEED_LOGIN的枚举常量,用于表示需要用户登录的响应状态,状态码为10,描述信息为"NEED_LOGIN", + // 当客户端发起需要登录才能访问的请求但未登录时,可通过该枚举值构建响应提示客户端进行登录操作。 + NEED_LOGIN(10, "NEED_LOGIN"); + // 用于存储响应状态对应的代码,不同的枚举常量有不同的整数值,以此区分不同的响应情况。 private int code; + // 用于存储响应状态对应的描述信息,以字符串形式展示具体的状态含义,方便直观理解每个枚举常量所代表的情况。 private String desc; - ResponseEnum(int code,String desc){ + // 枚举类型的构造函数,用于初始化每个枚举常量对应的code和desc属性值, + // 在定义枚举常量时传入相应的代码和描述信息,以此来完成每个枚举常量的具体状态设置。 + ResponseEnum(int code, String desc) { this.code = code; this.desc = desc; } -} +} \ No newline at end of file diff --git a/snailmall-api-gateway/src/main/java/com/njupt/swg/resp/ServerResponse.java b/snailmall-api-gateway/src/main/java/com/njupt/swg/resp/ServerResponse.java index 2dd46a2..bd9e87f 100644 --- a/snailmall-api-gateway/src/main/java/com/njupt/swg/resp/ServerResponse.java +++ b/snailmall-api-gateway/src/main/java/com/njupt/swg/resp/ServerResponse.java @@ -11,65 +11,110 @@ import java.io.Serializable; * @Date 2018/12/31 20:11 * @CONTACT 317758022@qq.com * @DESC 作为本项目的通用的返回封装类 + * 该类是项目中的一个通用返回值封装类,用于统一包装服务器端返回给客户端的响应信息, + * 包含了响应状态码、提示消息以及具体的数据内容等属性,通过提供一系列静态方法方便创建不同情况(成功、失败等)下的响应对象实例。 */ @Getter +// 使用Lombok的@Getter注解,自动为类中的私有属性(status、msg、data)生成对应的Getter方法,便于外部获取这些属性的值。 @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) +// 使用@JsonSerialize注解并指定Inclusion.NON_NULL配置,用于在将对象转换为JSON格式时,忽略值为null的属性, +// 这样在返回给客户端的JSON数据中就不会包含那些没有实际值的属性,减少数据传输量并使数据结构更简洁。 public class ServerResponse implements Serializable { + // 用于存储响应的状态码,通过不同的状态码值来表示请求处理的结果情况,如成功、失败、需要登录等不同状态。 private int status; + // 用于存储响应的提示消息,以字符串形式向客户端传达一些与响应相关的额外信息,比如成功时的提示、失败原因等。 private String msg; + // 用于存储响应中具体的数据内容,该数据类型通过泛型参数来灵活指定,根据不同的业务场景可以是各种类型的数据(如对象、列表等)。 private T data; - private ServerResponse(int status){ + // 私有构造函数,接收一个状态码参数,用于创建只包含状态码的ServerResponse对象实例, + // 通常在一些内部初始化或者特定场景下使用,其他更完整的构造函数会基于此进一步设置更多属性值。 + private ServerResponse(int status) { this.status = status; } - private ServerResponse(int status,String msg){ + + // 私有构造函数,接收状态码和提示消息两个参数,用于创建包含状态码和提示消息的ServerResponse对象实例, + // 可用于在需要返回带有特定提示信息的响应情况时进行对象初始化,比如返回错误提示消息等场景。 + private ServerResponse(int status, String msg) { this.status = status; this.msg = msg; } - private ServerResponse(int status,T data){ + + // 私有构造函数,接收状态码和具体数据两个参数,用于创建包含状态码和具体业务数据的ServerResponse对象实例, + // 当有需要返回成功处理后的数据给客户端时,可以使用此构造函数来初始化响应对象。 + private ServerResponse(int status, T data) { this.status = status; this.data = data; } - private ServerResponse(int status,String msg,T data){ + + // 私有构造函数,接收状态码、提示消息以及具体数据三个参数,用于创建一个完整包含状态码、提示消息和业务数据的ServerResponse对象实例, + // 可以在需要同时返回详细提示信息和数据的场景下使用此构造函数进行对象初始化。 + private ServerResponse(int status, String msg, T data) { this.status = status; this.msg = msg; this.data = data; } + /** + * @JsonIgnore注解用于标记该方法,使得在将对象转换为JSON格式时,忽略此方法对应的属性(这里实际是方法返回值当作属性来看待), + * 该方法用于判断当前响应是否表示成功状态,通过比较响应状态码与表示成功的状态码(ResponseEnum.SUCCESS.getCode())是否相等来确定, + * 返回true表示成功,false表示失败或其他非成功状态。 + */ @JsonIgnore - public boolean isSuccess(){ + public boolean isSuccess() { return this.status == ResponseEnum.SUCCESS.getCode(); } /** * 成功的方法 + * 以下是一组静态方法,用于创建表示成功情况的ServerResponse对象实例,方便在业务逻辑处理成功后返回相应的响应给客户端, + * 每个方法根据不同的参数需求返回不同内容设置的成功响应对象。 */ - public static ServerResponse createBySuccess(){ - return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),ResponseEnum.SUCCESS.getDesc()); + // 创建一个表示成功的ServerResponse对象实例,状态码使用ResponseEnum.SUCCESS中定义的成功状态码,提示消息也使用ResponseEnum.SUCCESS中定义的描述信息, + // 适用于简单的成功情况,不需要额外传递具体业务数据和自定义提示消息的场景。 + public static ServerResponse createBySuccess() { + return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(), ResponseEnum.SUCCESS.getDesc()); } - public static ServerResponse createBySuccessMessage(String message){ - return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),message); + + // 创建一个表示成功的ServerResponse对象实例,状态码使用ResponseEnum.SUCCESS中定义的成功状态码,提示消息使用传入的自定义参数message, + // 适用于需要返回成功状态但想自定义提示消息给客户端的场景,比如提示操作成功的具体说明等情况。 + public static ServerResponse createBySuccessMessage(String message) { + return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(), message); } - public static ServerResponse createBySuccess(T data){ - return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),data); + + // 创建一个表示成功的ServerResponse对象实例,状态码使用ResponseEnum.SUCCESS中定义的成功状态码,具体业务数据使用传入的参数data, + // 适用于有具体业务数据需要返回给客户端以表示操作成功且携带对应数据的场景,比如查询数据成功后返回查询到的数据列表等情况。 + public static ServerResponse createBySuccess(T data) { + return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(), data); } - public static ServerResponse createBySuccess(String message,T data){ - return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),message,data); + + // 创建一个表示成功的ServerResponse对象实例,状态码使用ResponseEnum.SUCCESS中定义的成功状态码,提示消息使用传入的参数message,具体业务数据使用传入的参数data, + // 适用于既需要返回自定义的提示消息又要携带具体业务数据来表示成功情况的场景,更加灵活地满足不同业务需求下成功响应的构建。 + public static ServerResponse createBySuccess(String message, T data) { + return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(), message, data); } /** * 失败的方法 + * 以下是一组静态方法,用于创建表示失败情况的ServerResponse对象实例,方便在业务逻辑处理出现错误等情况时返回相应的失败响应给客户端, + * 同样每个方法根据不同的参数需求返回不同内容设置的失败响应对象。 */ - public static ServerResponse createByError(){ - return new ServerResponse<>(ResponseEnum.ERROR.getCode(),ResponseEnum.ERROR.getDesc()); - } - public static ServerResponse createByErrorMessage(String msg){ - return new ServerResponse<>(ResponseEnum.ERROR.getCode(),msg); - } - public static ServerResponse createByErrorCodeMessage(int code,String msg){ - return new ServerResponse<>(code,msg); + // 创建一个表示失败的ServerResponse对象实例,状态码使用ResponseEnum.ERROR中定义的表示错误的状态码,提示消息也使用ResponseEnum.ERROR中定义的描述信息, + // 适用于一般性的错误情况,返回默认的错误提示给客户端表示操作出现问题的场景。 + public static ServerResponse createByError() { + return new ServerResponse<>(ResponseEnum.ERROR.getCode(), ResponseEnum.ERROR.getDesc()); } + // 创建一个表示失败的ServerResponse对象实例,状态码使用ResponseEnum.ERROR中定义的表示错误的状态码,提示消息使用传入的自定义参数msg, + // 适用于需要返回特定错误提示消息给客户端以说明具体错误原因的场景,比如参数错误、业务规则不满足等各种导致失败的具体情况说明。 + public static ServerResponse createByErrorMessage(String msg) { + return new ServerResponse<>(ResponseEnum.ERROR.getCode(), msg); + } + // 创建一个表示失败的ServerResponse对象实例,状态码使用传入的自定义参数code(通常是根据具体错误类型定义的不同状态码),提示消息使用传入的参数msg, + // 适用于需要根据不同错误类型返回对应状态码和具体错误提示消息的场景,更加灵活准确地向客户端反馈错误情况,比如不同业务模块下的不同错误状态反馈等。 + public static ServerResponse createByErrorCodeMessage(int code, String msg) { + return new ServerResponse<>(code, msg); + } -} +} \ No newline at end of file diff --git a/snailmall-api-gateway/src/main/java/com/njupt/swg/utils/CookieUtil.java b/snailmall-api-gateway/src/main/java/com/njupt/swg/utils/CookieUtil.java index ffab240..782bc41 100644 --- a/snailmall-api-gateway/src/main/java/com/njupt/swg/utils/CookieUtil.java +++ b/snailmall-api-gateway/src/main/java/com/njupt/swg/utils/CookieUtil.java @@ -9,40 +9,59 @@ import javax.servlet.http.HttpServletResponse; /** * cookie读写 + * 该类提供了与Cookie操作相关的实用方法,主要用于在Web应用中进行登录相关Cookie的写入、读取以及注销时的删除操作, + * 通过封装这些操作逻辑,方便在项目的不同业务场景中统一处理Cookie相关事宜,确保用户登录等状态信息的正确维护。 */ @Slf4j public class CookieUtil { + // 定义Cookie的域名,用于指定该Cookie在哪个域名下有效,这里设置为"oursnail.cn",表示该Cookie在该域名及其子域名下都可以被识别和使用。 private final static String COOKIE_DOMAIN = "oursnail.cn"; + // 定义Cookie的名称,用于唯一标识该Cookie,这里设置为"snailmall_login_token",通常用于存储用户登录相关的标识信息(如登录令牌等)。 private final static String COOKIE_NAME = "snailmall_login_token"; /** * 登陆的时候写入cookie - * @param response - * @param token + * 该方法用于在用户登录时向客户端写入一个Cookie,将登录相关的标识信息(如登录令牌)存储到客户端,方便后续识别用户登录状态等操作。 + * @param response 用于向客户端发送响应的HttpServletResponse对象,通过它来添加要写入客户端的Cookie信息。 + * @param token 要写入Cookie的值,通常是代表用户登录状态的令牌等关键信息,会被存储在名为COOKIE_NAME的Cookie中。 */ - public static void writeLoginToken(HttpServletResponse response,String token){ - Cookie ck = new Cookie(COOKIE_NAME,token); + public static void writeLoginToken(HttpServletResponse response, String token) { + // 创建一个新的Cookie对象,指定Cookie的名称(使用预先定义的COOKIE_NAME)和要存储的值(传入的token参数)。 + Cookie ck = new Cookie(COOKIE_NAME, token); + // 设置Cookie的域名,使其在指定的域名(COOKIE_DOMAIN)及其子域名下有效,确保在相应的网站范围内能正确识别该Cookie。 ck.setDomain(COOKIE_DOMAIN); - ck.setPath("/");//设值在根目录 - ck.setHttpOnly(true);//不允许通过脚本访问cookie,避免脚本攻击 - ck.setMaxAge(60*60*24*365);//一年,-1表示永久,单位是秒,maxage不设置的话,cookie就不会写入硬盘,只会写在内存,只在当前页面有效 - log.info("write cookieName:{},cookieValue:{}",ck.getName(),ck.getValue()); + // 设置Cookie的路径为根目录("/"),表示该Cookie在整个网站的所有页面路径下都可以被访问到,即全站有效。 + ck.setPath("/"); //设值在根目录 + // 设置Cookie为HttpOnly属性为true,这样可以防止通过客户端脚本(如JavaScript)访问该Cookie,有效避免脚本攻击,提高安全性。 + ck.setHttpOnly(true); //不允许通过脚本访问cookie,避免脚本攻击 + // 设置Cookie的最大存活时间,这里设置为一年(60秒 * 60分钟 * 24小时 * 365天),单位是秒, + // 如果设置为-1则表示永久有效,若不设置该属性(maxage不设置的话),Cookie就不会写入硬盘,只会写在内存,只在当前页面有效。 + ck.setMaxAge(60 * 60 * 24 * 365); //一年,-1表示永久,单位是秒,maxage不设置的话,cookie就不会写入硬盘,只会写在内存,只在当前页面有效 + // 记录日志,输出要写入的Cookie的名称和值,方便在调试等场景下查看具体的Cookie写入情况。 + log.info("write cookieName:{},cookieValue:{}", ck.getName(), ck.getValue()); + // 将创建并配置好的Cookie添加到响应中,这样客户端收到响应后就会将该Cookie保存下来,以便后续请求时携带该Cookie信息。 response.addCookie(ck); } /** * 读取登陆的cookie - * @param request - * @return + * 该方法用于从客户端发送的请求中读取之前写入的登录相关的Cookie信息(名称为COOKIE_NAME的Cookie), + * 如果能找到则返回其存储的值(通常是登录令牌等),若没找到则返回null。 + * @param request 用于接收客户端请求的HttpServletRequest对象,通过它来获取客户端携带的Cookie信息。 + * @return 返回从请求中读取到的登录相关Cookie的值(如果存在),若不存在则返回null。 */ - public static String readLoginToken(HttpServletRequest request){ + public static String readLoginToken(HttpServletRequest request) { + // 从请求中获取所有的Cookie数组,客户端发送请求时会携带之前服务器写入的Cookie信息,这里获取到这些Cookie以便后续查找特定的Cookie。 Cookie[] cks = request.getCookies(); - if(cks != null){ - for(Cookie ck:cks){ - log.info("cookieName:{},cookieBValue:{}",ck.getName(),ck.getValue()); - if(StringUtils.equals(ck.getName(),COOKIE_NAME)){ - log.info("return cookieName:{},cookieBValue:{}",ck.getName(),ck.getValue()); + if (cks!= null) { + for (Cookie ck : cks) { + // 记录每个Cookie的名称和值的日志信息,方便在调试等场景下查看请求中携带的Cookie情况。 + log.info("cookieName:{},cookieBValue:{}", ck.getName(), ck.getValue()); + // 通过比较Cookie的名称是否与预先定义的登录相关Cookie名称(COOKIE_NAME)相等,来判断是否是我们要找的登录Cookie。 + if (StringUtils.equals(ck.getName(), COOKIE_NAME)) { + // 如果找到对应的登录Cookie,记录其名称和值的日志信息,并返回该Cookie存储的值(通常是登录令牌等关键信息)。 + log.info("return cookieName:{},cookieBValue:{}", ck.getName(), ck.getValue()); return ck.getValue(); } } @@ -52,18 +71,27 @@ public class CookieUtil { /** * 注销的时候进行删除 - * @param request - * @param response + * 该方法用于在用户注销登录时,从客户端删除之前写入的登录相关的Cookie,使得客户端后续请求不再携带该Cookie, + * 以此来清除用户登录状态相关的标识信息,确保用户处于未登录状态。 + * @param request 用于接收客户端请求的HttpServletRequest对象,通过它获取客户端当前携带的Cookie信息,以便查找并删除登录相关的Cookie。 + * @param response 用于向客户端发送响应的HttpServletResponse对象,通过它来设置要删除的Cookie的相关属性并发送给客户端,使客户端执行删除操作。 */ - public static void delLoginToken(HttpServletRequest request,HttpServletResponse response){ + public static void delLoginToken(HttpServletRequest request, HttpServletResponse response) { + // 从请求中获取客户端携带的所有Cookie数组,用于查找要删除的登录相关Cookie。 Cookie[] cks = request.getCookies(); - if(cks != null){ - for(Cookie ck:cks) { - if(StringUtils.equals(ck.getName(),COOKIE_NAME)){ + if (cks!= null) { + for (Cookie ck : cks) { + // 通过比较Cookie的名称是否与预先定义的登录相关Cookie名称(COOKIE_NAME)相等,来确定是否是要删除的Cookie。 + if (StringUtils.equals(ck.getName(), COOKIE_NAME)) { + // 设置要删除的Cookie的域名,确保与之前写入时的域名一致,以便准确删除对应的Cookie。 ck.setDomain(COOKIE_DOMAIN); + // 设置Cookie的路径为根目录("/"),与之前写入时的路径设置保持一致,保证删除的是对应路径下的该Cookie。 ck.setPath("/"); - ck.setMaxAge(0);//0表示消除此cookie - log.info("del cookieName:{},cookieBValue:{}",ck.getName(),ck.getValue()); + // 设置Cookie的最大存活时间为0,表示立即删除该Cookie,客户端收到响应后会将该Cookie从本地移除。 + ck.setMaxAge(0); //0表示消除此cookie + // 记录要删除的Cookie的名称和值的日志信息,方便在调试等场景下查看具体的删除操作情况。 + log.info("del cookieName:{},cookieBValue:{}", ck.getName(), ck.getValue()); + // 将配置好的(即将要删除的)Cookie添加到响应中,客户端收到响应后会根据设置删除对应的Cookie。 response.addCookie(ck); return; } @@ -71,4 +99,4 @@ public class CookieUtil { } } -} +} \ No newline at end of file diff --git a/snailmall-api-gateway/src/main/java/com/njupt/swg/utils/DateTimeUtil.java b/snailmall-api-gateway/src/main/java/com/njupt/swg/utils/DateTimeUtil.java index 7b8fed0..90ad040 100644 --- a/snailmall-api-gateway/src/main/java/com/njupt/swg/utils/DateTimeUtil.java +++ b/snailmall-api-gateway/src/main/java/com/njupt/swg/utils/DateTimeUtil.java @@ -4,65 +4,117 @@ import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; - import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /** * @DESC 时间转换的工具类 + * 该类提供了一系列用于时间相关转换的实用方法,主要借助Joda-Time库以及Java原生的日期时间处理类, + * 实现字符串与日期对象之间的相互转换,以及日期对象与时间戳之间的转换功能,方便在项目中统一处理时间相关的格式化操作。 */ public class DateTimeUtil { //joda-time + // 用于说明该工具类在处理时间相关操作时使用了Joda-Time库,它是一个功能强大的日期时间处理库,能更方便地进行各种日期时间的操作和格式化等工作。 //str->Date //Date->str - public static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss"; - + // 注释说明该工具类提供了将字符串转换为日期对象以及将日期对象转换为字符串这两种方向的时间转换功能,方便在不同业务场景下对时间数据进行处理。 + public static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss"; + // 定义了一个标准的日期时间格式字符串常量,用于在没有指定特定格式时,作为默认的日期时间格式化模板,符合常见的年-月-日 时:分:秒的格式表示。 - public static Date strToDate(String dateTimeStr, String formatStr){ + /** + * 将指定格式的字符串转换为日期对象的方法。 + * 它使用Joda-Time库的DateTimeFormatter根据传入的格式字符串创建格式化器,然后解析输入的日期时间字符串, + * 最后将解析得到的DateTime对象转换为Java原生的Date对象并返回。 + * + * @param dateTimeStr 要转换的日期时间字符串,其格式需要与传入的formatStr参数相匹配。 + * @param formatStr 用于指定日期时间字符串的格式模板,例如"yyyy-MM-dd HH:mm:ss"等格式定义字符串。 + * @return 转换后的Java原生Date对象,如果解析过程出现问题可能会抛出异常(由Joda-Time库内部处理或者向上传播)。 + */ + public static Date strToDate(String dateTimeStr, String formatStr) { DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(formatStr); DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr); return dateTime.toDate(); } - public static String dateToStr(Date date,String formatStr){ - if(date == null){ + /** + * 将日期对象转换为指定格式的字符串的方法。 + * 首先判断传入的日期对象是否为null,如果是null则返回一个空字符串, + * 若不为null,则使用Joda-Time库创建对应日期的DateTime对象,再按照传入的格式字符串将其转换为相应格式的字符串并返回。 + * + * @param date 要转换的Java原生Date对象,代表一个具体的日期时间点。 + * @param formatStr 用于指定转换后的字符串格式模板,决定了最终返回的日期时间字符串的格式样式。 + * @return 转换后的日期时间字符串,如果传入的日期对象为null则返回空字符串,否则返回按照指定格式转换后的字符串。 + */ + public static String dateToStr(Date date, String formatStr) { + if (date == null) { return StringUtils.EMPTY; } DateTime dateTime = new DateTime(date); return dateTime.toString(formatStr); } - //固定好格式 - public static Date strToDate(String dateTimeStr){ + /** + * 将符合标准格式("yyyy-MM-dd HH:mm:ss")的字符串转换为日期对象的方法。 + * 它使用预先定义的标准格式字符串常量(STANDARD_FORMAT)创建DateTimeFormatter格式化器, + * 然后解析输入的日期时间字符串,最后将解析得到的DateTime对象转换为Java原生的Date对象并返回。 + * + * @param dateTimeStr 要转换的日期时间字符串,其格式需要符合"yyyy-MM-dd HH:mm:ss"标准格式要求。 + * @return 转换后的Java原生Date对象,如果解析过程出现问题可能会抛出异常(由Joda-Time库内部处理或者向上传播)。 + */ + public static Date strToDate(String dateTimeStr) { DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(STANDARD_FORMAT); DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr); return dateTime.toDate(); } - public static String dateToStr(Date date){ - if(date == null){ + /** + * 将日期对象转换为标准格式("yyyy-MM-dd HH:mm:ss")的字符串的方法。 + * 首先判断传入的日期对象是否为null,如果是null则返回一个空字符串, + * 若不为null,则使用Joda-Time库创建对应日期的DateTime对象,再按照标准格式将其转换为相应格式的字符串并返回。 + * + * @param date 要转换的Java原生Date对象,代表一个具体的日期时间点。 + * @return 转换后的日期时间字符串,如果传入的日期对象为null则返回空字符串,否则返回按照标准格式"yyyy-MM-dd HH:mm:ss"转换后的字符串。 + */ + public static String dateToStr(Date date) { + if (date == null) { return StringUtils.EMPTY; } DateTime dateTime = new DateTime(date); return dateTime.toString(STANDARD_FORMAT); } - //Date -> 时间戳 + /** + * 将日期对象转换为时间戳(从1970年1月1日00:00:00 UTC到指定日期时间的毫秒数)的方法。 + * 首先判断传入的日期对象是否为null,如果是null则返回null, + * 若不为null,则使用Java原生的SimpleDateFormat按照指定的标准格式("yyyy-MM-dd HH:mm:ss")创建格式化器, + * 先将日期对象转换为字符串再解析为Date对象,最后获取其对应的时间戳(以毫秒为单位)并返回。 + * + * @param date 要转换的Java原生Date对象,代表一个具体的日期时间点。 + * @return 转换后的时间戳(Long类型,表示从1970年1月1日00:00:00 UTC到指定日期时间的毫秒数),如果传入的日期对象为null则返回null, + * 若在转换过程中出现解析等问题则会抛出ParseException异常(需要调用者处理)。 + */ public static Long dateToChuo(Date date) throws ParseException { - if(date == null){ + if (date == null) { return null; } - SimpleDateFormat format = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return format.parse(String.valueOf(date)).getTime(); } + /** + * 主方法,用于简单测试该工具类中的时间转换功能,这里以将一个指定的日期时间字符串解析为Date对象,然后获取其时间戳并打印输出为例, + * 可以在开发过程中直接运行该类来验证部分功能是否符合预期,但实际应用中可能需要更完善的单元测试等方式来全面测试功能的正确性。 + * + * @param args 命令行参数,在这个简单测试场景下未使用到。 + * @throws ParseException 如果在解析日期时间字符串为Date对象过程中出现格式不匹配等问题,则会抛出该异常,由Java虚拟机向上传播处理。 + */ public static void main(String[] args) throws ParseException { - SimpleDateFormat format = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ); - String time="1970-01-06 11:45:55"; + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String time = "1970-01-06 11:45:55"; Date date = format.parse(time); - System.out.print("Format To times:"+date.getTime()); + System.out.print("Format To times:" + date.getTime()); } -} +} \ No newline at end of file diff --git a/snailmall-api-gateway/src/main/java/com/njupt/swg/utils/JsonUtil.java b/snailmall-api-gateway/src/main/java/com/njupt/swg/utils/JsonUtil.java index 135096d..30b873a 100644 --- a/snailmall-api-gateway/src/main/java/com/njupt/swg/utils/JsonUtil.java +++ b/snailmall-api-gateway/src/main/java/com/njupt/swg/utils/JsonUtil.java @@ -8,119 +8,154 @@ import org.codehaus.jackson.map.SerializationConfig; import org.codehaus.jackson.map.annotate.JsonSerialize; import org.codehaus.jackson.type.JavaType; import org.codehaus.jackson.type.TypeReference; - import java.io.IOException; import java.text.SimpleDateFormat; /** * jackson的序列化和反序列化 + * 该类是一个工具类,主要基于Jackson库提供了一系列用于对象序列化(将对象转换为JSON字符串)和反序列化(将JSON字符串转换为对象)的方法, + * 同时在类的初始化阶段对Jackson的ObjectMapper进行了一些配置,以满足项目中特定的JSON处理需求,例如统一日期格式、忽略某些转换错误等。 */ @Slf4j public class JsonUtil { + // 创建一个ObjectMapper对象,它是Jackson库中用于进行JSON序列化和反序列化操作的核心类,后续的各种序列化和反序列化操作都依赖它来实现。 private static ObjectMapper objectMapper = new ObjectMapper(); static { //所有字段都列入进行转换 + // 设置ObjectMapper在序列化时的包含规则,这里设置为JsonSerialize.Inclusion.ALWAYS,表示所有的字段都会被包含进序列化的结果中, + // 即使字段的值为null也会进行序列化处理,确保完整的对象结构能在JSON字符串中体现出来。 objectMapper.setSerializationInclusion(JsonSerialize.Inclusion.ALWAYS); //取消默认转换timestamp形式 - objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS,false); + // 配置ObjectMapper在序列化日期类型字段时,取消将日期转换为时间戳的默认行为,这样在序列化后的JSON字符串中日期会以指定的格式呈现,而不是时间戳形式。 + objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false); //忽略空bean转json的错误 - objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS,false); + // 配置ObjectMapper在尝试将一个空的Java对象(没有任何属性值的对象)转换为JSON字符串时,忽略可能出现的错误, + // 使得即使对象为空也能正常进行序列化操作,避免抛出异常导致程序中断。 + objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false); //统一时间的格式 + // 设置ObjectMapper在处理日期类型数据时使用的统一日期格式,通过传入一个SimpleDateFormat对象,指定了格式为DateTimeUtil.STANDARD_FORMAT(在其他地方定义的标准格式), + // 保证序列化和反序列化过程中日期格式的一致性和规范性。 objectMapper.setDateFormat(new SimpleDateFormat(DateTimeUtil.STANDARD_FORMAT)); //忽略json存在属性,但是java对象不存在属性的错误 - objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES,false); + // 配置ObjectMapper在进行反序列化时,忽略JSON字符串中存在但对应的Java对象中不存在的属性所引发的错误, + // 这样在JSON数据结构可能发生变化或者不完全匹配Java对象结构时,依然能够正常进行反序列化操作,提高了程序的兼容性和容错性。 + objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false); } /** * 序列化方法,将对象转为字符串 - * @param obj - * @param - * @return + * 该方法用于将一个Java对象转换为JSON字符串表示形式,如果对象为null则直接返回null, + * 在转换过程中若出现IO异常(例如对象的某些属性无法正常序列化等情况),会记录警告日志并返回null。 + * + * @param obj 要进行序列化的Java对象,可以是任意类型的对象,由泛型参数表示其通用性。 + * @param 泛型参数,用于表示传入对象的类型,确保返回的JSON字符串能正确对应相应类型的对象序列化结果。 + * @return 返回转换后的JSON字符串,如果对象为null或者序列化过程出现异常则返回null。 */ - public static String obj2String(T obj){ - if(obj == null){ + public static String obj2String(T obj) { + if (obj == null) { return null; } try { - return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj); + // 判断传入的对象是否本身就是字符串类型,如果是则直接返回该字符串,否则使用ObjectMapper将对象转换为JSON字符串并返回。 + return obj instanceof String? (String) obj : objectMapper.writeValueAsString(obj); } catch (IOException e) { - log.warn("parse object to string error",e); + log.warn("parse object to string error", e); return null; } } /** * 序列化方法,同上,只是输出的格式是美化的,便于测试 - * @param obj - * @param - * @return + * 该方法与obj2String方法功能类似,也是将Java对象转换为JSON字符串,不同之处在于它会使用美化的格式输出JSON字符串, + * 使得生成的JSON字符串更易于阅读和查看,方便在测试等场景下查看对象序列化后的具体结构,同样在出现异常时会记录警告日志并返回null。 + * + * @param obj 要进行序列化的Java对象,可以是任意类型的对象,由泛型参数表示其通用性。 + * @param 泛型参数,用于表示传入对象的类型,确保返回的JSON字符串能正确对应相应类型的对象序列化结果。 + * @return 返回转换后的JSON字符串,如果对象为null或者序列化过程出现异常则返回null,正常情况下返回美化格式的JSON字符串。 */ - public static String obj2StringPretty(T obj){ - if(obj == null){ + public static String obj2StringPretty(T obj) { + if (obj == null) { return null; } try { - return obj instanceof String ? (String) obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj); + // 使用ObjectMapper的writerWithDefaultPrettyPrinter方法获取一个能够生成美化格式JSON字符串的写入器, + // 然后通过该写入器将对象转换为美化后的JSON字符串并返回。 + return obj instanceof String? (String) obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj); } catch (IOException e) { - log.warn("parse object to string error",e); + log.warn("parse object to string error", e); return null; } } /** * 比较简单的反序列化的方法,将字符串转为单个对象 - * @param str - * @param clazz - * @param - * @return + * 该方法用于将一个JSON字符串反序列化为指定类型的Java对象,在进行反序列化前会先判断字符串是否为空以及指定的目标类是否为null, + * 如果有任意一个为空则直接返回null,若反序列化过程中出现IO异常(例如JSON格式不匹配目标对象结构等情况),会记录警告日志并返回null。 + * + * @param str 要进行反序列化的JSON字符串,包含了要转换为Java对象的数据内容。 + * @param clazz 目标Java对象的类型,通过传入的Class参数明确指定要将JSON字符串反序列化为哪种类型的对象,确保类型的准确性。 + * @param 泛型参数,用于表示要反序列化得到的对象类型,与传入的clazz参数类型相对应。 + * @return 返回反序列化后的Java对象,如果字符串为空、目标类为null或者反序列化过程出现异常则返回null。 */ - public static T String2Obj(String str,Class clazz){ - if(StringUtils.isEmpty(str) || clazz == null){ + public static T String2Obj(String str, Class clazz) { + if (StringUtils.isEmpty(str) || clazz == null) { return null; } try { - return clazz.equals(String.class)?(T)str:objectMapper.readValue(str,clazz); + // 判断传入的目标类是否就是字符串类型的Class对象,如果是则直接将传入的字符串强制转换并返回(因为本身就是字符串无需真正的反序列化操作), + // 否则使用ObjectMapper将JSON字符串按照指定的目标类进行反序列化并返回对应的对象。 + return clazz.equals(String.class)? (T) str : objectMapper.readValue(str, clazz); } catch (IOException e) { - log.warn("parse string to obj error",e); + log.warn("parse string to obj error", e); return null; } } /** * 复杂对象的反序列化(通用) - * @param str - * @param typeReference - * @param - * @return + * 该方法用于处理更复杂的反序列化场景,能够将JSON字符串反序列化为复杂类型的对象(例如包含泛型的集合类型等), + * 通过传入TypeReference类型的参数来指定复杂对象的具体类型信息,同样在字符串为空或者TypeReference为null时会直接返回null, + * 若反序列化出现异常会记录警告日志并返回null。 + * + * @param str 要进行反序列化的JSON字符串,包含了要转换为复杂Java对象的数据内容。 + * @param typeReference 用于指定复杂对象类型的TypeReference对象,通过它可以准确描述包含泛型等复杂结构的对象类型信息,确保反序列化的准确性。 + * @param 泛型参数,用于表示要反序列化得到的复杂对象类型,与传入的typeReference所描述的类型相对应。 + * @return 返回反序列化后的复杂Java对象,如果字符串为空、typeReference为null或者反序列化过程出现异常则返回null。 */ - public static T Str2Obj(String str, TypeReference typeReference){ - if(StringUtils.isEmpty(str) || typeReference == null){ + public static T Str2Obj(String str, TypeReference typeReference) { + if (StringUtils.isEmpty(str) || typeReference == null) { return null; } try { - return (T) (typeReference.getType().equals(String.class)?str:objectMapper.readValue(str,typeReference)); + // 判断传入的TypeReference所表示的类型是否就是字符串类型,如果是则直接返回传入的字符串(无需反序列化操作), + // 否则使用ObjectMapper按照TypeReference指定的复杂类型信息将JSON字符串进行反序列化并返回对应的对象。 + return (T) (typeReference.getType().equals(String.class)? str : objectMapper.readValue(str, typeReference)); } catch (IOException e) { - log.warn("parse string to obj error",e); + log.warn("parse string to obj error", e); return null; } } /** * 第二种方式实现复杂对象的反序列化 - * @param str - * @param collectionClass - * @param elementClasses - * @param - * @return + * 该方法是另一种实现复杂对象反序列化的方式,通过传入集合类型的Class对象以及元素类型的Class对象(可以多个,用于表示集合中元素的类型信息), + * 利用ObjectMapper的类型工厂构建出JavaType对象来准确描述复杂对象的类型结构,然后进行反序列化操作, + * 在出现异常时同样会记录警告日志并返回null。 + * + * @param str 要进行反序列化的JSON字符串,包含了要转换为复杂Java对象的数据内容。 + * @param collectionClass 表示集合类型的Class对象,用于指定要反序列化得到的对象是哪种集合类型(如List、Set等)。 + * @param elementClasses 可变参数,用于指定集合中元素的类型Class对象,可以传入多个,按照顺序依次表示集合中各元素的类型信息。 + * @param 泛型参数,用于表示要反序列化得到的复杂对象类型,与传入的collectionClass和elementClasses所构建的类型结构相对应。 + * @return 返回反序列化后的复杂Java对象,如果反序列化过程出现异常则返回null。 */ - public static T Str2Obj(String str,Class collectionClass,Class... elementClasses){ - JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass,elementClasses); + public static T Str2Obj(String str, Class collectionClass, Class... elementClasses) { + JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClasses); try { - return objectMapper.readValue(str,javaType); + return objectMapper.readValue(str, javaType); } catch (IOException e) { - log.warn("parse string to obj error",e); + log.warn("parse string to obj error", e); return null; } } -} +} \ No newline at end of file