分类 Spring 中的文章

WebService

SpringBoot提供webService服务 1.编码 1.1第一步:引入maven依赖 <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-spring-boot-starter-jaxws</artifactId> <version>3.2.5</version> </dependency> 1.2第二步:创建CXF配置类 import com.shizhongcai.webservice.server.advertise.*; import org.apache.cxf.Bus; import org.apache.cxf.bus.spring.SpringBus; import org.apache.cxf.jaxws.EndpointImpl; import org.apache.cxf.transport.servlet.CXFServlet; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.annotation.Resource; import javax.xml.ws.Endpoint; @Configuration public class CXFConfig { /** * 这里需要注意 由于springmvc 的核心类 为DispatcherServlet * 此处若不重命名此bean的话 原本的mvc就被覆盖了。可查看配置类:DispatcherServletAutoConfiguration * 一种方法是修改方法名称 或者指定bean名称 * 这里需要注意 若beanName命名不是 cxfServletRegistration 时,会创建两个CXFServlet的。 * 具体可查看下自动配置类:Declaration org.apache.cxf.spring.boot.autoconfigure.CxfAutoConfiguration * 也可以不设置此bean 直接通过配置项 cxf.path 来修改访问路径的 * @return */ @Bean("cxfServletRegistration") public ServletRegistrationBean dispatcherServlet() { //注册servlet 拦截services 开头的请求 不设置 默认为:/services/* return new ServletRegistrationBean(new CXFServlet(), "/services/*"); } @Resource private AdvertiseService advertiseService; @Bean(name = Bus.……

阅读全文

雪花算法

Java静态内部类实现雪花算法(SnowFlake) 前言 在生成表主键时,通常可以考虑自增主键/UUID/雪花算法 - 自增主键: - 1.容易被爬虫遍历数据。 - 2.分库分表可能会有ID冲突。 - UUID - 1.太长,有索引碎片 - 2.无序 - 雪花算法 - 适合在分布式场景下生成唯一ID,保证唯一可排序 原理 SnowFlake算法生成ID的是一个64bit大小的整数。 由于在Java中64bit的整数是long类型,所以在Java中SnowFlake算法生成的id就是long来存储的。 - id结构如下: 0-0000000000 0000000000 0000000000 0000000000 0-00000-00000-000000000000 算法描述: 1bit 因为二进制中最高位是符号位,1表示负数,0表示正数。生成的ID都是正整数,所以最高位固定为0。 41bit-时间戳 精确到毫秒级,41位的长度可以使用69年。时间位还有一个很重要的作用是可以根据时间进行排序。 10bit-工作机器id,Twitter实现中使用前5位作为数据中心标识,后5位作为机器标识,可以部署2^10 = 1024个节点; 12bit-序列号 这个是用来记录同一个毫秒内产生的不同 id。 >12位(bit)可以表示的最大正整数是2^{12}-1 = 4095,即可以用0、1、2、3、….4094这4095个数字,来表示同一机器同一时间截(毫秒)内产生的4095个ID序号。 实现代码 /** * 描述: Twitter的分布式自增ID雪花算法snowflake (Java版) * **/ public class SnowFlake { /** * 起始的时间戳 * 2021-01-01 00:00:00 */ private final static long START_STMP = 1609430400000L; /** * 每一部分占用的位数 */ private final static long SEQUENCE_BIT = 12; //序列号占用的位数 private final static long MACHINE_BIT = 5; //机器标识占用的位数 private final static long DATACENTER_BIT = 5;//数据中心占用的位数 /** * 每一部分的最大值 */ private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT); private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT); private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT); /** * 每一部分向左的位移 */ private final static long MACHINE_LEFT = SEQUENCE_BIT; private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT; private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT; private long datacenterId; //数据中心 private long machineId; //机器标识 private long sequence = 0L; //序列号 private long lastStmp = -1L;//上一次时间戳 public SnowFlake(long datacenterId, long machineId) { if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) { throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0"); } if (machineId > MAX_MACHINE_NUM || machineId < 0) { throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0"); } this.……

阅读全文

Spring事务的隔离级别和传播行为

简述 在高并发的情况下,MySQL 事务的并发处理会带来几个问题脏读、不可重复读、幻读。由于高并发事务带来这几个问题,所以就产生了事务的隔离级别。 事务是什么 事务是数据库操作的最小工作单元,用户定义的一系列数据库操作作为一个整体一起向系统提交,要么都执行、要么都不执行。是不可分割的工作单元。 > 如果没做好并发控制,可能会造成脏读、不可重复读和幻读等问题。 可交叉程度 脏读Dirty Read(看到的数据则是不正确的) 当一个事务能看见另外一个事务未提交的数据时,就称为脏读。如果这个事务被回滚了而不是提交了,那么其它事务看到的数据则是不正确的,是“脏”的。 Non-repeatable Read(不可重复读) 两次读取到的数据不同 假设事务A读取了一行数据,接下来事务B改变了这行数据,之后事务A再一次读取这行数据,结果就是事务A两次读取到的数据不同。 Phantom Read(幻读) 发现多出来一条数据 假设事务A通过一个 where 条件读取到了一个结果集,事务B这时插入了一条符合事务A的 where 条件的数据,之后事务A通过同样的 where 条件再次查询时,发现多出来一条数据。 1.事务隔离级别(Isolation) JDBC 规范增加了隔离级别,来满足了 SQL:2003 定义的 4 种事务隔离级别。 在安装MySQL时,安装默认的隔离级别就是:可重复读。 可以通过 select @@global.tx_isolation; 来查看当前隔离级别。 隔离级别从最宽松到最严格,排序如下所示: TRANSACTION_NONE(无事务) 这意味着当前的 JDBC 驱动不支持事务,也意味着这个驱动不符合 JDBC 规范。 1.READ_UNCOMMITTED(读未提交) 允许事务看到其它事务修改了但未提交的数据,这意味着有可能是脏读、不可重复读或者、幻读。 2.READ_COMMITTED(读提交) 一个事务在未提交之前,所做的修改不会被其它事务所看见。这能避免脏读,但避免不了不可重复读和幻读。 3.**REPEATABLE_READ(可重复读取) MySQL默认的事务隔离级别 避免了脏读和不可重复读,但幻读**依然是有可能发生的。 4.SERIALIZABLE(序列化) 避免了脏读、不可重复读以及幻读。 2.Propagation事务传播行为 Propagation属性用来枚举事务的传播行为。所谓事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。Spring支持7种事务传播行为,默认为REQUIRED。 1.……

阅读全文

参数校验

通过RestControllerAdvice和ExceptionHandler实现全局异常捕获 起因: 我么希望通过全局统一的异常处理将自定义错误码以json的形式发送给前端。 步骤: 1.定义一个统一结果返回类BaseRspVo import com.shizhongcai.business.common.domain.enums.ErrorCodesEnum; import com.shizhongcai.business.common.exception.BaseException; import lombok.Data; @Data public class BaseRspVo<T> { private boolean success; private String msg; private Integer errorCode; private T data; public BaseRspVo() { } public BaseRspVo (T data){ this.success = true; this.msg= ErrorCodesEnum.SUCCESS.getMsg(); this.errorCode = ErrorCodesEnum.SUCCESS.getCode(); this.data = data; } public BaseRspVo (boolean success, String msg, int errorCode) { this.success = success; this.msg = msg; this.errorCode = errorCode; } public BaseRspVo (boolean success, String msg, int errorCode, T data) { this(success,msg,errorCode); this.……

阅读全文

全局异常处理

通过RestControllerAdvice和ExceptionHandler实现全局异常捕获 起因: 我么希望通过全局统一的异常处理将自定义错误码以json的形式发送给前端。 步骤: 1.定义一个统一结果返回类BaseRspVo import com.shizhongcai.business.common.domain.enums.ErrorCodesEnum; import com.shizhongcai.business.common.exception.BaseException; import lombok.Data; @Data public class BaseRspVo<T> { private boolean success; private String msg; private Integer errorCode; private T data; public BaseRspVo() { } public BaseRspVo (T data){ this.success = true; this.msg= ErrorCodesEnum.SUCCESS.getMsg(); this.errorCode = ErrorCodesEnum.SUCCESS.getCode(); this.data = data; } public BaseRspVo (boolean success, String msg, int errorCode) { this.success = success; this.msg = msg; this.errorCode = errorCode; } public BaseRspVo (boolean success, String msg, int errorCode, T data) { this(success,msg,errorCode); this.……

阅读全文

Filter和Interceptor

#Filter - Filter是javax.servlet包中定义的接口,任何实现Filter接口的类都能称为过滤器。 - 主要用途:字符过滤集、控制权限、防止跨站脚本攻击(XSS)等等。 - 实现: 1. 如果是传统项目,需要在web.xml中配置filter <filter> <description>字符集过滤器</description> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <description>字符集编码</description> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 2. 如果是springBoot项目,可以在定义好自己的filter类之后,需要使用filterRegisterationBean将filter进行注入。 public class MDCFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { } } @Bean public FilterRegistrationBean mdcFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.……

阅读全文

JSR-303参数校验

JSR-303参数校验 步骤 1.创建校验参数类 import lombok.Data; import org.hibernate.validator.constraints.Range; import org.springframework.format.annotation.DateTimeFormat; import javax.validation.constraints.*; import java.util.Date; /** * 参数验证请求参数 * @Author shizhongcai * @Date 2019/11/20 15:35 */ @Data public class ValidatorReqVo{ @NotNull(message = "id不能为空") private Integer id; @NotNull @Future(message = "格式为yyyy-MM-dd,且为将来时间") @DateTimeFormat(pattern = "yyyy-MM-dd") private Date future; @NotNull @DecimalMin(value = "0.1") @DecimalMax(value = "1000") private Double doubleValue; @NotNull @Min(value = 1) @Max(value = 10) private Integer intValue; @NotNull @Range(min = 1,max = 10,message = "最小为1,最大为10") private Integer range; @NotBlank @Email private String email; @NotBlank @Size(min = 10,max = 20,message = "字符串长度在10-20之间") private String strSize; } 2.……

阅读全文

其他

其他一些日常工作中遇到的问题: 其他 1.如果需要在sit环境引入多个配置文件,比如application.yml何application-common.yml,只需要在application.yml中加入spring.profiles.include = common即可。 2.@Scope默认是单例模式,即scope=“singleton”。 另外scope还有prototype、request、session、global session作用域。scope=“prototype”多例 3.@Aspect执行顺序问题 Aspect先执行是随机的,如果需要定义顺序,可以使用@Order注解修饰Aspect类。值越小,优先级越高。 4.获取配置文件中的值 4.1属性获取配置文件的值@Value(“${属性名}”) 4.2将配置文件的属性赋给实体类@ConfigurationProperties(prefix = “my”) 5. ……

阅读全文

基于自定义注解的参数解析器

基于自定义注解的参数解析器 背景 在工作中,常会对接一些第三方应用,通常第三方会采用Rsa+Aes加密来通信,但是每个第三方的加密方式可能不一致。此时得好好规划一下实现方案了。因此我们采用注解+Spring自带了参数解析类,实现自己的参数解析器。 原理 通过HandlerMethodArgumentResolver实现自定义参数解析 实现 步骤1:创建注解,例如创建一个解析Aes加密的注解AesJson import java.lang.annotation.*; @Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface AesJson { } 步骤2:创建加密参数实体类对象 传入的JSON数据格式为{“data”:“xxxxxxxxxx”} import lombok.Data; @Data public class AesBaseParams { private String data; } 步骤3:新建AesArgumentResolver类实现HandlerMethodArgumentResolver import com.alibaba.fastjson.JSON; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.MethodParameter; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; import javax.servlet.http.HttpServletRequest; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URLDecoder; import java.util.HashMap; import java.util.Map; import java.util.Objects; /** * aes加密算法加密的入参,通过@AesJson注解自动解析 */ public class AesArgumentResolver implements HandlerMethodArgumentResolver { private static final Logger logger = LoggerFactory.……

阅读全文

接口限流-令牌桶限流

guava之令牌桶算法实现接口限流 实现步骤 1.创建注解RateLimit import java.lang.annotation.*; /** * 自定义注解:限流令牌桶注解,用以创建令牌桶以及设定令牌桶大小 */ @Inherited @Documented @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RateLimit { double limitNum() default 20; //默认每秒放入桶中20 } 2.创建令牌桶切面RateLimitAspect import com.google.common.util.concurrent.RateLimiter; import com.shizhongcai.business.common.annotation.RateLimit; import com.shizhongcai.business.common.domain.enums.ErrorCodesEnum; import com.shizhongcai.business.common.exception.BaseException; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import java.lang.reflect.Method; import java.util.concurrent.ConcurrentHashMap; /** * 令牌桶切面,桶中无令牌进行服务降级 * */ @Component @Scope @Aspect public class RateLimitAspect { //用来存放不同接口的RateLimiter令牌桶(key为接口名称,value为RateLimiter) //ConcurrentHashMap线程安全 private ConcurrentHashMap<String, RateLimiter> map = new ConcurrentHashMap<>(); //令牌桶 private RateLimiter rateLimiter; @Pointcut("@annotation(com.……

阅读全文