ビジネス オーケストレーションにパイプライン モードを使用する方法について説明する (パート 2)

序文

前回の記事では、パイプライン パターンを使用したビジネス オーケストレーションの2 つの実装を紹介しました。この記事では、その他の実装方法を紹介します

実現方法

方法 1: springboot を使用して自動的に組み立てる

1. 新しいパイプライン エンティティを作成する
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PipelineDefinition {

public static final String PREFIX = "lybgeek_pipeline_";

private String comsumePipelineName;

private List<String> pipelineClassNames;




}
复制代码

}

@Data
@AllArgsConstructor
@NoArgsConstructor
@ConfigurationProperties(prefix = PipelineDefinitionProperties.PREFIX)
public class PipelineDefinitionProperties {



public final static String PREFIX = "lybgeek.pipeline";

private List<PipelineDefinition> chain;




}
复制代码

}

2.自動アセンブリクラスを書く
@Configuration
@EnableConfigurationProperties(PipelineDefinitionProperties.class)
public class PipelineAutoConfiguration implements BeanFactoryAware,InitializingBean, SmartInitializingSingleton {



@Autowired
private PipelineDefinitionProperties pipelineDefinitionProperties;

private DefaultListableBeanFactory defaultListableBeanFactory;


@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {

    defaultListableBeanFactory = (DefaultListableBeanFactory)beanFactory;

}

private void registerPipeline(DefaultListableBeanFactory defaultListableBeanFactory, PipelineDefinition pipelineDefinition) {
    LinkedBlockingDeque linkedBlockingDeque = buildPipelineQuque(pipelineDefinition);
    GenericBeanDefinition beanDefinition = (GenericBeanDefinition) BeanDefinitionBuilder.genericBeanDefinition(ChannelPipeline.class).getBeanDefinition();
    beanDefinition.getPropertyValues().addPropertyValue("channelHandlers",linkedBlockingDeque);
    defaultListableBeanFactory.registerBeanDefinition(PipelineDefinition.PREFIX + pipelineDefinition.getComsumePipelineName() ,beanDefinition);
}

@SneakyThrows
private LinkedBlockingDeque buildPipelineQuque(PipelineDefinition pipelineDefinition) {
    List<String> pipelineClassNames = pipelineDefinition.getPipelineClassNames();
    if(CollectionUtil.isEmpty(pipelineClassNames)){
       throw new PipelineException("pipelineClassNames must config");
    }

    LinkedBlockingDeque linkedBlockingDeque = new LinkedBlockingDeque();
    for (String pipelineClassName : pipelineClassNames) {
        Class<?> pipelineClassClass = Class.forName(pipelineClassName);
        if(!AbstactChannelHandler.class.isAssignableFrom(pipelineClassClass)){
            throw new PipelineException("pipelineClassNames must be 【com.github.lybgeek.pipeline.handler.AbstactChannelHandler】 subclass");
        }
        Object pipeline = pipelineClassClass.getDeclaredConstructor().newInstance();
        linkedBlockingDeque.addLast(pipeline);
    }

    return linkedBlockingDeque;
}

@Override
public void afterPropertiesSet() throws Exception {
    if(CollectionUtil.isNotEmpty(pipelineDefinitionProperties.getChain())){
        for (PipelineDefinition pipelineDefinition : pipelineDefinitionProperties.getChain()) {
            registerPipeline(defaultListableBeanFactory, pipelineDefinition);
        }
    }
}

@Override
public void afterSingletonsInstantiated() {
    Map<String, ChannelPipeline> pipelineBeanMap = defaultListableBeanFactory.getBeansOfType(ChannelPipeline.class);
    pipelineBeanMap.forEach((key,bean)->{
        bean.setHandlerContext(ChannelHandlerContext.getCurrentContext());
    });

}




}
复制代码

}

3. spring.factory と書く
org.springframework.boot.autoconfigure.EnableAutoConfiguration=

com.github.lybgeek.pipeline.spring.autoconfigure.PipelineAutoConfiguration</code>复制代码

业务项目如何使用该方式实现业务编排

示例:

