今天遇到一个问题,就是我们对外暴露的接口方法需要加一个注解,这个注解是在实现类的方法上加还是在接口的方法签名前加,我看代码规范都是写在实现类的方法上。我挺好奇接口签名上能反射拿到么。遂写了个demo验证下我的想法。
@SpringBootApplication
public class Test {
public static void main(String[] args) {
SpringApplication.run(Test.class);
}
}
@org.springframework.context.annotation.Configuration
class Configuration {
@Bean
public BeanLoading beanLoading() {
return new BeanLoading();
}
}
// 拿接口的注解
@Component
class ApplicationStartedListener implements ApplicationListener<ApplicationStartedEvent> {
@Override
public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) {
ConfigurableApplicationContext applicationContext = applicationStartedEvent.getApplicationContext();
UserService userService = applicationContext.getBean(UserService.class);
if (userService.getClass().isAnnotationPresent(TestClassAnnotation.class)) {
System.out.println("find class annotation");
}
Method[] methods = userService.getClass().getMethods();
for (Method method : methods) {
if (AnnotationUtils.findAnnotation(method, TestMethodAnnotation.class) != null) {
System.out.println("find TestMethodAnnotation !! ");
return ;
}
}
System.out.println("not find TestMethodAnnotation!!");
}
}
class BeanLoading implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("bean loaded after " + beanName);
return bean;
}
}
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface TestMethodAnnotation {
}
interface UserService {
@TestMethodAnnotation
void say();
}
@Component
class UserServiceImpl implements UserService {
// @TestMethodAnnotation
@Override
public void say() {
}
}
输出结果:find TestMethodAnnotation !!
实验证明,在父方法签名加和子类签名加效果是一样的,这个问题本质其实是一个方法注解的问题。拍拍脑袋就有两个问题,父类的方法注解能继承么,类注解能继承么。如果方法能继承,那类上是怎样的结果呢.
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface TestClassAnnotation {
}
@TestClassAnnotation
interface UserService {
@TestMethodAnnotation
void say();
}
@Component
class UserServiceImpl implements UserService {
// @TestMethodAnnotation
@Override
public void say() {
}
}
not find class anotation !!
这并不能获取到类上的注解。
于是我在注解上添加inherited注解:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface TestClassAnnotation {
}
依然是 not find class anotation !!.
后来看到:
Note that this meta-annotation type has no effect if the annotated
* type is used to annotate anything other than a class. Note also
* that this meta-annotation only causes annotations to be inherited
* from superclasses; annotations on implemented interfaces have no
* effect.
也就是说对接口是无效的。
把UserService改成抽象类即能识别到对应类的注解。
@SpringBootApplication
public class Test {
public static void main(String[] args) {
SpringApplication.run(Test.class);
}
}
@org.springframework.context.annotation.Configuration
class Configuration {
@Bean
public BeanLoading beanLoading() {
return new BeanLoading();
}
}
// 拿接口的注解
@Component
class ApplicationStartedListener implements ApplicationListener<ApplicationStartedEvent> {
@Override
public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) {
ConfigurableApplicationContext applicationContext = applicationStartedEvent.getApplicationContext();
UserService userService = applicationContext.getBean(UserService.class);
if (userService.getClass().isAnnotationPresent(TestClassAnnotation.class)) {
System.out.println("find class annotation !!");
} else {
System.out.println("not find class anotation !!");
}
Method[] methods = userService.getClass().getMethods();
for (Method method : methods) {
if (AnnotationUtils.findAnnotation(method, TestMethodAnnotation.class) != null) {
System.out.println("find TestMethodAnnotation !! ");
return ;
}
}
System.out.println("not find TestMethodAnnotation!!");
}
}
class BeanLoading implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("bean loaded after " + beanName);
return bean;
}
}
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface TestMethodAnnotation {
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface TestClassAnnotation {
}
@TestClassAnnotation
class UserService {
@TestMethodAnnotation
void say() {
}
}
@Component
class UserServiceImpl extends UserService {
// @TestMethodAnnotation
@Override
public void say() {
}
}
即可识别到基类的注解。
这个问题本质上是注解继承问题,还有一些复杂场景,例如抽象方法的重写,以及字段注解继承等等问题,网上有很多其他资料,也可以根据上面的的代码自己改改验证会更加深刻。