# shmet **Repository Path**: sun-haomingipad/shmet ## Basic Information - **Project Name**: shmet - **Description**: 需求推进业务解决方案 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: develop - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-12-28 - **Last Updated**: 2026-06-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # TOOL > 专注轮子DEMO、总结工具、解决方案片段 ## 本地调用-初版 > 又名: 接口注册、SDK普通接口调用业务方复写逻辑 > > 所在路径:src/main/java/com/shmet/tool/localcall > > 所有相关接口禁用AOP切面织入 **问题描述** 1、新增复写接口,每次都要增加getService的逻辑 2、复写接口的调用,每次都要先getService取到实例,才能调用,不符合接口调用习惯 **解决思路** 1、通过注解进行标记,解决新增复写接口,每次都要增加getService和接口常量的问题 2、通过代理模式,内聚解决每次调用都要getService取实例的步骤 3、新增的扩展,可以集成本地调用、dubbo动态调用,以满足特定场景 ### 使用方式 ##### 提供方 > 定义复写接口,如下 ```java // 普通接口,字段联动的事件接口,需复写扩展 public interface FdLinkageEventService extends FormBizService { } ``` > 提供默认实现抽象逻辑 ```java public abstract class AbsFdLinkageEventService implements FdLinkageEventService { } ``` > 定义代理调用接口,并定义代理功能,如下 ```java @Service public class FdLinkageEventProxy extends AbsFdLinkageEventService implements FormBizServiceProxy { } ``` ### 业务模块 > 增加复写接口,@Service的value要按照约定填写,module+接口名称,如下 > > 不按照约定填写,启动会报错 > > 正例:【"formdatareportFdLinkageEventService","workflowFdLinkageEventService"】 > > 反例:【"formdatareport_FdLinkageEventService","formdatareportFormBizService"】 ```java @Service(FormGroup.FORMREPORT + "FdLinkageEventService") public class ReportFdLinkageEventService extends AbsFdLinkageEventService implements FdLinkageEventService { } ``` ### 设计要点 ##### 注册接口入池 > 通过实现BeanPostProcessor增加扩展点 > > 1、bean初始化时,对实现了FormBizService接口的,进行注册入池,以module和serviceName生成key > > 2、使用时根据module和serviceName为key取实例信息,通过map获取接口单例 ##### 创建代理对象 > 1、bean初始化时,对实现了FormBizServiceProxy的接口,进行CGLIB动态代理,返回代理对象 ```java public class FormBizPostProcessor implements BeanPostProcessor, ApplicationContextAware { private static ApplicationContext applicationContext; private static final Logger logger = LoggerFactory.getLogger(FormBizPostProcessor.class); private static volatile Map FORM_SDK_BEAN_MAP = new ConcurrentHashMap(); @Override public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException { // 对顶级复写接口、顶级代理接口,进行CGLIB代理和入队 Class clazz = bean.getClass(); if (FormBizServiceProxy.class.isAssignableFrom(clazz)) { return new FormBizProxyFactory<>(bean).getProxy(); } else if (FormBizService.class.isAssignableFrom(clazz)) { // 存放的常量池 if (StringUtils.equals(beanName, clazz.getName())) { throw new RuntimeException("Interface provision @Service does not set value, please fill in as agreed! class address: {" + clazz.getName() + "}"); } else if (FormBizPostProcessor.getServiceBeanMap().containsKey(beanName)) { // 重复实例化的提示 throw new RuntimeException("Repetitive ServiceRegister class serviceName: {" + beanName + "} class address: {" + clazz.getName() + "}"); } else { FormBizPostProcessor.setServiceBean(beanName, bean); } return bean; } return bean; } } ``` > 2、代理逻辑要点 ```java public class FormBizProxyFactory implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { // 从约定的参数中,根据基类取实体 FormBaseParam param = this.getBaseParamByArgs(args); if (Objects.nonNull(param)) { String module = param.getModule(); String type = this.getServNameByClass(this.obj.getClass()); // 根据参数取示例对象 Object instance = FormBizPostProcessor.getServiceBean(module + type); if (Objects.nonNull(instance)) { // 换成要执行的示例的方法 Method newMethod = instance.getClass().getMethod(method.getName(), method.getParameterTypes()); return newMethod.invoke(instance, args); } } return method.invoke(this.obj, args); } } ``` ### 使用约定 > 1、约定复写的接口,第一个参数必有FormBeanParam,且必有module参数 > > 2、业务模块复写接口的@Service的value要按照约定填写,module+接口名称,如下: > > 正例:【"formdatareportFdLinkageEventService","workflowFdLinkageEventService"】 > > 反例:【"formdatareport_FdLinkageEventService","formdatareportFormBizService"】 ### 实现核心 - 核心bean > FormBeanParam 约定接口第一位参数,必有module,无值或为空走默认逻辑 - 注册核心 > FormBizPostProcessor 扩展点注册类 > > FormBizProxyFactory 动态代理工厂:cglib - 顶层接口 > FormBizService 模块复写的顶层接口 > > FormBizServiceProxy 代理接口的顶层接口 ## 本地调用-第二版 > 能覆盖第一版的能力,但是设计与第一版不同 > > 差异: > > 1、抛弃显示声明的代理类 > 2、更贴合普通接口的调用 > > 相关知识点: Spring的Bean依赖注入、Spring的多种PostPostProcessor拓展点、Spring整合MyBatis值@Mapper的托管、Java JDK动态代理 ### 使用效果展示 > 定义基接口,以及基接口的相关实现类 ```java /** * @author shmet * @date 2022/7/29 9:05 * @desc 业务接口,定义接口,以及继承基接口 */ public interface FormService extends BusService { /** * @Description: * @Author: shmet * @Date: 2022/7/29 9:08 * @param module: * @return: java.lang.String */ String getForm(String module); } /** * @author shmet * @date 2022/7/29 9:05 * @desc 表单数据上报的相关实现 */ @Service public class FormdatareportFormServiceImpl implements FormService{ @Override public String getForm(String module) { return this.getClass().getName(); } } /** * @author shmet * @date 2022/7/29 9:07 * @desc 审批流相关实现 */ @Service public class WorkflowFormServiceImpl implements FormService { @Override public String getForm(String module) { return getClass().getName(); } } ``` > 定义controller的使用 ```java /** * @author shmet * @date 2022/7/8 18:11 * @desc 一个普通的controller, 本地调用接口使用@LocalReference注入 */ @RestController @RequestMapping(value = {"/api/common/bus/"}) public class BusController { @LocalReference private FormService formService; @LocalReference private BusinessService businessService; @RequestMapping("/hello/{module}") public String hello(@PathVariable String module) { return businessService.hello(module); } @RequestMapping("/getForm/{module}") public String getForm(@PathVariable String module) { return formService.getForm(module); } } ``` ### 设计要点及流程 #### 接口注册,业务实现类注册 > 实现ApplicationContextAware回调扩展,使用回传的ApplicationContext.getBeansOfType(),取基接口的所有实现 > ```java /** * @author shmet * @date 2022/7/28 15:46 * @desc 类接口注册 */ @Component public class BeanContext implements ApplicationContextAware { private static Map instanceRegisterMap = new HashMap<>(); public static BusService getRegBean(String name) { return instanceRegisterMap.get(name); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { // 注册实例 Map objectRegMap = applicationContext.getBeansOfType(BusService.class); for (BusService instance : objectRegMap.values()) { String beanName = instance.getClass().getSimpleName().replace("Impl", ""); beanName = beanName.substring(0, 1).toLowerCase() + beanName.substring(1); instanceRegisterMap.put(beanName, instance); } } } ``` #### 接口注入 > 通过InstantiationAwareBeanPostProcessor扩展点,在bean实例化完毕后,扫描带有@LocalReference的属性,并为其生成代理对象, 使用JDK动态代理 > > 知识点: JDK动态代理是接口级,与CGLIB不同,无实现类也可以用 ```java /** * @author shmet * @date 2022/7/28 15:46 * @desc 实现自动注入 */ @Component public class BeanAutoWrite implements InstantiationAwareBeanPostProcessor { @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException { try { Class clazz = bean.getClass(); for (Field field : clazz.getDeclaredFields()) { if (field.isAnnotationPresent(LocalReference.class)) { Object proxyInstance = new BusJdkProxy(field.getType()).getProxy(); ReflectionUtils.makeAccessible(field); field.set(bean, proxyInstance); } } } catch (IllegalAccessException e) { e.printStackTrace(); } return pvs; } } /** * @author shmet * @date 2022/7/28 18:35 * @desc jdk接口冬天代理 */ public class BusJdkProxy implements InvocationHandler { /** * @author shmet * @date 2022/7/29 * @desc 全局代理对象单例池 */ private static Map proxySingletonPool = new ConcurrentHashMap<>(); private Class proxyInterface; public BusJdkProxy(Class proxyInterface){ this.proxyInterface = proxyInterface; } /** * @author shmet * @date 2022/7/29 * @desc 生成代理对象 */ public T getProxy() { String className = this.proxyInterface.getName(); if (proxySingletonPool.containsKey(className)) { return (T) proxySingletonPool.get(className); } else { synchronized (proxySingletonPool) { if (proxySingletonPool.containsKey(className)) { return (T) proxySingletonPool.get(className); } else { T tProxy = (T) Proxy.newProxyInstance(proxyInterface.getClassLoader(), new Class[]{proxyInterface}, this); proxySingletonPool.put(className, tProxy); return tProxy; } } } } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 从约定的参数中,根据基类取实体 String module = this.getBaseParamByArgs(args); String type = this.getServNameByClass(this.proxyInterface); // 根据参数取示例对象 Object instance = BeanContext.getRegBean(module + type); if (Objects.nonNull(instance)) { // 换成要执行的示例的方法 Method newMethod = instance.getClass().getMethod(method.getName(), method.getParameterTypes()); return newMethod.invoke(instance, args); } return null; } /** * @author shmet * @date 2022/6/28 * @desc 获取ser名称 */ public String getServNameByClass(Class objClass) { return objClass.getSimpleName(); } /** * @author shmet * @date 2022/6/27 * @desc 遍历取约定对象,假设关键参数在第一位 */ public String getBaseParamByArgs(Object[] args) { if (null != args && args.length > 0) { return args[0] + ""; } return null; } } ``` ## 本地调用-第三版 > 覆盖前两版,更贴合普通的使用方式 > > 差异: > > 1、属性注入口,改为接口扫描的形式 > 2、与spring整合,符合Bean创建相关规范 > > 相关知识点: Spring的ImportBeanDefinitionRegistrar拓展点、BeanDefinition的配置、借鉴Spring整合MyBatis、Aop托管、Java JDK动态代理 > 相关设计模式:Spring织入、代理 1、伪接口使用AOP切入,会有触发两次AOP的情况 ### 使用方式 > 使用@FormBizScan修饰,启动时会生成实现的代理, 接口需继承FormBizService ```java @LocalCallScan public interface LocalService { } ``` > 注入示例 ```java @RestController @RequestMapping(value = {"/api/common/three/"}) public class ThreeController { @Autowired private LocalService localService; @Autowired private FormLayoutService formLayoutService; @RequestMapping("/hello/{module}") public String hello(@PathVariable String module) { return localService.hello(module); } } ``` ### 对接方式 ```java @Service public class FormLocalService implements LocalService { // 逻辑省略 } ``` ### 使用约定 > 1、约定复写的接口,第一个参数必有FormBeanParam,且必有module参数 > > 2、业务模块复写接口的@Service的value要按照约定填写,module+接口名称,如下: > > 正例:【"formdatareportFdLinkageEventService","workflowFdLinkageEventService"】 > > 反例:【"formdatareport_FdLinkageEventService","formdatareportFormBizService"】 > > 3、默认实现需要为serviceName+Impl > > 正例: FdEffectRangeService -> 【"FdEffectRangeServiceImpl"】 > > 反例: FdEffectRangeService -> 【"CommonFdEffectRangeServiceImpl"】 ### 汇总 > 1、定义顶层接口,用来获取所有实现类的示例 > > 2、利用BeanPostProcessor扩展点,实现自定义注入 > > 3、生成单例可复用代理类 > > 4、代理逻辑实现根据参数,动态转向调用,这里可以继承dubbo实现混编动态调用