后端简易实验框架
引子
后端开发时,常常需要启动应用,将应用中所需的 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
}
}