博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
sbc(三)自定义Starter-SpringBoot重构去重插件
阅读量:5937 次
发布时间:2019-06-19

本文共 7109 字,大约阅读时间需要 23 分钟。

前言

之前看过的朋友应该记得我后文说过之后要用SpringBoot来进行重构。

这次采用自定义的starter的方式来进行重构。

关于starter(起步依赖)其实在第一次使用SpringBoot的时候就已经用到了,比如其中的:

org.springframework.boot
spring-boot-starter-web
复制代码

我们只需要引入这一个依赖SpringBoot就会把相关的依赖都加入进来,自己也不需要再去担心各个版本之间的兼容问题(具体使用哪个版本由使用的spring-boot-starter-parent版本决定),这些SpringBoot都已经帮我们做好了。

01.jpg

Spring自动化配置

先加入需要的一些依赖:

org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-aop
org.springframework.boot
spring-boot-starter-redis
org.springframework.boot
spring-boot-configuration-processor
true
com.crossoverJie
sbc-common
1.0.0-SNAPSHOT
复制代码

创建了CheckReqConf配置类用于在应用启动的时候自动配置。

当然前提还得在resources目录下创建META-INF/spring.factories配置文件用于指向当前类,才能在应用启动时进行自动配置。

spring.factories:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.crossoverJie.request.check.conf.CheckReqConf复制代码

使用条件化配置

试着考虑下如下情况:

因为该插件是使用redis来存储请求信息的,外部就依赖了redis。如果使用了该插件的应用没有配置或者忘了配置redis的一些相关连接,那么在应用使用过程中肯定会出现写入redis异常。

如果异常没有控制好的话还有可能影响项目的正常运行。

那么怎么解决这个情况呢,可以使用Spring4.0新增的条件化配置来解决。

解决思路是:可以简单的通过判断应用中是否配置有spring.redis.hostredis连接,如果没有我们的这个配置就会被忽略掉。

实现代码:

import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Conditional;import org.springframework.context.annotation.Configuration;@Configuration@ComponentScan("com.crossoverJie.request.check.interceptor,com.crossoverJie.request.check.properties")//是否有redis配置的校验,如果没有配置则不会加载改配置,也就是当前插件并不会生效@Conditional(CheckReqCondition.class)public class CheckReqConf {}复制代码

具体校验的代码CheckReqCondition:

public class CheckReqCondition implements Condition {    private static Logger logger = LoggerFactory.getLogger(CheckReqCondition.class);    @Override    public boolean matches(ConditionContext context, AnnotatedTypeMetadata annotatedTypeMetadata) {        //如果没有加入redis配置的就返回false        String property = context.getEnvironment().getProperty("spring.redis.host");        if (StringUtils.isEmpty(property)){            logger.warn("Need to configure redis!");            return false ;        }else {            return true;        }    }}复制代码

只需要实现org.springframework.context.annotation.Condition并重写matches()方法,即可实现个人逻辑。

可以在使用了该依赖的配置文件中配置或者是不配置spring.redis.host这个配置,来看我们的切面类(ReqNoDrcAspect)中53行的日志是否有打印来判断是否生效。

这样只有在存在该key的情况下才会应用这个配置。

当然最好的做法是直接尝试读、写redis,看是否连接畅通来进行判断。

AOP切面

最核心的其实就是这个切面类,里边主要逻辑和之前是一模一样的就不在多说,只是这里应用到了自定义配置。

切面类ReqNoDrcAspect:

//切面注解@Aspect//扫描@Component//开启cglib代理@EnableAspectJAutoProxy(proxyTargetClass = true)public class ReqNoDrcAspect {    private static Logger logger = LoggerFactory.getLogger(ReqNoDrcAspect.class);    @Autowired    private CheckReqProperties properties ;    private String prefixReq ;    private long day ;    @Autowired    private RedisTemplate
redisTemplate; @PostConstruct public void init() throws Exception { prefixReq = properties.getRedisKey() == null ? "reqNo" : properties.getRedisKey() ; day = properties.getRedisTimeout() == null ? 1L : properties.getRedisTimeout() ; logger.info("sbc-request-check init......"); logger.info(String.format("redis prefix is [%s],timeout is [%s]", prefixReq, day)); } /** * 切面该注解 */ @Pointcut("@annotation(com.crossoverJie.request.check.anotation.CheckReqNo)") public void checkRepeat(){ } @Before("checkRepeat()") public void before(JoinPoint joinPoint) throws Exception { BaseRequest request = getBaseRequest(joinPoint); if(request != null){ final String reqNo = request.getReqNo(); if(StringUtil.isEmpty(reqNo)){ throw new SBCException(StatusEnum.REPEAT_REQUEST); }else{ try { String tempReqNo = redisTemplate.opsForValue().get(prefixReq +reqNo); logger.debug("tempReqNo=" + tempReqNo); if((StringUtil.isEmpty(tempReqNo))){ redisTemplate.opsForValue().set(prefixReq + reqNo, reqNo, day, TimeUnit.DAYS); }else{ throw new SBCException("请求号重复,"+ prefixReq +"=" + reqNo); } } catch (RedisConnectionFailureException e){ logger.error("redis操作异常",e); throw new SBCException("need redisService") ; } } } } public static BaseRequest getBaseRequest(JoinPoint joinPoint) throws Exception { BaseRequest returnRequest = null; Object[] arguments = joinPoint.getArgs(); if(arguments != null && arguments.length > 0){ returnRequest = (BaseRequest) arguments[0]; } return returnRequest; }}复制代码

这里我们的写入rediskey的前缀和过期时间改为从CheckReqProperties类中读取:

@Component//定义配置前缀@ConfigurationProperties(prefix = "sbc.request.check")public class CheckReqProperties {    private String redisKey ;//写入redis中的前缀    private Long redisTimeout ;//redis的过期时间 默认是天    public String getRedisKey() {        return redisKey;    }    public void setRedisKey(String redisKey) {        this.redisKey = redisKey;    }    public Long getRedisTimeout() {        return redisTimeout;    }    public void setRedisTimeout(Long redisTimeout) {        this.redisTimeout = redisTimeout;    }    @Override    public String toString() {        return "CheckReqProperties{" +                "redisKey='" + redisKey + '\'' +                ", redisTimeout=" + redisTimeout +                '}';    }}复制代码

这样如果是需要很多配置的情况下就可以将内容封装到该对象中,方便维护和读取。

使用的时候只需要在自己应用的application.properties中加入

# 去重配置sbc.request.check.redis-key = reqsbc.request.check.redis-timeout= 2复制代码

应用插件

使用方法也和之前差不多(在应用):

