Spring中基于自定义注解的方法调用?
Method invocation based on custom annotation in Spring?
我有一个自定义注释:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
EventType[] events() default EventType.MESSAGE;
}
并且 class B 中有一些方法,使用方法如下:
@Controller(events = {EventType.MESSAGE, EventType.DIRECT_MESSAGE})
public void onMessage(Message msg) { }
@Controller(events = {EventType.STAR_ADDED})
public void onStarAdded(Message msg) { }
现在,我想根据来自另一个 class A 的注释 events
值调用上述方法。也就是说,当classA接收到STAR_ADDED
类型的事件时,我想调用classB中的所有方法 带有注释 @Controller(events = {EventType.STAR_ADDED})
。
我知道如何在 Java 中执行此操作,但是 Spring 是否提供任何 API 来执行此操作?如果是,代码片段也会有帮助。
方案一:
你也可以这样做:
enum EventType {
MESSAGE {
@Override
public void handleMessage(Service service, Message message) {
service.onMessage(message);
}
},
STAR_ADDED {
@Override
public void handleMessage(Service service, Message message) {
service.onStarAdded(message);
}
public abstract void handleMessage(Service service, Message message);
}
}
在你的另一个 class 中,你知道什么是 "active" 事件:
yourEvent.handleMessage(service, message);
方案二:
我不知道 spring 是否有专门的东西,否则你也可以使用反射。这是一个使用反射的例子(我更喜欢上面的解决方案=>没有反射的枚举):
for(Method method: Service.class.getDeclaredMethods()){
Controller annotation = m.getAnnotation(Controller.class);
for(EventType event: annotation.events()){
if(event.equals(yourActiveEventType)){
method.invoke(service, message);
}
return ...
}
}
提示(非解决方案)3:
我真的不认为以下内容适用于您的场景,但我想我会提到它... Spring AOP 允许您在调用带注释的方法时触发一些代码(这有点像与你的情况相反),检查这个答案,但它可能值得你阅读:aspectj-pointcut-for-all-methods-of-a-class-with-specific-annotation
@Around("execution(@Controller * com.exemple.YourService.*(..))")
public Object aroundServiceMethodAdvice(final ProceedingJoinPoint pjp)
throws Throwable {
// perform actions before
return pjp.proceed();
// perform actions after
}
方案四:(在评论后添加)
使用org.reflections
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.10</version>
</dependency>
示例:
Service service = ...;
Message message = ...;
Set<Method> methods =
ReflectionUtils.getMethods(Service.class, ReflectionUtils.withAnnotation(Controller.class),ReflectionUtils.withParametersAssignableTo(Message.class));
for(Method m: methods){
Controller controller = m.getAnnotation(Controller.class);
for(EventType eventType: controller.value()){
if(EventType.MESSAGE.equals(eventType)){
m.invoke(service, message);
}
}
}
这假定您已经持有对服务对象的引用(您的方法所在的位置)。
由于您使用的是 Spring,如果您的 'Services' 是 spring 托管的,您可能会从 spring 的上下文中获取实例,您必须自己尝试一下,因为这在某种程度上与您的设计有关:
@Autowired
private ApplicationContext appContext;
Reflections r = new Reflections(new MethodAnnotationsScanner(), "com.your.package");
Set<Method> methods = r.getMethodsAnnotatedWith(Controller.class);
for(Method m: methods){
Controller controller = m.getAnnotation(Controller.class);
for(EventType eventType: controller.value()){
if(EventType.MESSAGE.equals(eventType)){
String className = m.getDeclaringClass().getSimpleName();
className = className.replaceFirst(className.substring(0,1), className.substring(0,1).toLowerCase());
Object service = appContext.getBean(className);
m.invoke(service, message);
}
}
}
如果您的 Class 是 spring 托管的并且使用其默认的驼峰命名法添加到上下文中,则此方法有效。
您可以简化逻辑,但我相信主要元素都在那里。
我有一个自定义注释:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
EventType[] events() default EventType.MESSAGE;
}
并且 class B 中有一些方法,使用方法如下:
@Controller(events = {EventType.MESSAGE, EventType.DIRECT_MESSAGE})
public void onMessage(Message msg) { }
@Controller(events = {EventType.STAR_ADDED})
public void onStarAdded(Message msg) { }
现在,我想根据来自另一个 class A 的注释 events
值调用上述方法。也就是说,当classA接收到STAR_ADDED
类型的事件时,我想调用classB中的所有方法 带有注释 @Controller(events = {EventType.STAR_ADDED})
。
我知道如何在 Java 中执行此操作,但是 Spring 是否提供任何 API 来执行此操作?如果是,代码片段也会有帮助。
方案一:
你也可以这样做:
enum EventType {
MESSAGE {
@Override
public void handleMessage(Service service, Message message) {
service.onMessage(message);
}
},
STAR_ADDED {
@Override
public void handleMessage(Service service, Message message) {
service.onStarAdded(message);
}
public abstract void handleMessage(Service service, Message message);
}
}
在你的另一个 class 中,你知道什么是 "active" 事件:
yourEvent.handleMessage(service, message);
方案二:
我不知道 spring 是否有专门的东西,否则你也可以使用反射。这是一个使用反射的例子(我更喜欢上面的解决方案=>没有反射的枚举):
for(Method method: Service.class.getDeclaredMethods()){
Controller annotation = m.getAnnotation(Controller.class);
for(EventType event: annotation.events()){
if(event.equals(yourActiveEventType)){
method.invoke(service, message);
}
return ...
}
}
提示(非解决方案)3:
我真的不认为以下内容适用于您的场景,但我想我会提到它... Spring AOP 允许您在调用带注释的方法时触发一些代码(这有点像与你的情况相反),检查这个答案,但它可能值得你阅读:aspectj-pointcut-for-all-methods-of-a-class-with-specific-annotation
@Around("execution(@Controller * com.exemple.YourService.*(..))")
public Object aroundServiceMethodAdvice(final ProceedingJoinPoint pjp)
throws Throwable {
// perform actions before
return pjp.proceed();
// perform actions after
}
方案四:(在评论后添加)
使用org.reflections
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.10</version>
</dependency>
示例:
Service service = ...;
Message message = ...;
Set<Method> methods =
ReflectionUtils.getMethods(Service.class, ReflectionUtils.withAnnotation(Controller.class),ReflectionUtils.withParametersAssignableTo(Message.class));
for(Method m: methods){
Controller controller = m.getAnnotation(Controller.class);
for(EventType eventType: controller.value()){
if(EventType.MESSAGE.equals(eventType)){
m.invoke(service, message);
}
}
}
这假定您已经持有对服务对象的引用(您的方法所在的位置)。
由于您使用的是 Spring,如果您的 'Services' 是 spring 托管的,您可能会从 spring 的上下文中获取实例,您必须自己尝试一下,因为这在某种程度上与您的设计有关:
@Autowired
private ApplicationContext appContext;
Reflections r = new Reflections(new MethodAnnotationsScanner(), "com.your.package");
Set<Method> methods = r.getMethodsAnnotatedWith(Controller.class);
for(Method m: methods){
Controller controller = m.getAnnotation(Controller.class);
for(EventType eventType: controller.value()){
if(EventType.MESSAGE.equals(eventType)){
String className = m.getDeclaringClass().getSimpleName();
className = className.replaceFirst(className.substring(0,1), className.substring(0,1).toLowerCase());
Object service = appContext.getBean(className);
m.invoke(service, message);
}
}
}
如果您的 Class 是 spring 托管的并且使用其默认的驼峰命名法添加到上下文中,则此方法有效。
您可以简化逻辑,但我相信主要元素都在那里。