1、创建管道执行器
@Slf4j
public class UserCheckChannelHandler extends AbstactChannelHandler {



@Override
public boolean handler(ChannelHandlerContext chx) {
    ChannelHandlerRequest channelHandlerRequest = chx.getChannelHandlerRequest();
    System.out.println("yml------------------------------------步骤一:用户数据校验【"+channelHandlerRequest.getRequestId()+"】");
    Object params = channelHandlerRequest.getParams();
    if(params instanceof User){
        User user = (User)params;
        if(StringUtils.isBlank(user.getFullname())){
            log.error("用户名不能为空");
            return false;
        }
        return true;
    }


    return false;
}




}
复制代码
@Slf4j
public class UserFillUsernameAndEmailChannelHandler extends AbstactChannelHandler {
@SneakyThrows
@Override
public boolean handler(ChannelHandlerContext chx) {
ChannelHandlerRequest channelHandlerRequest = chx.getChannelHandlerRequest();
System.out.println("yml------------------------------------步骤二:用户名以及邮箱填充【将汉语转成拼音填充】【"+channelHandlerRequest.getRequestId()+"】");
Object params = channelHandlerRequest.getParams();
if(params instanceof User){
User user = (User)params;
String fullname = user.getFullname();
HanyuPinyinOutputFormat hanyuPinyinOutputFormat = new HanyuPinyinOutputFormat();
hanyuPinyinOutputFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
String username = PinyinHelper.toHanYuPinyinString(fullname, hanyuPinyinOutputFormat);
user.setUsername(username);
user.setEmail(username + "@qq.com");
return true;
}



    return false;
}




}
复制代码

。。。其他执行器具体查看链接代码

2、配置yml文件
lybgeek:
pipeline:
chain:
- comsumePipelineName: userYmlService
pipelineClassNames:
- com.github.lybgeek.pipeline.spring.test.yml.handler.UserCheckChannelHandler
- com.github.lybgeek.pipeline.spring.test.yml.handler.UserFillUsernameAndEmailChannelHandler
- com.github.lybgeek.pipeline.spring.test.yml.handler.UserPwdEncryptChannelHandler
- com.github.lybgeek.pipeline.spring.test.yml.handler.UserMockSaveChannelHandler
- com.github.lybgeek.pipeline.spring.test.yml.handler.UserPrintChannleHandler复制代码
3、具体业务service引入管道bean
@Service
public class UserYmlServiceImpl implements UserYmlService {



@Autowired
private ApplicationContext applicationContext;

@Override
public boolean save(User user) {

    ChannelPipeline pipeline =  applicationContext.getBean(ChannelPipeline.class,PipelineDefinition.PREFIX + StringUtils.uncapitalize(UserYmlService.class.getSimpleName()));

    return pipeline.start(ChannelHandlerRequest.builder().params(user).build());
}




}复制代码
4、测试
    @Test
public void testPipelineYml(){
boolean isOk = userYmlService.save(user);
Assert.assertTrue(isOk);



}


复制代码

方式二:利用spring自定义标签

1、定义xsd约束文件pipeline.xsd
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns="http://lybgeek.github.com/schema/pipeline"
targetNamespace="http://lybgeek.github.com/schema/pipeline">



&lt;xsd:import namespace="http://www.w3.org/XML/1998/namespace"/&gt;
&lt;xsd:import namespace="http://www.springframework.org/schema/beans"
            schemaLocation="http://www.springframework.org/schema/beans/spring-beans.xsd"/&gt;
&lt;xsd:import namespace="http://www.springframework.org/schema/tool"/&gt;

&lt;xsd:annotation&gt;
    &lt;xsd:documentation&gt;
        &lt;![CDATA[ Namespace support for pipeline services ]]&gt;&lt;/xsd:documentation&gt;
&lt;/xsd:annotation&gt;


