service.getClass().getAnnotation(XXXAnnotation.class) 取值为Null
springboot2.7 java8
问题
在使用工厂模式封装service时,需要通过service的class获取其类型注解,但是有些工厂类可以取到annotation注解,有些取不到
渠道注解:
/**
* xxx渠道注解
*
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XxxType {
/**
* 渠道的值为XxxTypeEnum枚举
*/
XxxTypeEnum value();
}
enum:
/**
* 枚举类
*/
@Getter
@AllArgsConstructor
public 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类型");
}
}
工厂类:
@Component
public class XxxServiceFactory implements ApplicationContextAware {
/**
* xxx服务的映射集合
*/
private static final Map<XxxTypeEnum, XxxService> 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<String, Object> 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
@Slf4j
public 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
@Slf4j
public 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<String, Object> 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<Annotation> 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);
});
});
}
这样就能正确的获得注解,并完成工厂类的初始化啦