springboot2.7 java8
问题
在使用工厂模式封装service时,需要通过service的class获取其类型注解,但是有些工厂类可以取到annotation注解,有些取不到
渠道注解:
/** * xxx渠道注解 * */@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface XxxType { /** * 渠道的值为XxxTypeEnum枚举 */ XxxTypeEnum value();}
enum:
/** * 枚举类 */@Getter@AllArgsConstructorpublic enum XxxTypeEnum { X1("X1", "渠道1"), X2("X2", "渠道2"); private final String code; private final String message; public static XxxTypeEnum getEnumByCode(String code){ for(XxxTypeEnum value:values()){ if(StringUtils.equals(value.code, code)){ return value; } } throw new CommonException("未知的XXX类型"); }}
工厂类:
@Componentpublic class XxxServiceFactory implements ApplicationContextAware { /** * xxx服务的映射集合 */ private static final Map SERVICE_MAP = new HashMap(); /** * 工厂方法获取服务实现 * * @param xxxType 渠道 * @return 服务 */ public static XxxService getService(XxxTypeEnum xxxType) { XxxService service = SERVICE_MAP.get(xxxType); if (service == null) { throw new CommonException("没有匹配的服务实现类"); } return service; } /** * 初始化渠道枚举-xxx服务的映射的映射 * * @param applicationContext 应用上下文 */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { Map serviceMap = applicationContext.getBeansWithAnnotation(XxxType.class); if (ObjectUtil.isEmpty(serviceMap)) { throw new CommonException("服务映射初始化失败"); } serviceMap.forEach((key, bean) -> { if (!(bean instanceof XxxService)) { throw new CommonException("注解:" + XxxType.class + ",只能用于" + XxxService.class + "的实现类中"); } XxxService service = (XxxService) bean; XxxType annotation = service.getClass().getAnnotation(XxxType.class); // annotation 有时为null SERVICE_MAP.put(annotation.value(), service); }); }}
public interface XxxService { void test(); void doSomething();}
渠道1服务实现类
/** * 渠道1xxx服务实现类 */@XxxType(XxxTypeEnum.X1)@Service@Slf4jpublic class X1XxxServiceImpl implements XxxService { @Override public void test() { log.info("测试渠道1 test"); } /** * 此方法需要事务包裹 */ @Transactional(rollbackFor = Exception.class) @Override public void doSomething() { log.info("测试渠道1 do something"); }}
渠道2服务实现类
/** * 渠道1xxx服务实现类 */@XxxType(XxxTypeEnum.X2)@Service@Slf4jpublic class X2XxxServiceImpl implements XxxService { @Override public void test() { log.info("测试渠道2 test"); } @Override public void doSomething() { log.info("测试渠道2 do something"); }}
解决
以上为部分代码,项目启动时,显示渠道1服务实现类的annotation
为null
,直接npe,找了半天,发现是因为渠道1内的doSomething
方法添加事务注解,因为@Transactional
也是基于aop的,所以此时拿到的bean是代理对象,而代理对象的方法是不会把原来父类中的方法的注解加上去的,所以为null
,所以换了种方式
... /** * 初始化渠道枚举-xxx服务的映射的映射 * * @param applicationContext 应用上下文 */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { Map serviceMap = applicationContext.getBeansWithAnnotation(XxxType.class); if (ObjectUtil.isEmpty(serviceMap)) { throw new CommonException("服务映射初始化失败"); } serviceMap.forEach((key, bean) -> { if (!(bean instanceof XxxService)) { throw new CommonException("注解:" + XxxType.class + ",只能用于" + XxxService.class + "的实现类中"); } // XxxService service = (XxxService) bean; // XxxType annotation = service.getClass().getAnnotation(XxxType.class); // SERVICE_MAP.put(annotation.value(), service); List list = AnnotationUtil.scanClass(bean.getClass()); list.stream().filter(annotation -> annotation instanceof XxxType).findFirst().ifPresent(annotation -> { XxxType xxxType = (XxxType) annotation; SERVICE_MAP.put(xxxType .value(), (XxxService) bean); }); }); }
这样就能正确的获得注解,并完成工厂类的初始化啦