&lt;xsd:complexType name="pipelineType"&gt;
    &lt;xsd:choice&gt;
        &lt;xsd:element ref="pipelineHandler" minOccurs="1" maxOccurs="unbounded"/&gt;
    &lt;/xsd:choice&gt;
    &lt;xsd:attribute name="id" type="xsd:ID"&gt;
        &lt;xsd:annotation&gt;
            &lt;xsd:documentation&gt;&lt;![CDATA[ The unique identifier for a bean. ]]&gt;&lt;/xsd:documentation&gt;
        &lt;/xsd:annotation&gt;
    &lt;/xsd:attribute&gt;
    &lt;xsd:attribute name="consumePipelinesServiceClassName" type="xsd:string" use="required"&gt;
        &lt;xsd:annotation&gt;
            &lt;xsd:documentation&gt;&lt;![CDATA[ consumePipelinesService class name  ]]&gt;&lt;/xsd:documentation&gt;
        &lt;/xsd:annotation&gt;
    &lt;/xsd:attribute&gt;
    &lt;xsd:attribute name="consumePipelinesMethod" type="xsd:string" use="required"&gt;
        &lt;xsd:annotation&gt;
            &lt;xsd:documentation&gt;&lt;![CDATA[ consumePipelinesMethod name  ]]&gt;&lt;/xsd:documentation&gt;
        &lt;/xsd:annotation&gt;
    &lt;/xsd:attribute&gt;
    &lt;xsd:attribute name="argsType" type="xsd:string" use="required"&gt;
        &lt;xsd:annotation&gt;
            &lt;xsd:documentation&gt;&lt;![CDATA[ consumePipelinesMethod args type , multiple args types are separated by commas ]]&gt;&lt;/xsd:documentation&gt;
        &lt;/xsd:annotation&gt;
    &lt;/xsd:attribute&gt;
&lt;/xsd:complexType&gt;

&lt;xsd:complexType name="pipelineHandlerType"&gt;
    &lt;xsd:attribute name="className" type="xsd:string" use="required"&gt;
        &lt;xsd:annotation&gt;
            &lt;xsd:documentation&gt;&lt;![CDATA[ pipelineHanlder class name]]&gt;&lt;/xsd:documentation&gt;
        &lt;/xsd:annotation&gt;
    &lt;/xsd:attribute&gt;
    &lt;xsd:attribute name="order" type="xsd:string" use="required"&gt;
        &lt;xsd:annotation&gt;
            &lt;xsd:documentation&gt;&lt;![CDATA[ pipeline class name]]&gt;&lt;/xsd:documentation&gt;
        &lt;/xsd:annotation&gt;
    &lt;/xsd:attribute&gt;
&lt;/xsd:complexType&gt;



&lt;xsd:element name="pipelineHandler" type="pipelineHandlerType"&gt;
    &lt;xsd:annotation&gt;
        &lt;xsd:documentation&gt;&lt;![CDATA[ The pipelineHandler config ]]&gt;&lt;/xsd:documentation&gt;
    &lt;/xsd:annotation&gt;
&lt;/xsd:element&gt;

&lt;xsd:element name="pipeline" type="pipelineType"&gt;
    &lt;xsd:annotation&gt;
        &lt;xsd:documentation&gt;&lt;![CDATA[ The pipeline config ]]&gt;&lt;/xsd:documentation&gt;
    &lt;/xsd:annotation&gt;
&lt;/xsd:element&gt;




</xsd:schema>
复制代码
2、配置xsd约束文件

在classpath下的resources文件夹新建META-INF文件夹,再建立一个文件spring.schemas,内容如下

http://lybgeek.github.com/schema/pipeline/pipeline.xsd=META-INF/pipeline.xsd复制代码
3、定义解析自定义标签的类
public class PipelineNamespaceHandler extends NamespaceHandlerSupport {



@Override
public void init() {
   registerBeanDefinitionParser("pipeline",new PipelineBeanDefinitionParser());
}




}
复制代码
public class PipelineBeanDefinitionParser implements BeanDefinitionParser {



@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
    PipelineConfig pipelineConfig = buildPipelineConfig(element);
    List&lt;HandlerInvotation&gt; handlerInvotations = this.buildHandlerInvotations(pipelineConfig);
    GenericBeanDefinition beanDefinition = getGenericBeanDefinition(element, parserContext, pipelineConfig, handlerInvotations);
    return beanDefinition;
}

private GenericBeanDefinition getGenericBeanDefinition(Element element, ParserContext parserContext, PipelineConfig pipelineConfig, List&lt;HandlerInvotation&gt; handlerInvotations) {
    GenericBeanDefinition beanDefinition = (GenericBeanDefinition) BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
    beanDefinition.getPropertyValues().addPropertyValue("pipelineServiceClz",pipelineConfig.getConsumePipelinesService());
    beanDefinition.getPropertyValues().addPropertyValue("handlerInvotations",handlerInvotations);
    beanDefinition.getPropertyValues().addPropertyValue("createByXml",true);
    beanDefinition.setBeanClass(ComsumePipelineFactoryBean.class);
    String beanName = BeanUtils.generateBeanName(element,"id",parserContext,pipelineConfig.getConsumePipelinesService().getSimpleName());
    parserContext.getRegistry().registerBeanDefinition(beanName,beanDefinition);
    return beanDefinition;
}

