Recently, a new project has used the cola framework, which has an extension point, specifically to take a look at its source code, and make it clear
Cola Extension Point Usage
Cola provides an extension point mechanism that allows us to dynamically select implementation classes through bizId (organization ID), useCase (use case), scenario (business scenario). Similar to our policy pattern, the implementation class is dynamically selected to handle our requests while the program is running.
I won't explain much, but the following example shows how this extension is used.
1. Define extension point interfaces to implement ExtensionPointI
public interface OrganizationExtPt extends ExtensionPointI { /** * Query all departments under the enterprise based on corpId * * @return department */ String getDepartmentsByCorpId(); }
2. Pin Scene Extension Point Implementation
@Extension(bizId = "organize",useCase = "getByCorpId",scenario = "dingTalk") public class DingTalkOrganizationExt implements OrganizationExtPt { @Override public String getDepartmentsByCorpId() { System.out.println("In organizational business, use cases for getting Department lists from enterprise numbers, how business is handled in pinned scenarios"); System.out.println("Configuration information via pins API Obtain organizational information and assemble departmental information for cloud hub identification"); return "Nail"; } }
3. Implementation of WeChat Extension Point Interface
@Extension(bizId = "organize",useCase = "getByCorpId",scenario = "wechat") @Slf4j public class WechatOrganizationExt implements OrganizationExtPt{ public String getDepartmentsByCorpId() { 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"); return "WeChat"; } }
4. Extension point use
Used in the command executor.
@Component public class OrgazationQueryExe { @Resource private ExtensionExecutor extensionExecutor; public String execute(OrgnizationQry cmd) { return extensionExecutor.execute(OrganizationExtPt.class, cmd.getBizScenario(), ex -> ex.getDepartmentsByCorpId()); } }
5. Use of Test Extension Points
@RestController public class OrganizationController { @Resource private OrgazationQueryExe orgazationQueryExe; @GetMapping(value = "/organization/getDepartmentsByCorpId/{corpId}/{scenario}") public String 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 orgazationQueryExe.execute(qry); } }
As you can see, depending on our parameters, the implementation classes that are ultimately processed are different.
Exploration of Extension Point Principle
When I first used the Cola extension point, I thought, how did it find a specific implementation? In our code example above, the only entry is the following:
extensionExecutor.execute(OrganizationExtPt.class, cmd.getBizScenario(), ex -> ex.getDepartmentsByCorpId());
So follow up to see how the source code works.
public <R, T> R execute(Class<T> targetClz, BizScenario bizScenario, Function<T, R> exeFunction) { // 1. Find the specific implementation class responsible for execution T component = locateComponent(targetClz, bizScenario); // 2. Execution return exeFunction.apply(component); }
Continuing down, you find that the implementation class is retrieved from the extensionRepository. So the second question arises: When was the implementation class placed in the extensionRepository?
At this point, use IDEA to locate cola's location about the extension point source:
Based on the SpringBoot auto-configuration mechanism, we know that the Extension AutoConfiguration will be initialized by Spring, so this is our entry to explore the source code further.
@Configuration public class ExtensionAutoConfiguration { @Bean(initMethod = "init") @ConditionalOnMissingBean(ExtensionBootstrap.class) public ExtensionBootstrap bootstrap() { return new ExtensionBootstrap(); } @Bean @ConditionalOnMissingBean(ExtensionRepository.class) public ExtensionRepository repository() { return new ExtensionRepository(); } @Bean @ConditionalOnMissingBean(ExtensionExecutor.class) public ExtensionExecutor executor() { return new ExtensionExecutor(); } @Bean @ConditionalOnMissingBean(ExtensionRegister.class) public ExtensionRegister register() { return new ExtensionRegister(); } }
I guessed by name that ExtensionBootstrap was obviously associated with initialization, so go and see what it did. Then a flowchart is drawn. I won't take you with me to see the specific code (because I think it's easier to see the code myself)
Simply put:
- When initialized, it looks for Bean s in the Spring container with the @Extension annotation
- Register these beans with Extension Repository
A. Extension Repository can be easily understood as a Map
i. Key:ExtensionCoordinate, consisting of interface name, bizId, useCase, scenario
ii. value: corresponding implementation class - Now we can understand extensionExecutor. Two parameters in execute. From these two parameters you can assemble an ExtensionCoordinate
a. Class targetClz: Target Interface
b. BizScenario: Contains bizId, useCase, scenario
Reference article:
https://www.cnblogs.com/snidget/p/12961700.html