工作中经常碰到一个场景:数据需要经过一系列处理后入库。最近碰巧遇到,感觉可以这个模式,就封装了一个。比较简单,直接上代码。
代码
AbstractBizHandler
使用了泛型,归属同一业务类型的责任链,子类实现时指定需要处理的数据类型。
package com.commons.chain.handler;
/**
* AbstractBizHandler<br>
* 业务处理抽象类,子类继承实现具体处理业务
*
* 使用责任链模式实现具体逻辑
*/
public abstract class AbstractBizHandler<T> {
/**
* 业务链中下一个业务逻辑
*/
private AbstractBizHandler successor;
/**
* 模板方法,定义业务逻辑
*
* @param dto 子类实现泛型传入在整个处理链中需要的数据
* @throws Exception 处理异常时返回
*/
@SuppressWarnings("unchecked")
public void handler(T dto) throws Exception {
// 具体处理逻辑
preHandler(dto);
// 责任链还有待处理业务则继续
if (getSuccessor() != null) {
getSuccessor().handler(dto);
}
}
/**
* 子类实现具体处理逻辑
*/
protected abstract void preHandler(T dto) throws Exception;
/**
* 子类实现,数值大小决定在每个责任链中位置
* 数值越大,在责任链中位置越靠后
*
* @return 位置大小
*/
public abstract Integer getOrder();
public void setSuccessor(AbstractBizHandler successor) {
this.successor = successor;
}
private AbstractBizHandler getSuccessor() {
return successor;
}
}
注解BizType
注解标识子类实现归属的业务责任链。
package com.commons.chain.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* BizType<br>
* 业务类型
*
* 该注解加载{@link com.commons.chain.handler.AbstractBizHandler}的子类实现上,用于标识所归属的业务处理链
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BizType {
/**
* 用于标识不同业务处理链
* 数组形式,不同的业务可以共享同一个业务处理逻辑
*/
String[] value();
}
BizChainUtil
使用了spring的一个特性,需要引入spring的包。
package com.commons.chain.util;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import com.commons.chain.annotation.BizType;
import com.commons.chain.handler.AbstractBizHandler;
/**
* BizChainUtil<br>
* 业务链帮助类
*
* 基于spring注解,包扫描必须包含该package
*/
@Component
public class BizChainUtil implements ApplicationListener {
private static final Logger LOGGER = LoggerFactory.getLogger(BizChainUtil.class);
/**
* 所有业务实现类,主键为spring中获取的beanName
*/
private final static ConcurrentMap<String, AbstractBizHandler> CACHED_INSTANCES = new ConcurrentHashMap<String, AbstractBizHandler>();
/**
* 按业务分组后业务实现类,主键为业务名称
*/
private final static Map<String, List<AbstractBizHandler>> CACHED_BIZ_CHAIN = new ConcurrentHashMap<String, List<AbstractBizHandler>>();
/**
* 按业务分组后业链,主键为业务名称
*/
private final static Map<String, AbstractBizHandler> BIZ_CHAIN = new ConcurrentHashMap<String, AbstractBizHandler>();
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
if (((ContextRefreshedEvent) event).getApplicationContext().getParent() == null) {
ApplicationContext applicationContext = ((ContextRefreshedEvent) event).getApplicationContext();
// 从spring中获取所有业务逻辑实现类
Map<String, AbstractBizHandler> beans = applicationContext.getBeansOfType(AbstractBizHandler.class, true, false);
// 构建业务链
buildBizChain(beans);
}
}
}
/**
* 构建业务链
*
* @param beans 业务实现类
*/
private void buildBizChain(Map<String, AbstractBizHandler> beans) {
for (Map.Entry<String, AbstractBizHandler> entry : beans.entrySet()) {
String beanName = entry.getKey();
AbstractBizHandler handler = entry.getValue();
if (handler.getClass().isAnnotationPresent(BizType.class)) {
// 缓存所有业务实现类
CACHED_INSTANCES.putIfAbsent(beanName, handler);
// 获取实现类所属业务,按业务归属分组
List<String> allBizTypeName = Arrays.asList(handler.getClass().getAnnotation(BizType.class).value());
for (String bizTypeName : allBizTypeName){
List<AbstractBizHandler> bizHandlerList = CACHED_BIZ_CHAIN.get(bizTypeName);
if (bizHandlerList == null || bizHandlerList.isEmpty()) {
bizHandlerList = new LinkedList<AbstractBizHandler>();
bizHandlerList.add(handler);
CACHED_BIZ_CHAIN.put(bizTypeName, bizHandlerList);
} else {
bizHandlerList.add(handler);
}
}
}
}
// 重构业务链
refreshAllBizChain();
}
/**
* 刷新所有业务链
*/
public static void refreshAllBizChain() {
if (!CACHED_BIZ_CHAIN.isEmpty()) {
for (Map.Entry<String, List<AbstractBizHandler>> entry : CACHED_BIZ_CHAIN.entrySet()) {
String bizTypeName = entry.getKey();
List<AbstractBizHandler> bizHandlerList = entry.getValue();
refreshBizChain(bizTypeName, bizHandlerList);
}
}
}
/**
* 刷新指定业务链
*
* @param bizTypeName 业务类型名称
*/
public static void refreshSpecailBizChain(String bizTypeName) {
List<AbstractBizHandler> bizHandlerList = CACHED_BIZ_CHAIN.get(bizTypeName);
if (bizHandlerList == null || bizHandlerList.isEmpty()){
LOGGER.error("指定的业务类型{}不存在",bizTypeName);
return;
}
refreshBizChain(bizTypeName, bizHandlerList);
}
/**
* 手动添加业务实现类到指定业务链
* 添加后会自动触发刷新构建新的业务链
*
* @param bizTypeName 业务类型名称
* @param bizHandler 业务处理
*/
public static void addBizHandler(String bizTypeName, AbstractBizHandler bizHandler) {
List<AbstractBizHandler> bizHandlerList = CACHED_BIZ_CHAIN.get(bizTypeName);
if (bizHandlerList == null || bizHandlerList.isEmpty()){
LOGGER.error("指定的业务类型{}不存在",bizTypeName);
return;
}
bizHandlerList.add(bizHandler);
refreshBizChain(bizTypeName, bizHandlerList);
}
/**
* 获取所有业务链
*/
public static Map<String, AbstractBizHandler> getAllBizChain() {
if (BIZ_CHAIN.isEmpty()){
LOGGER.error("业务链还未初始化");
return null;
}
return BIZ_CHAIN;
}
/**
* 获取指定业务链
*
* @param bizTypeName 业务类型名称
*/
public static AbstractBizHandler getSpecailBizChain(String bizTypeName) {
AbstractBizHandler baseBizHandler = BIZ_CHAIN.get(bizTypeName);
if (baseBizHandler == null){
LOGGER.error("指定的业务类型{}不存在",bizTypeName);
return null;
}
return baseBizHandler;
}
/**
* 刷新指定业务链
*
* @param bizTypeName 业务类型名称
*/
private static void refreshBizChain(String bizTypeName, List<AbstractBizHandler> bizHandlerList) {
if (!bizHandlerList.isEmpty()) {
// 按照order排序,数值越小,优先执行
Collections.sort(bizHandlerList, new Comparator<AbstractBizHandler>() {
@Override
public int compare(AbstractBizHandler o1, AbstractBizHandler o2) {
return o1.getOrder().compareTo(o2.getOrder());
}
});
// 组装业务处理责任链
AbstractBizHandler baseHandler = null;
for (int i = bizHandlerList.size() - 1; i >= 0; i--) {
final AbstractBizHandler last = bizHandlerList.get(i);
last.setSuccessor(baseHandler);
baseHandler = last;
}
BIZ_CHAIN.put(bizTypeName, baseHandler);
}
}
}
applicationConsumer.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
">
<context:component-scan base-package="com.commons.chain.util" />
<context:component-scan base-package="com.commons.chain.demo.handler" />
<context:annotation-config/>
</beans>
demo
2条实现抽象类的业务链。
第一条业务链
CustomerRequest.java
package com.commons.chain.demo.handler.filter;
/**
* CustomerRequest<br>
*
*/
public class CustomerRequest {
private String name;
private String age;
public CustomerRequest(){}
public CustomerRequest(String name, String age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
@Override
public String toString() {
return "CustomerRequest{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
}
AgeFilter.java
package com.commons.chain.demo.handler.filter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.commons.chain.annotation.BizType;
import com.commons.chain.handler.AbstractBizHandler;
/**
* AgeFilter<br>
*
*/
@BizType("filter")
@Service
public class AgeFilter extends AbstractBizHandler<CustomerRequest> {
private static final Logger LOGGER = LoggerFactory.getLogger(AgeFilter.class);
@Override
protected void preHandler(CustomerRequest dto) throws Exception {
if (dto.getAge() == null){
throw new Exception("请求年龄为null");
}
LOGGER.info("请求年龄{}", dto.getAge());
}
@Override
public Integer getOrder() {
return 2;
}
}
NameFilter.java
package com.commons.chain.demo.handler.filter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.commons.chain.annotation.BizType;
import com.commons.chain.handler.AbstractBizHandler;
/**
* NameFilter<br>
*
*/
@BizType("filter")
@Service
public class NameFilter extends AbstractBizHandler<CustomerRequest> {
private static final Logger LOGGER = LoggerFactory.getLogger(NameFilter.class);
@Override
protected void preHandler(CustomerRequest dto) throws Exception {
if (dto.getName() == null){
throw new Exception("请求名称为null");
}
LOGGER.info("请求姓名{}", dto.getName());
}
@Override
public Integer getOrder() {
return 1;
}
}
第二条业务链
Policy.java
package com.commons.chain.demo.handler.policy;
/**
* Policy<br>
*
*/
public class Policy {
private String type;
private String insureDate;
public Policy() {
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getInsureDate() {
return insureDate;
}
public void setInsureDate(String insureDate) {
this.insureDate = insureDate;
}
@Override
public String toString() {
return "Policy{" +
"type='" + type + '\'' +
", insureDate='" + insureDate + '\'' +
'}';
}
}
CheckPolicy.java
package com.commons.chain.demo.handler.policy;
import org.springframework.stereotype.Service;
import com.commons.chain.annotation.BizType;
import com.commons.chain.handler.AbstractBizHandler;
/**
* CheckPolicy<br>
*
*/
@BizType("policy")
@Service
public class CheckPolicy extends AbstractBizHandler<Policy> {
@Override
protected void preHandler(Policy dto) throws Exception {
if (dto == null || dto.getType() == null || dto.getInsureDate() == null){
throw new Exception("入参为空");
}
}
@Override
public Integer getOrder() {
return 1;
}
}
SavePolicy.java
package com.commons.chain.demo.handler.policy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.commons.chain.annotation.BizType;
import com.commons.chain.demo.handler.filter.AgeFilter;
import com.commons.chain.handler.AbstractBizHandler;
/**
* SavePolicy<br>
*
*/
@BizType("policy")
@Service
public class SavePolicy extends AbstractBizHandler<Policy> {
private static final Logger LOGGER = LoggerFactory.getLogger(AgeFilter.class);
@Override
protected void preHandler(Policy dto) throws Exception {
LOGGER.info("入库成功");
}
@Override
public Integer getOrder() {
return 2;
}
}
test
BizChainTest.java
package com.commons.chain.demo.test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.commons.chain.demo.handler.filter.CustomerRequest;
import com.commons.chain.demo.handler.policy.Policy;
import com.commons.chain.handler.AbstractBizHandler;
import com.commons.chain.util.BizChainUtil;
/**
* BizChainTest<br>
*
*/
public class BizChainTest {
public static void main(String[] args){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
new String[] { "applicationConsumer.xml" });
AbstractBizHandler<CustomerRequest> filterChain = BizChainUtil.getSpecailBizChain("filter");
CustomerRequest request = new CustomerRequest();
try {
if (filterChain != null){
filterChain.handler(request);
}
} catch (Exception e) {
System.out.println("过滤业务链调用异常:"+e.getMessage());
}
try {
request.setName("chris");
if (filterChain != null){
filterChain.handler(request);
}
} catch (Exception e) {
System.out.println("过滤业务链调用异常:"+e.getMessage());
}
AbstractBizHandler<Policy> policyChain = BizChainUtil.getSpecailBizChain("policy");
Policy policy = new Policy();
try {
if (policyChain != null){
policyChain.handler(policy);
}
} catch (Exception e) {
System.out.println("保单业务链调用异常:"+e.getMessage());
}
try {
policy.setType("延迟保修");
policy.setInsureDate("2018-01-30");
if (policyChain != null){
policyChain.handler(policy);
}
System.out.println("保单入库成功");
} catch (Exception e) {
System.out.println("保单业务链调用异常:"+e.getMessage());
}
}
}
总结
好像代码贴完了,没什么写的了,说一个写时出现的问题,BizChainUtil
中有一段代码:
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
if (((ContextRefreshedEvent) event).getApplicationContext().getParent() == null) {
// ....
}
}
}
当时项目中使用时候第一版没这么写,用的是下面这样做的判断:
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (ContextRefreshedEvent.class.getName().equals(event.getClass().getName())) {
....
}
结果导致业务链代码运行时偶发出现栈溢出,碰巧代码运行到框架的dao层调用,然后傻不拉几就去找了公司组件框架的负责人,最后也没找出来。
后来发现是spring的context问题,spring的root applicationcontext和mvc的dispatch Servlet 里面context,都会调用这边,后面就改成上面那一版。