@SneakyThrows
private List&lt;HandlerInvotation&gt; buildHandlerInvotations(PipelineConfig pipelineConfig){
    List&lt;HandlerInvotation&gt; handlerInvotations = new ArrayList&lt;&gt;();
    for (PipelineHandlerConfig pipelineHandlerConfig : pipelineConfig.getPipelineChain()) {
        if(!AbstactChannelHandler.class.isAssignableFrom(pipelineHandlerConfig.getPipelineClass())){
            throw new PipelineException("pipelineHandler className must be 【com.github.lybgeek.pipeline.handler.AbstactChannelHandler】 subclass");
        }

        AbstactChannelHandler channelHandler = (AbstactChannelHandler) pipelineHandlerConfig.getPipelineClass().getDeclaredConstructor().newInstance();
        HandlerInvotation invotation = HandlerInvotation.builder()
                .args(pipelineConfig.getArgs())
                .handler(channelHandler)
                .order(pipelineHandlerConfig.getOrder())
                .consumePipelinesMethod(pipelineConfig.getConsumePipelinesMethod())
                .build();
        handlerInvotations.add(invotation);

    }
    return handlerInvotations;
}

@SneakyThrows
private PipelineConfig buildPipelineConfig(Element element){
    String argsType = element.getAttribute("argsType");
    String[] argsTypeArr = trimArrayElements(commaDelimitedListToStringArray(argsType));
    String consumePipelinesMethod = element.getAttribute("consumePipelinesMethod");
    String consumePipelinesServiceClassName = element.getAttribute("consumePipelinesServiceClassName");


    Class[] args = null;
    if(ArrayUtil.isNotEmpty(argsTypeArr)){
        args = new Class[argsTypeArr.length];
        for (int i = 0; i &lt; argsTypeArr.length; i++) {
            Class argType = Class.forName(argsTypeArr[i]);
            args[i] = argType;
        }
    }

    List&lt;PipelineHandlerConfig&gt; pipelineHandlerConfigs = buildPipelineHandlerConfig(element);

    return PipelineConfig.builder().args(args)
            .consumePipelinesMethod(consumePipelinesMethod)
            .consumePipelinesService(Class.forName(consumePipelinesServiceClassName))
            .pipelineChain(pipelineHandlerConfigs)
            .build();
}

@SneakyThrows
private List&lt;PipelineHandlerConfig&gt; buildPipelineHandlerConfig(Element element){
    NodeList nodeList = element.getChildNodes();
    if (nodeList == null) {
        return Collections.emptyList();
    }

    List&lt;PipelineHandlerConfig&gt; pipelineHandlerConfigs = new ArrayList&lt;&gt;();
    for (int i = 0; i &lt; nodeList.getLength(); i++) {
        if (!(nodeList.item(i) instanceof Element)) {
            continue;
        }
        Element childElement = (Element) nodeList.item(i);
        if ("pipelineHandler".equals(childElement.getNodeName()) || "pipelineHandler".equals(childElement.getLocalName())) {
            String pipelineHanlderClassName = childElement.getAttribute("className");
            String pipelineHanlderOrder = childElement.getAttribute("order");
            Class pipelineHanlderClass = Class.forName(pipelineHanlderClassName);
            PipelineHandlerConfig pipelineHandlerConfig = PipelineHandlerConfig.builder()
                    .PipelineClass(pipelineHanlderClass)
                    .order(Integer.valueOf(pipelineHanlderOrder))
                    .build();
            pipelineHandlerConfigs.add(pipelineHandlerConfig);
        }
    }

    return pipelineHandlerConfigs;
}




}
复制代码
4、注册解析类

在META-INF文件夹新建spring.handlers文件,内容如下

http://lybgeek.github.com/schema/pipeline=com.github.lybgeek.pipeline.spring.shema.PipelineNamespaceHandler复制代码

业务项目如何使用该方式实现业务编排

示例:

