后端简易实验框架
引子
后端开发时,常常需要启动应用,将应用中所需的 bean 实例化并注入依赖 OK,而后验证一些新功能。一般的做法是,启动应用后,通过工具或 Curl 命令发送请求来触发 API 接口。有时,要验证的功能不一定通过 API 接口透漏出去,而是内部的业务逻辑。此时,要验证这些业务逻辑,不得不额外开发一些接口来触发调用。这样不够经济。
可以采用一些方法,在 Spring 容器正常启动,且将 bean 实例化并注入依赖 OK 后,自动运行一些实验代码,在实验代码里调用所要验证的服务业务逻辑。
实验框架
定义实验接口
定义实验接口: IExperiment.java
public interface IExperiment {
void test();
}
编写一个实验类
InsertOrderExperiment.java
@Component
public class InsertOrderExperiment implements IExperiment {
private static Log log = LogFactory.getLog(InsertOrderExperiment.class);
@Resource
OrderMapper orderMapper;
@Override
public void test() {
OrderDO orderDO = new OrderDO();
orderDO.setOrderNo("E202009010000001234000003");
orderDO.setUserId(123245L);
orderDO.setShopId(654321L);
orderDO.setState(1);
orderDO.setDeliveryType(DeliveryType.express.getCode());
orderDO.setPrice(100L);
orderDO.setPayWay(PayWay.wxpay.getCode());
orderDO.setBookTime(1609556430L);
Map map = new HashMap<>();
map.put("expressFee", 10L);
orderDO.setExtend(JSON.toJSONString(map));
Integer insertId = orderMapper.insert(orderDO);
log.info("Order InsertId: " + insertId);
}
}
自动加载实验
Experiments.java
@Component
public class Experiments implements ApplicationListener<ContextRefreshedEvent>, ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
doExperiments();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
private void doExperiments() {
Map<String, IExperiment> experiments = applicationContext.getBeansOfType(IExperiment.class);
experiments.forEach(
(key, value) -> {
value.test();
}
);
}
}
ContextRefreshedEvent原理
为什么实现了 ApplicationListener<ContextRefreshedEvent> 会在 context freshed 之后自动调用 onApplicationEvent 方法呢?
ContextRefreshedEvent发布
在 AbstractApplicationContext.java 的 refresh 方法中调用了 finishRefresh 方法:
protected void finishRefresh() {
// Initialize lifecycle processor for this context.
initLifecycleProcessor();
// Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh();
// Publish the final event.
publishEvent(new ContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active.
LiveBeansView.registerApplicationContext(this);
}
protected void publishEvent(Object event, ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Publishing event in " + getDisplayName() + ": " + event);
}
// Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<Object>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
}
}
// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// Publish event via parent context as well...
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
触发监听器
@Override
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(new Runnable() {
@Override
public void run() {
invokeListener(listener, event);
}
});
}
else {
invokeListener(listener, event);
}
}
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event); // 调用 onApplicationEvent 方法
}
catch (ClassCastException ex) {
// code for handling exception
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了