项目系统在运行时,突然发现了通知无法消费处理的异常,在排除通知未发送的原因后,发现是系统中对于kafka的定阅出现了问题。报错的信息如下:
log":"java.lang.NullPointerException: null\n","stream":"stdout","time":"2022-05-30T07:05:06.931666672Z"}
{"log":"\u0009at com.zte.sdon.urm.framework.event.UrmEventHandleListenerRegistery.serverStarted(UrmEventHandleListenerRegistery.java:25)\n","stream":"stdout","time":"2022-05-30T07:05:06.93168196Z"}
{"log":"\u0009at io.dropwizard.lifecycle.setup.LifecycleEnvironment$ServerListener.lifeCycleStarted(LifecycleEnvironment.java:117)\n","stream":"stdout","time":"2022-05-30T07:05:06.931689514Z"}
{"log":"\u0009at org.eclipse.jetty.util.component.AbstractLifeCycle.setStarted(AbstractLifeCycle.java:194)\n","stream":"stdout","time":"2022-05-30T07:05:06.931695308Z"}
{"log":"\u0009at
从日志中发现是如下代码的第25行出现了空指针
从上图代码中箭头指向分析,分析理论上是不可能为空的,因为这些服务,就是从IOC容器上根据注解方式过滤出的对象,再取这些对象上的注解,怎么会就找不到对应注解了呢?
Debug后,发现取的对象,不再是原始对象,而是代理对象,通过团队内咨询,果然是有一个需求里增加了AOPA增强,导致进入到IOC里的是代理对象,如下的AOP注解**@Mutex**
@UrmEventHandleListener
@Service
@Slf4j
public class AddLinksHandleListener {
@Subscribe
@Mutex(classConvertingToLockList = AddLinksLockListConverter.class)
public void onAddLinks(AddLinks addLinks) {
log.info("receive addLinks notification : {}", addLinks);
OriginalLinkAddOperation originalLinkAddOperation = OriginalLinkAddOperation.getInstance(addLinks);
try {
originalLinkAddOperation.apply();
} catch (Exception e) {
log.warn("onAddLinks failed, exception is ", e);
throw e;
}
}
}
直接从代理对象getClass是获取不到原始类型的,自然也就无法找到其注解。
JDK代理和CGLIB代理
com.sun.proxy.$Proxy0
class com.zte.sdn.oscp.anno.proxy.ByeService3c26428bE n h a n c e r B y C G L I B " role="presentation" style="text-align: center; position: relative;">
本项目中,针对原始注解,增加了**@Inherited**描述,这样保证即使是生成的代理对象,其注解信息不丢失。
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Inherited
public @interface XXEventHandleListener {
boolean async() default false;
}
经过上面的修改,可能对于加一个@Inherited功能正常,还是不明白为什么?
以JDK动态代理为例,原理上最终生成的代理对象类实际上一个ProxyXX的类同时实现了原来类的接口。(JDK动态代码要求有接口)
这样代理对象的接口也是可以获取到原来注解的,而不管其是否有@Inherited 注解。前提是注解要在接口类上。
public static void main(String[] args) {
HelloService helloService = new HelloService();
Hello helloServiceProxy = (Hello) Proxy.newProxyInstance(HelloService.class.getClassLoader(), new Class[]{Hello.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
return method.invoke(helloService, args);
}
});
helloServiceProxy.say();
Arrays.stream(helloService.getClass().getAnnotations()).forEach(System.out::println);
//com.sun.proxy.$Proxy0
System.out.println(helloServiceProxy.getClass());
final Annotation[] annotations = helloServiceProxy.getClass().getInterfaces()[0].getAnnotations();
//@com.zte.sdn.oscp.anno.proxy.XXEventHandleListener(async=false)
Arrays.stream(annotations).forEach(System.out::println);
}
CGLIB代理,其代理对象是一个继承原来类的新类(不需要接口)。这样获取其父类,理论也是可以得到注解的,如下:
public static void main(String[] args) {
ByeService byeService = new ByeService();
final ByeService byeServiceProxy = create(byeService);
byeServiceProxy.say();
//class com.zte.sdn.oscp.anno.proxy.ByeService$$EnhancerByCGLIB$$3c26428b
System.out.println(byeServiceProxy.getClass());
final Annotation[] annotations = byeServiceProxy.getClass().getSuperclass().getAnnotations();
//@com.zte.sdn.oscp.anno.proxy.XXEventHandleListener(async=false)
Arrays.stream(annotations).forEach(System.out::println);
}
那在CGLIB中,为了使者用无感知是否代理,则需要在注解里增加@Inherited,这样代理对象的类则也可以继承该注解
public static void main(String[] args) {
ByeService byeService = new ByeService();
final ByeService byeServiceProxy = create(byeService);
byeServiceProxy.say();
//class com.zte.sdn.oscp.anno.proxy.ByeService$$EnhancerByCGLIB$$3c26428b
System.out.println(byeServiceProxy.getClass());
Annotation[] annotations = byeServiceProxy.getClass().getSuperclass().getAnnotations();
//@com.zte.sdn.oscp.anno.proxy.XXEventHandleListener(async=false)
Arrays.stream(annotations).forEach(System.out::println);
//这要正确获取到正确注解,则需要增加@Inherited
annotations = byeServiceProxy.getClass().getAnnotations();
//@com.zte.sdn.oscp.anno.proxy.XXEventHandleListener(async=false)
Arrays.stream(annotations).forEach(System.out::println);
}
1、从上文的分析来看,如果是CGLIB代理的原理,通过增加@Inherited可以保证注解信息不丢失。
2、而JDK代理,只有在接口上的注解才不会丢失
最后框架提供的根据注解获取对象,作为框架其并不知道使用了何种代理
final List<?> allServices = ServiceUtils.getLocator().getAllServices(UrmEventHandleListener.class);
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class AnnotationProxyTest {
public static void main1(String[] args) {
HelloService helloService = new HelloService();
Hello helloServiceProxy = (Hello) Proxy.newProxyInstance(HelloService.class.getClassLoader(), new Class[]{Hello.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
return method.invoke(helloService, args);
}
});
helloServiceProxy.say();
Arrays.stream(helloService.getClass().getAnnotations()).forEach(System.out::println);
//com.sun.proxy.$Proxy0
System.out.println(helloServiceProxy.getClass());
final Annotation[] annotations = helloServiceProxy.getClass().getInterfaces()[0].getAnnotations();
//@com.zte.sdn.oscp.anno.proxy.XXEventHandleListener(async=false)
Arrays.stream(annotations).forEach(System.out::println);
}
public static void main(String[] args) {
ByeService byeService = new ByeService();
final ByeService byeServiceProxy = create(byeService);
byeServiceProxy.say();
//class com.zte.sdn.oscp.anno.proxy.ByeService$$EnhancerByCGLIB$$3c26428b
System.out.println(byeServiceProxy.getClass());
Annotation[] annotations = byeServiceProxy.getClass().getSuperclass().getAnnotations();
//@com.zte.sdn.oscp.anno.proxy.XXEventHandleListener(async=false)
Arrays.stream(annotations).forEach(System.out::println);
//这要正确获取到正确注解,则需要增加@Inherited
annotations = byeServiceProxy.getClass().getAnnotations();
//@com.zte.sdn.oscp.anno.proxy.XXEventHandleListener(async=false)
Arrays.stream(annotations).forEach(System.out::println);
}
public static <T> T create(Object target) {
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
return methodProxy.invokeSuper(obj, args);
}
});
return (T) enhancer.create();
}
}