1、创建管道执行器
@Slf4j
public class UserCheckChannelHandler extends AbstactChannelHandler {



@Override
public boolean handler(ChannelHandlerContext chx) {
    ChannelHandlerRequest channelHandlerRequest = chx.getChannelHandlerRequest();
    System.out.println("XML------------------------------------步骤一:用户数据校验【"+channelHandlerRequest.getRequestId()+"】");
    String json = JSON.toJSONString(channelHandlerRequest.getParams());
    List&lt;User&gt; users = JSON.parseArray(json,User.class);
    if(CollectionUtil.isEmpty(users) || StringUtils.isBlank(users.get(0).getFullname())){
        log.error("用户名不能为空");
        return false;
    }
    return true;


}




}
复制代码
@Slf4j
public class UserFillUsernameAndEmailChannelHandler extends AbstactChannelHandler {
@SneakyThrows
@Override
public boolean handler(ChannelHandlerContext chx) {
ChannelHandlerRequest channelHandlerRequest = chx.getChannelHandlerRequest();
System.out.println("XML------------------------------------步骤二:用户名以及邮箱填充【将汉语转成拼音填充】【"+channelHandlerRequest.getRequestId()+"】");
String json = JSON.toJSONString(channelHandlerRequest.getParams());
List<User> users = JSON.parseArray(json,User.class);
if(CollectionUtil.isNotEmpty(users)){
User user = users.get(0);
String fullname = user.getFullname();
HanyuPinyinOutputFormat hanyuPinyinOutputFormat = new HanyuPinyinOutputFormat();
hanyuPinyinOutputFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
String username = PinyinHelper.toHanYuPinyinString(fullname, hanyuPinyinOutputFormat);
user.setUsername(username);
user.setEmail(username + "@qq.com");
return true;



    }



    return false;
}




}
复制代码

。。。其他执行器具体查看链接代码

2、定义管道xml
<?xml version="1.0" encoding="UTF-8"?>




<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:lybgeek="http://lybgeek.github.com/schema/pipeline"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://lybgeek.github.com/schema/pipeline http://lybgeek.github.com/schema/pipeline/pipeline.xsd">



&lt;lybgeek:pipeline consumePipelinesServiceClassName="com.github.lybgeek.pipeline.spring.test.xml.service.UserXmlService" consumePipelinesMethod="save" argsType="com.github.lybgeek.pipeline.spring.test.model.User"&gt;
    &lt;lybgeek:pipelineHandler className="com.github.lybgeek.pipeline.spring.test.xml.handler.UserCheckChannelHandler" order="1"/&gt;
    &lt;lybgeek:pipelineHandler className="com.github.lybgeek.pipeline.spring.test.xml.handler.UserFillUsernameAndEmailChannelHandler" order="2"/&gt;
    &lt;lybgeek:pipelineHandler className="com.github.lybgeek.pipeline.spring.test.xml.handler.UserPwdEncryptChannelHandler" order="3"/&gt;
    &lt;lybgeek:pipelineHandler className="com.github.lybgeek.pipeline.spring.test.xml.handler.UserMockSaveChannelHandler" order="4"/&gt;
    &lt;lybgeek:pipelineHandler className="com.github.lybgeek.pipeline.spring.test.xml.handler.UserPrintChannleHandler" order="5"/&gt;
&lt;/lybgeek:pipeline&gt;


复制代码
3、创建业务管道类
public interface UserXmlService {
boolean save(User user);
}
复制代码

直接定义接口即可

4、项目启动类上加上@ImportResource("classpath:/pipeline.xml")
@SpringBootApplication
@ImportResource("classpath:/pipeline.xml")
public class SpringPipelineApplication  {



public static void main(String[] args) {
    SpringApplication.run(SpringPipelineApplication.class);
}




}
复制代码
5、测试
    @Test
public void testPipelineXml(){
boolean isOk = userXmlService.save(user);
Assert.assertTrue(isOk);



}</code></pre><p></p><h2>总结</h2><p>本文的管道模式的核心逻辑内核和上篇文章是一样,只是把管道执行器通过配置文件集中管理起来,这个后续维护也比较不容易出错</p><h2>demo链接</h2><p><a href="https://link.segmentfault.com/?enc=vCBa8EMOH2UK5HgT%2FdaQ4Q%3D%3D.87KfMl3PdP7e0VLkJztNPLPBek5VZuMK%2BMNKEQ9MgNm3uHGpbez6uMD0zEb51H2OBrlQScpQpqlxTGIeo%2BxDpeUqjHKK5z0DS9I%2BRXSwq8k%3D" rel="nofollow">https://github.com/lyb-geek/springboot-learning/tree/master/springboot-pipeline</a></p><blockquote>




本文来源:聊聊如何利用管道模式来进行业务编排(下篇)


复制代码

おすすめ

転載: juejin.im/post/7147723948459819021