A Preliminary Study on the Use and Design of cola Extension Points
Packaging changes provide flexibility to respond to changes in program requirements.
Extension point use
Steps:
Defines an extension point interface, which can be a validator, converter, entity; must end with ExtPt to represent an extension point.
For example, I define an extension point interface for the organizational structure of a cloud hub, a messaging extension point, an open extension point, and a rest interface extension point for a web api.
Define Extension Point Interface
package com.authine.web.cola.domain.customer; import com.alibaba.cola.extension.ExtensionPointI; import com.authine.web.cola.dto.domainmodel.Department; import java.util.List; /** * @author carter * create_date 2020/5/25 14:25 * description Define Extension Point Interfaces, some methods for organizations. */ public interface OrganizationExtPt extends ExtensionPointI { /** * Query all departments under the enterprise based on corpId * * @param corpId Enterprise Number * @param includeDelete Does it contain deleted departments * @return department */ List<Department> getDepartmentsByCorpId(String corpId, Boolean includeDelete); }
For example, business expansion is pinned down by WeChat:
This is based on the extended theory (x,y);
That is, the key of the extension point is derived from the business, use case, and scenario, and then the extension class is the business processing code for the actual business scenario.
Pin Scene Extension Point Implementation
package com.authine.web.cola.domain.customer.extpt; import com.alibaba.cola.extension.Extension; import com.authine.web.cola.dto.domainmodel.Department; import com.authine.web.cola.domain.customer.OrganizationExtPt; import lombok.extern.slf4j.Slf4j; import java.util.Collections; import java.util.List; /** * @author carter * create_date 2020/5/25 14:32 * description Enterprise Departments Nail Expansion in the Scenario of Getting Departments List via corpId */ @Extension(bizId = "organize",useCase = "getByCorpId",scenario = "dingTalk") @Slf4j public class DingTalkOrganizationExt implements OrganizationExtPt { @Override public List<Department> getDepartmentsByCorpId(String corpId, Boolean includeDelete) { log.info("In organizational business, use cases for getting Department lists from enterprise numbers, how business is handled in pinned scenarios"); log.info("Configuration information via pins API Obtain organizational information and assemble departmental information for cloud hub identification"); Department department = new Department(); department.setName("dingTalk"); department.setCorpId(corpId); return Collections.singletonList(department); } }
Enterprise WeChat Extension Point Implementation
package com.authine.web.cola.domain.customer.extpt; import com.alibaba.cola.extension.Extension; import com.authine.web.cola.dto.domainmodel.Department; import com.authine.web.cola.domain.customer.OrganizationExtPt; import lombok.extern.slf4j.Slf4j; import java.util.Collections; import java.util.List; /** * @author carter * create_date 2020/5/25 15:05 * description Extension Point Implementation of Enterprise WeChat */ @Extension(bizId = "organize",useCase = "getByCorpId",scenario = "wechat") @Slf4j public class WechatOrganizationExt implements OrganizationExtPt { @Override public List<Department> getDepartmentsByCorpId(String corpId, Boolean includeDelete) { log.info("Business: Organizational, Use Case: Getting Departments by Enterprise Number , Scenario: Enterprise WeChat"); log.info("Through Enterprise WeChat API Get departmental information for your organization and wrap it in the desired Department list"); Department department = new Department(); department.setName("wechat"); department.setCorpId(corpId); return Collections.singletonList(department); } }
Extension point use
Used in the command executor.
package com.authine.web.cola.executor.query; import com.alibaba.cola.command.Command; import com.alibaba.cola.command.CommandExecutorI; import com.alibaba.cola.dto.MultiResponse; import com.alibaba.cola.extension.ExtensionExecutor; import com.authine.web.cola.dto.domainmodel.Department; import com.authine.web.cola.domain.customer.OrganizationExtPt; import com.authine.web.cola.dto.OrgnizationQry; import java.util.List; /** * @author carter * create_date 2020/5/25 15:09 * description Query organization for instructions execution */ @Command public class OrgazationQueryExe implements CommandExecutorI<MultiResponse, OrgnizationQry> { private final ExtensionExecutor extensionExecutor; public OrgazationQueryExe(ExtensionExecutor extensionExecutor) { this.extensionExecutor = extensionExecutor; } @Override public MultiResponse execute(OrgnizationQry cmd) { String corpId = cmd.getCorpId(); boolean includeDelete = cmd.isIncludeDelete(); List<Department> departmentList = extensionExecutor.execute(OrganizationExtPt.class, cmd.getBizScenario(), ex -> ex.getDepartmentsByCorpId(corpId, includeDelete)); return MultiResponse.ofWithoutTotal(departmentList); } }
Test the use of extension points
Encapsulates an http interface to invoke.
package com.authine.web.cola.controller; import com.alibaba.cola.dto.MultiResponse; import com.alibaba.cola.extension.BizScenario; import com.authine.web.cola.api.OrganizationServiceI; import com.authine.web.cola.dto.OrgnizationQry; import com.authine.web.cola.dto.domainmodel.Department; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; @RestController public class OrganizationController { private final OrganizationServiceI organizationServiceI; public OrganizationController(OrganizationServiceI organizationServiceI) { this.organizationServiceI = organizationServiceI; } @GetMapping(value = "/organization/getDepartmentsByCorpId/{corpId}/{scenario}") public MultiResponse<Department> listCustomerByName(@PathVariable("corpId") String corpId,@PathVariable("scenario") String scenario){ OrgnizationQry qry = new OrgnizationQry(); qry.setCorpId(corpId); qry.setIncludeDelete(true); qry.setBizScenario(BizScenario.valueOf("organize","getByCorpId",scenario)); return organizationServiceI.getDepartmentsByCorpId(qry); } }
Below are the results of testing using interfaces.
Summary
The metadata-based extension point design provides flexibility to cope with the diversity of business scenarios and to support version upgrades. Other extension points (checkers, converters) and others can also be easily extended. Use examples are in the framework's unit test cases.
Extension point design
Design Essence
Design concept.Is a data-based configuration extension.That is, based on the configuration data on the notes.
The @Extension source is as follows:
package com.alibaba.cola.extension; import com.alibaba.cola.common.ColaConstant; import org.springframework.stereotype.Component; import java.lang.annotation.*; @Inherited @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Component public @interface Extension { String bizId() default BizScenario.DEFAULT_BIZ_ID; String useCase() default BizScenario.DEFAULT_USE_CASE; String scenario() default BizScenario.DEFAULT_SCENARIO; }
The illustration is as follows:
The following is a further study of the source code.Start with the source code you are using.
ExtensionExecutor
The class diagram is shown below.
First, the Component is labeled, so you can get instances in ioc by type.
Finally, the execution function is placed in the parent AbstractComponentExecutor;
Focus on the analysis of the functions it achieves: namely, expanding instances through coordinates;
/** * if the bizScenarioUniqueIdentity is "ali.tmall.supermarket" * * the search path is as below: * 1,first try to get extension by "ali.tmall.supermarket", if get, return it. * 2,loop try to get extension by "ali.tmall", if get, return it. * 3,loop try to get extension by "ali", if get, return it. * 4,if not found, try the default extension * @param targetClz */ protected <Ext> Ext locateExtension(Class<Ext> targetClz, BizScenario bizScenario) { checkNull(bizScenario); Ext extension; String bizScenarioUniqueIdentity = bizScenario.getUniqueIdentity(); logger.debug("BizScenario in locateExtension is : " + bizScenarioUniqueIdentity); // first try extension = firstTry(targetClz, bizScenarioUniqueIdentity); if (extension != null) { return extension; } // loop try extension = loopTry(targetClz, bizScenarioUniqueIdentity); if (extension != null) { return extension; } throw new ColaException("Can not find extension with ExtensionPoint: "+targetClz+" BizScenario:"+bizScenarioUniqueIdentity); }
The steps are as follows:
ExtensionRepository
package com.alibaba.cola.extension; import java.util.HashMap; import java.util.Map; import org.springframework.stereotype.Component; import lombok.Getter; /** * ExtensionRepository * @author fulan.zjf 2017-11-05 */ @Component public class ExtensionRepository { @Getter private Map<ExtensionCoordinate, ExtensionPointI> extensionRepo = new HashMap<>(); }
Inside is an empty map, mainly the assembly process.Look at the Extension Register below;
ExtensionRegister
Look at the name, that is, register the components of the extension.
/* * Copyright 2017 Alibaba.com All right reserved. This software is the * confidential and proprietary information of Alibaba.com ("Confidential * Information"). You shall not disclose such Confidential Information and shall * use it only in accordance with the terms of the license agreement you entered * into with Alibaba.com. */ package com.alibaba.cola.boot; import com.alibaba.cola.common.ApplicationContextHelper; import com.alibaba.cola.common.ColaConstant; import com.alibaba.cola.exception.framework.ColaException; import com.alibaba.cola.extension.*; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * ExtensionRegister * @author fulan.zjf 2017-11-05 */ @Component public class ExtensionRegister implements RegisterI{ @Autowired private ExtensionRepository extensionRepository; @Override public void doRegistration(Class<?> targetClz) { ExtensionPointI extension = (ExtensionPointI) ApplicationContextHelper.getBean(targetClz); Extension extensionAnn = targetClz.getDeclaredAnnotation(Extension.class); String extPtClassName = calculateExtensionPoint(targetClz); BizScenario bizScenario = BizScenario.valueOf(extensionAnn.bizId(), extensionAnn.useCase(), extensionAnn.scenario()); ExtensionCoordinate extensionCoordinate = new ExtensionCoordinate(extPtClassName, bizScenario.getUniqueIdentity()); ExtensionPointI preVal = extensionRepository.getExtensionRepo().put(extensionCoordinate, extension); if (preVal != null) { throw new ColaException("Duplicate registration is not allowed for :" + extensionCoordinate); } } /** * @param targetClz * @return */ private String calculateExtensionPoint(Class<?> targetClz) { Class[] interfaces = targetClz.getInterfaces(); if (ArrayUtils.isEmpty(interfaces)) throw new ColaException("Please assign a extension point interface for "+targetClz); for (Class intf : interfaces) { String extensionPoint = intf.getSimpleName(); if (StringUtils.contains(extensionPoint, ColaConstant.EXTENSION_EXTPT_NAMING)) return intf.getName(); } throw new ColaException("Your name of ExtensionPoint for "+targetClz+" is not valid, must be end of "+ ColaConstant.EXTENSION_EXTPT_NAMING); } }
The registration process is as follows:
This is the process of registering extension classes with the extended warehouse.
Registration timing.The start time is registered with the package scan.
RegisterFactory
Place various registers in ioc and return them in a unified way.
/* * Copyright 2017 Alibaba.com All right reserved. This software is the * confidential and proprietary information of Alibaba.com ("Confidential * Information"). You shall not disclose such Confidential Information and shall * use it only in accordance with the terms of the license agreement you entered * into with Alibaba.com. */ package com.alibaba.cola.boot; import com.alibaba.cola.command.Command; import com.alibaba.cola.command.PostInterceptor; import com.alibaba.cola.command.PreInterceptor; import com.alibaba.cola.event.EventHandler; import com.alibaba.cola.extension.Extension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * RegisterFactory * * @author fulan.zjf 2017-11-04 */ @Component public class RegisterFactory{ @Autowired private PreInterceptorRegister preInterceptorRegister; @Autowired private PostInterceptorRegister postInterceptorRegister; @Autowired private CommandRegister commandRegister; @Autowired private ExtensionRegister extensionRegister; @Autowired private EventRegister eventRegister; public RegisterI getRegister(Class<?> targetClz) { PreInterceptor preInterceptorAnn = targetClz.getDeclaredAnnotation(PreInterceptor.class); if (preInterceptorAnn != null) { return preInterceptorRegister; } PostInterceptor postInterceptorAnn = targetClz.getDeclaredAnnotation(PostInterceptor.class); if (postInterceptorAnn != null) { return postInterceptorRegister; } Command commandAnn = targetClz.getDeclaredAnnotation(Command.class); if (commandAnn != null) { return commandRegister; } Extension extensionAnn = targetClz.getDeclaredAnnotation(Extension.class); if (extensionAnn != null) { return extensionRegister; } EventHandler eventHandlerAnn = targetClz.getDeclaredAnnotation(EventHandler.class); if (eventHandlerAnn != null) { return eventRegister; } return null; } }
BootStrap
Scan java's class es for ioc assembly;
/* * Copyright 2017 Alibaba.com All right reserved. This software is the * confidential and proprietary information of Alibaba.com ("Confidential * Information"). You shall not disclose such Confidential Information and shall * use it only in accordance with the terms of the license agreement you entered * into with Alibaba.com. */ package com.alibaba.cola.boot; import java.util.List; import java.util.Set; import java.util.TreeSet; import org.springframework.beans.factory.annotation.Autowired; import com.alibaba.cola.exception.framework.ColaException; import lombok.Getter; import lombok.Setter; /** * <B>Core boot startup class for application </B> * <p> * Responsible for scanning inApplicationContext.xmlGet CommandExecutors, intercepters, extensions, validators, etc. * Deliver to each registrar for registration. * * @author fulan.zjf 2017-11-04 */ public class Bootstrap { @Getter @Setter private List<String> packages; private ClassPathScanHandler handler; @Autowired private RegisterFactory registerFactory; public void init() { Set<Class<?>> classSet = scanConfiguredPackages(); registerBeans(classSet); } /** * @param classSet */ private void registerBeans(Set<Class<?>> classSet) { for (Class<?> targetClz : classSet) { RegisterI register = registerFactory.getRegister(targetClz); if (null != register) { register.doRegistration(targetClz); } } }
Registration of other core components is also included in the code.
AbstractComponentExecutor
An abstract component executor whose main function is to locate an extension class and then execute the interface's methods.
The source code is as follows:
package com.alibaba.cola.boot; import com.alibaba.cola.extension.BizScenario; import com.alibaba.cola.extension.ExtensionCoordinate; import java.util.function.Consumer; import java.util.function.Function; /** * @author fulan.zjf * @date 2017/12/21 */ public abstract class AbstractComponentExecutor { /** * Execute extension with Response * * @param targetClz * @param bizScenario * @param exeFunction * @param <R> Response Type * @param <T> Parameter Type * @return */ public <R, T> R execute(Class<T> targetClz, BizScenario bizScenario, Function<T, R> exeFunction) { T component = locateComponent(targetClz, bizScenario); return exeFunction.apply(component); } public <R, T> R execute(ExtensionCoordinate extensionCoordinate, Function<T, R> exeFunction){ return execute((Class<T>) extensionCoordinate.getExtensionPointClass(), extensionCoordinate.getBizScenario(), exeFunction); } /** * Execute extension without Response * * @param targetClz * @param context * @param exeFunction * @param <T> Parameter Type */ public <T> void executeVoid(Class<T> targetClz, BizScenario context, Consumer<T> exeFunction) { T component = locateComponent(targetClz, context); exeFunction.accept(component); } public <T> void executeVoid(ExtensionCoordinate extensionCoordinate, Consumer<T> exeFunction){ executeVoid(extensionCoordinate.getExtensionPointClass(), extensionCoordinate.getBizScenario(), exeFunction); } protected abstract <C> C locateComponent(Class<C> targetClz, BizScenario context); }
Function <T, R> is the main functional interface for java8. T: An instance of an extension class registered in the system; R is the method that calls T to use the class and the return value after execution.
The choice of which method to execute is given to the business logic code.
Four different overload methods are provided.
Summary
Extend by key,value.
Code
Originality is not easy, attention is valuable, forwarding price is higher!Please indicate the source of the reprint so that we can communicate with each other, make common progress and welcome communication. I will continue to share my knowledge of Java software programming and the way programmers develop their careers. Welcome to my attention. I have sorted out various resources of programming learning over the years, paid attention to the public number'Li Fuchun Continuous Output', and sent'Learning Materials'to share with you!