springboot 技巧实战:
现在市面有很多springboot的教程,感觉都是常规操作没有太大的意思,这些随便翻翻一些博客就能学到。今天来一下不长规的操作,利用springboot所提供的特性来大大的提高我们的开发规范、以及项目的耦合程度
一、实例一:让yml帮我们配置系统常量
1.1 应用场景:
- 项目的常量的配置文件:例如公众号的appid、secret、消息模板id……
- 项目(单体项目)众多的配置:多数据源、shiro、mybatis、mq、druid、视图配置、redis……,如果配置在一起,上线发布简直是个灾难
1.2 既然有问题,那就有解决方案:
- 把众多的配置文件分离开来:application-redis.yml、application-const.yml、application-db.yml
- 然后在主配置文件:application.yml中引入进来即可,注意格式 - 和具体的名称之间是有一个间隔的
spring:
profiles:
include:
- redis
- db
- const
- shiro
- ……
- 如果对于常量的话,我们在应用中取值方式如下:
// 这个是主配置文件中的
@Value("${cms.test}")
private String test;
// 这个则是通过include引入进来的
@Value("${cms.db.url}")
private String url;
是不是感觉世界瞬间变的清爽了起来呢,这样上线需要修改配置的话,我们就可以一个个的去找具体的配置文件改,不至于漏改的情况。当然也可以使用springboot自带的多环境配置。
##二、实例二:使用springboot事件模式让我们的应用解耦
2.1 应用场景:
很多时候呢,有这这样的场景:在注册的时候需要发送邮件,做订单支付的时候需要扣减用户积分或者是增加用户的积分,同时下发邮件通知商家或者客户端,大家想想这三个业务集合到一起是不是把事务的生命周期延长的很多,同时支付是核心业务是需要优先满足的,二发送邮件通知以及积分的比重就要比支付要小得多,因此要优先保证支付业务的顺利完成,但是假如积分业务或者邮件业务出现异常,整个的支付流程都回滚,这显然是不对的,那么下面就来说说我们的解决方案
1.2 解决方案(关键词:springboot事件模型):
下面呢用语言就难以描述,所以选择直接上代码
UserController.java代码
// 下面的业务非常的简单,就是模拟一个用户注册,简单的信息保存
@PostMapping("/register")
public User register() {
User user = new User();
user.setName("测试一个名字");
user.setAge(12);
userService.save(user);
return user;
}
UserService.java代码
@Autowired
private UserDAO userDAO;
// 我们主要的就是依靠这个来把事件发布出去
@Autowired
public ApplicationEventPublisher publisher;
/**
* 插入user实体
* @param user
* @return
*/
public Integer save(User user){
/**
* 这里面这个参数就是作为事件源来传递的,但是接受的时候一定不能搞个Object来接受,
* 如果在消息监听的地方搞个这个来接受的话,所有的事件发布都会被监听者接受
*/
Integer effectRows = userDAO.save(user);
publisher.publishEvent(user);
return effectRows;
}
ScoreService.java代码
@Autowired
private ScoreDAO scoreDAO;
/**
* 插入积分实体score
* @param score
* @return
*/
public Integer save(Score score){
return scoreDAO.save(score);
}
/**
* 通过这个事件的监听,
*
* 注意这个地方 接受的参数不能是Map、Object……等类型的,如果是这个类型的,有可能你的事件会成为广播事件
* 一旦注册为Object,将响应所有的事件消息
* @param user
*/
@EventListener
public void userRegisterEvent(User user){
Score score = new Score();
score.setUserId(user.getId());
score.setScoreReason("注册赠送积分");
score.setScoreNum(10);
save(score);
}
上面我们将注册业务和(邮件)积分业务分离了开来,但是依旧是有问题的代码,因为一旦积分业务发生异常,注册业务依旧无法成功的执行,
解决的办法就是使用异步来做,下面看改造后的代码
首先需要开启异步执行,需要在启动类上加载@EnableAsync
// 我们把ScoerService的代码模拟异常,然后测试
@EventListener
@Async
public void userRegisterEvent(User user){
Score score = new Score();
score.setUserId(user.getId());
score.setScoreReason("注册赠送积分");
score.setScoreNum(10);
int i = 1/0;
save(score);
}
结果:
1、积分业务报错
2、注册业务正常的执行
上面具体的内容就是这样,使用springboot自带的ApplicationEventPublisher把事件发布出去,然后使用@EventListener来进行监听,
在结合异步来达到一个业务解耦的目的
三、实例三,获取HttpServletRequest
3.1 应用场景:
很多时候我们需要在service中获取与request相关的一些东西,如果把request传来传去就太不优雅了
于是我们可以借助于下面的代码来实现request的获取
RequestAttributes requestsAttributes = RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
HttpServletResponse response = ((ServletRequestAttributes)requestAttributes).getResponse();
通过上面的代码,我们就不必在各个层之间传递request|response了,只需要调用下面的方法代码即可获取到相关的对象;
下面简单的谈一谈它的实现原理,要想知道这个的实现的原理,我们就需要了解一下ThreadLocal这个知识点,
是在springmvc处理包装reqeuest的时候进行设置进去的,具体的方法是:processRequest
四、实例四,修改一个已经装载好的bean
4.1 应用场景:
这个可能是我以后需要去做的一件事儿,为mybatis自动生成单体sql的插件,目前有三种方式:1、使用mybatis自带的拦截器来实现,
2、通过实现ConfigurationCustomizer接口实现customize来实现,获取到Configuration实例,然后替换MapperRegistry的实现类来实现
3、通过实现BeanPostProcessor接口,通过Bean的前置处理器和后置处理器对指定的bean做一些处理 。之前大致看过mybatisplus的扩展mybatis的插件的很是使用用的复写整个sqlsessionfactory的方式,个人认为这种方式侵入性太大,所以后面准备自己撸一个,至于使用哪种方式可能会到时候在实践
下面的代码我们做的一个事情就是:数据库配置文件我们经常写的是用户名和密码,但是这样并不安全,所以我们在配置文件中使用加密带加盐的密码
利用下面的类进行解密,当然目前开源的项目中有这样的一个组件技术:jasypt-spring-boot-starter大家可以去了解一下
@Component
@Slf4j
public class PwdDecodeExtension implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
/**
* 注意这里我们对bean做了修改之后要把这个bean返回回去,这样的话我们修改的bean才会被重新的添加到ioc
* 的容器当中,我们做的修改才会生效
*/
System.err.println(beanName);
if (bean instanceof DruidDataSource){
DruidDataSource dataSource = (DruidDataSource) bean;
String password = dataSource.getPassword();
// 在这里我们可以对这个密码进行一系列的操作,这个怎么去做,相信你自有办法
log.info("数据库的密码=====>{}",password);
}
return bean;
}
}
还有很多很多的应用,例如我们可以扩展SqlSessionFactory的方法,扩展Mybatis的自动生成sql的方法,拦截mybatis的sql打印以及分库……
博客首发地址csdn:https://blog.csdn.net/weixin_42849915
简书地址:https://www.jianshu.com/u/4b48be4cf59f
希望结识更多的大牛一同学习一同进步