一、前言
有时候我们会碰到这样的情况:
有n个场景,每个场景都有自己的逻辑,即n个处理逻辑,
这时候我们就需要通过某个参数的值代表这n个场景,然后去加载每个场景不同的bean对象,即不同的类,这些类中都有一个同名的方法,但是里面的逻辑不同,类似策略模式、工厂模式等
假设这样的场景,银行卡分几种类型,比如普通会员,黄金会员,黑卡会员......
普通会员、黄金会员和黑卡的权限不一样,消费能力不一样等等内容,这里我们就以消费的能力举例,普通会员只能转账5万元,黄金只能转账20万元,黑卡100万元,
我们有3各类,分别是Putong.class,Huangjin.class,Heika.class,这三个类里面有一个同样的方法,名字叫:checkPermissions()方法,三个类上都加了一个自定义注解@MemberTypeDefinition
二、代码demo参考
1、自定义注解
package com.zygxsq.test; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自定义注解 * **/ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface MemberTypeDefinition { /** * 数据源默认执行方法 */ public static final String DEFAULT_DAO_METHOD_NAME = "checkPermissions"; /** * 数据源执行的方法名称, 默认为getData, 该方法必须只有一个参数{@link BaseDataSpec}, 返回结果必须是{@link BaseResult}, 可见性必须是public * * @return 方法名称 */ String value() default DEFAULT_DAO_METHOD_NAME; }
2、基础model类
Putong.class
package com.zygxsq.test; import org.springframework.stereotype.Component; /** * 普通会员处理逻辑 * **/ @MemberTypeDefinition @Slf4j @Component public class Putong { public BaseResult checkPermissions(BaseDataSpec baseDataSpec) { // 省略中间的一些处理逻辑 // ...... // 省略中间的一些处理逻辑 // 最后封装返回结果 BaseResult baseResult = new BaseResult(); baseResult.setResponse("我是普通会员"); return baseResult; } }
Huangjin.class
package com.zygxsq.test; import org.springframework.stereotype.Component; /** * 黄金会员处理逻辑 * **/ @MemberTypeDefinition @Slf4j @Component public class Huangjin { public BaseResult checkPermissions(BaseDataSpec baseDataSpec) { // 省略中间的一些处理逻辑 // ...... // 省略中间的一些处理逻辑 // 最后封装返回结果 BaseResult baseResult = new BaseResult(); baseResult.setResponse("我是黄金会员"); return baseResult; } }
Heika.class
package com.zygxsq.test; import org.springframework.stereotype.Component; /** * 黑卡会员处理逻辑 * **/ @MemberTypeDefinition @Slf4j @Component public class Heika { public BaseResult checkPermissions(BaseDataSpec baseDataSpec) { // 省略中间的一些处理逻辑 // ...... // 省略中间的一些处理逻辑 // 最后封装返回结果 BaseResult baseResult = new BaseResult(); baseResult.setResponse("我是黑卡会员"); return baseResult; } }
3、入参和返回参数对象
入参对象
package com.zygxsq.test; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import java.io.Serializable; import java.util.List; /** * 入参数据对象 * **/ @Data @SuperBuilder @NoArgsConstructor @AllArgsConstructor public class BaseDataSpec implements Serializable { /** * serialVersionUID */ private static final long serialVersionUID = -5669150877852962345L; /** * id */ private String id; /** * 名字 */ private String name; }
返回结果对象
package com.zygxsq.test; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; /** * 返回结果对象 * **/ @Data @Builder @NoArgsConstructor @AllArgsConstructor public class BaseResult implements Serializable { /** * serialVersionUID */ private static final long serialVersionUID = 7510262928468530569L; private String response; }
4、反射核心代码
那如何通过反射进行加载呢,BaseDataAnnotationApplication.class
package com.zygxsq.test; import com.google.common.collect.Maps; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Map; /** * 启动加载 * **/ @Slf4j @Component public class BaseDataAnnotationApplication implements ApplicationContextAware, InitializingBean { private ApplicationContext applicationContext; /** * <className,V> */ private final Map<String, Object> loadMap = Maps.newConcurrentMap(); /** * 特殊人群表缓存 <id,1> ,获取 SpecialPeople.class里面所有的表名 */ public static Map<String,Object> dmTablesMap = Maps.newConcurrentMap(); @Override public void afterPropertiesSet() throws Exception { // 获取加了 MemberTypeDefinition 注解的源表bean loadSourceDefinition(); // 获取SpecialPeople.class里面所有的表名 loadSpecialMap(); } private void loadSourceDefinition() throws Exception{ Map<String, Object> beans = applicationContext.getBeansWithAnnotation(MemberTypeDefinition.class); for (final Object serviceObject : beans.values()) { final Class<? extends Object> calcuteClass = serviceObject.getClass(); MemberTypeDefinition annotation = calcuteClass.getAnnotation(MemberTypeDefinition.class); if (null == annotation) { log.error("类: {} 注解缺失", calcuteClass); continue; } loadMap.put(calcuteClass.getName(), serviceObject); } } /** * 获取SpecialPeople.class里面所有的表名 * @throws Exception */ private void loadSpecialMap() throws Exception { dmTablesMap = Maps.newConcurrentMap(); Field[] declaredFields = SpecialPeople.class.getDeclaredFields(); for (Field declaredField : declaredFields) { Class<?> type = declaredField.getType(); String typeName = declaredField.getGenericType().getTypeName(); Class<?> aClass = Class.forName(typeName); if (type == People.class) { People people = (People) declaredField.get(aClass); String id = people.getId().trim(); dmTablesMap.put(id, "1"); } } } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } public <T> BaseResult buildData(Class<T> clazz, BaseDataSpec baseDataSpec) throws Exception{ // 获取执行方法名, 默认为getData String methodName; MemberTypeDefinition annotation = clazz.getAnnotation(MemberTypeDefinition.class); if (annotation == null || StringUtils.isBlank(annotation.value())) { methodName = MemberTypeDefinition.DEFAULT_DAO_METHOD_NAME; } else { methodName = annotation.value(); } Method method; Object bean = loadMap.get(clazz.getName()); BaseResult result = null; try { method = bean.getClass().getMethod(methodName, BaseDataSpec.class); result = (BaseResult) method.invoke(bean, baseDataSpec); } catch (NoSuchMethodException e) { throw new Exception(clazz.getName()+"未找到执行方法:"+methodName); } catch (Exception e2) { throw new Exception(clazz.getName()+"未找到执行方法:"+methodName); } return result; } public <T> BaseResult buildData(Class<?> sourceClass) { return null; } }
通过上面的application,就可以加对象加载到缓存里了,然后我们直接调用即可
5、测试接口
package com.zygxsq.test; import com.zygxsq.test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/users") public class TestController { @Autowired private BaseDataAnnotationApplication baseDataAnnotationApplication; @RequestMapping("/getUserType") public String getUserType() { // ... // 省略查询表等逻辑 // ... Class<test.Putong> putongClass = Putong.class; BaseDataSpec baseDataSpec = BaseDataSpec.builder() .id("888888") .build(); BaseResult baseResult = baseDataAnnotationApplication.buildData(putongClass,baseDataSpec); String response = baseResult.getResponse(); System.out.println(response); return response; } }
6、代码结构
所有代码结构,大致是这样,具体的一些代码可以放在不同的package下面,我这里仅仅是让大家可以直观的看到有这些类