  • 加入依赖:
com.crossoverJie.request.check
request-check
1.0.0-SNAPSHOT
复制代码
  • 在接口上加上注解:
@RestController@Api(value = "orderApi", description = "订单API", tags = {
"订单服务"})public class OrderController implements OrderService{ private final static Logger logger = LoggerFactory.getLogger(OrderController.class); @Override @CheckReqNo public BaseResponse
getOrderNo(@RequestBody OrderNoReqVO orderNoReq) { BaseResponse
res = new BaseResponse(); res.setReqNo(orderNoReq.getReqNo()); if (null == orderNoReq.getAppId()){ throw new SBCException(StatusEnum.FAIL); } OrderNoResVO orderNoRes = new OrderNoResVO() ; orderNoRes.setOrderId(DateUtil.getLongTime()); res.setCode(StatusEnum.SUCCESS.getCode()); res.setMessage(StatusEnum.SUCCESS.getMessage()); res.setDataBody(orderNoRes); return res ; }}复制代码

使用效果如下:

02.jpg
03.jpg

总结

注意一点是spring.factories的路径不要搞错了,之前就是因为路径写错了,导致自动配置没有加载,AOP也就没有生效,排查了好久。。

项目:

博客:。

weixinchat.jpg

转载地址:http://duttx.baihongyu.com/

你可能感兴趣的文章
IO流的应用————小型资源管理器
查看>>
命令行编译器vbc.exe和csc.exe的使用
查看>>
通用存储过程.分页存储过程
查看>>
查看linux版本
查看>>
[Django学习] Django基础(6)_Field lookups
查看>>
find 命令练习
查看>>
Spring第一部分
查看>>
nova image-list 和 glance image-list 有什么区别
查看>>
导航栏4种效果---原生js
查看>>
同源策略引发对跨域jsonp跨域的理解
查看>>
PAT1138 Postorder Traversal(树的遍历)
查看>>
I.MX6 ifconfig: SIOCSIFHWADDR: Cannot assign requested address
查看>>
CAS与MVC集成下的“循环重定向”分析
查看>>
稀疏向量的一些内容
查看>>
使用grunt实现web自动化
查看>>
事件委托(代理)
查看>>
使用JavaScript获取CSS伪元素属性
查看>>
正则化
查看>>
javascript弹窗
查看>>
结对编程项目作业2-结对编项目设计文档
查看>>