如何在 Spring Boot with Kotlin 中处理自定义注解?
How to process custom annotation in Spring Boot with Kotlin?
上下文:
我想在 Spring Boot 中创建自定义注释并添加额外的处理逻辑。我给出了一个带有相当简单注释的示例,但我希望有几个这样的注释具有更细粒度的控制。
有几种方法可以解决这个问题:
- 创建过滤器
- 创建拦截器
- 使用自定义处理创建注释
我必须使用最新的,因为上面两个不适用于我的用例。
问题:
我在 Kotlin 中有一个自定义注释,我希望它在运行时被注册和检查。
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class OfflineProcessing(val allow: Bool)
REST 控制器如下:
@RestController
class ApiController {
@GetMapping("/api/version")
@OfflineProcessing(true)
fun apiVersion(): String {
return "0.1.0"
}
}
想法是对每个方法进行注解,并根据 OflineProcessing
是否允许进行条件逻辑。
我试过创建初级 PostBeanProcessor
:
@Component
class OfflineProcessingAnnotationProcessor @Autowired constructor(
val configurableBeanFactory: ConfigurableListableBeanFactory
) : BeanPostProcessor {
@Throws(BeansException::class)
override fun postProcessBeforeInitialization(bean: Any, beanName: String): Any? {
println("Before processor. Bean name: $beanName, Bean: $bean. Bean factory: $configurableBeanFactory.")
return super.postProcessBeforeInitialization(bean, beanName)
}
@Throws(BeansException::class)
override fun postProcessAfterInitialization(bean: Any, beanName: String): Any? {
println("After processor. Bean name: $beanName, Bean: $bean. Bean factory: $configurableBeanFactory.")
return super.postProcessAfterInitialization(bean, beanName)
}
}
显然,在 BeanPostProcessor 中,注释没有与其他注释一起记录,我很困惑如何访问它,到目前为止我没有找到没有 BeanPostProcessor 的任何其他好的例子。
依赖关系:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
我做错了什么吗?还是我尝试使用错误的方法来完成任务?
不是您问题的直接答案,但在那种情况下我会简单地使用 Spring AOP 而不是实现 BeanPostProcessor。为此,您可以定义以下注释和相应的方面,例如:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
annotation class OfflineProcessing(val allowed: Boolean = true)
@Aspect
@Component
class OfflineProcessingAspect {
@Pointcut("@annotation(<path.to.annotation.package>.OfflineProcessing)")
fun offlineProcessingPointcut(offlineProcessing: OfflineProcessing?) {
}
@Around("offlineProcessingPointcut(offlineProcessing)")
@Throws(Throwable::class)
fun around(pjp: ProceedingJoinPoint, offlineProcessing: OfflineProcessing): Object {
if (offlineProcessing.allowed()) {
// your pre-processing logic here
val result = pjp.proceed()
// post-processing logic here
return result
}
// do the same for non-allowed offline-processing ...
}
}
最后,添加如下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
这是一个一般性问题,并不特定于 Kotlin。
我认为您在尝试解决此问题时的误解是您在 BeanPostProcessors 上进行中继。这个 bean 是在早期创建的,它可能是一个单例,所以当你执行 rest 请求时它不会被调用。这意味着您将需要检查具有您的注释的 bean,然后以某种方式在它们之上创建一个代理 bean,并将您的逻辑嵌入该代理中。
这与 AOP 所做的非常相似,然后@eol 的方法是匹配复活节。
我想建议使用拦截器而不是 bean 创建拦截器。
我的回答受到了
的启发
定义注解
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class OfflineProcessing(val allow: Boolean)
定义拦截器
@Component
class CustomRestAnnotationInterceptor:HandlerInterceptor {
private val logger: Logger = LoggerFactory.getLogger(this.javaClass)
override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
if (handler is HandlerMethod) {
var isOffline: OfflineProcessing? = handler.getMethodAnnotation(OfflineProcessing::class.java)
if (null == isOffline) {
isOffline = handler.method.declaringClass
.getAnnotation(OfflineProcessing::class.java)
}
if (null != isOffline) {
logger.info("method called has OfflineProcessing annotation with allow=${isOffline.allow}" )
}
}
return true
}
}
将拦截器添加到路径
@Configuration
class WebConfig : WebMvcConfigurer {
@Autowired
lateinit var customRestAnnotationInterceptor: CustomRestAnnotationInterceptor
override fun addInterceptors(registry: InterceptorRegistry) {
// Custom interceptor, add intercept path and exclude intercept path
registry.addInterceptor(customRestAnnotationInterceptor).addPathPatterns("/**")
}
}
在你的控制器上使用注解
日志会显示
2022-04-14 08:48:58.785 INFO 32595 --- [nio-8080-exec-1] .h.s.k.q.CustomRestAnnotationInterceptor : method called has OfflineProcessing annotation with allow=true
上下文: 我想在 Spring Boot 中创建自定义注释并添加额外的处理逻辑。我给出了一个带有相当简单注释的示例,但我希望有几个这样的注释具有更细粒度的控制。
有几种方法可以解决这个问题:
- 创建过滤器
- 创建拦截器
- 使用自定义处理创建注释
我必须使用最新的,因为上面两个不适用于我的用例。
问题:
我在 Kotlin 中有一个自定义注释,我希望它在运行时被注册和检查。
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class OfflineProcessing(val allow: Bool)
REST 控制器如下:
@RestController
class ApiController {
@GetMapping("/api/version")
@OfflineProcessing(true)
fun apiVersion(): String {
return "0.1.0"
}
}
想法是对每个方法进行注解,并根据 OflineProcessing
是否允许进行条件逻辑。
我试过创建初级 PostBeanProcessor
:
@Component
class OfflineProcessingAnnotationProcessor @Autowired constructor(
val configurableBeanFactory: ConfigurableListableBeanFactory
) : BeanPostProcessor {
@Throws(BeansException::class)
override fun postProcessBeforeInitialization(bean: Any, beanName: String): Any? {
println("Before processor. Bean name: $beanName, Bean: $bean. Bean factory: $configurableBeanFactory.")
return super.postProcessBeforeInitialization(bean, beanName)
}
@Throws(BeansException::class)
override fun postProcessAfterInitialization(bean: Any, beanName: String): Any? {
println("After processor. Bean name: $beanName, Bean: $bean. Bean factory: $configurableBeanFactory.")
return super.postProcessAfterInitialization(bean, beanName)
}
}
显然,在 BeanPostProcessor 中,注释没有与其他注释一起记录,我很困惑如何访问它,到目前为止我没有找到没有 BeanPostProcessor 的任何其他好的例子。
依赖关系:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
我做错了什么吗?还是我尝试使用错误的方法来完成任务?
不是您问题的直接答案,但在那种情况下我会简单地使用 Spring AOP 而不是实现 BeanPostProcessor。为此,您可以定义以下注释和相应的方面,例如:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
annotation class OfflineProcessing(val allowed: Boolean = true)
@Aspect
@Component
class OfflineProcessingAspect {
@Pointcut("@annotation(<path.to.annotation.package>.OfflineProcessing)")
fun offlineProcessingPointcut(offlineProcessing: OfflineProcessing?) {
}
@Around("offlineProcessingPointcut(offlineProcessing)")
@Throws(Throwable::class)
fun around(pjp: ProceedingJoinPoint, offlineProcessing: OfflineProcessing): Object {
if (offlineProcessing.allowed()) {
// your pre-processing logic here
val result = pjp.proceed()
// post-processing logic here
return result
}
// do the same for non-allowed offline-processing ...
}
}
最后,添加如下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
这是一个一般性问题,并不特定于 Kotlin。
我认为您在尝试解决此问题时的误解是您在 BeanPostProcessors 上进行中继。这个 bean 是在早期创建的,它可能是一个单例,所以当你执行 rest 请求时它不会被调用。这意味着您将需要检查具有您的注释的 bean,然后以某种方式在它们之上创建一个代理 bean,并将您的逻辑嵌入该代理中。
这与 AOP 所做的非常相似,然后@eol 的方法是匹配复活节。
我想建议使用拦截器而不是 bean 创建拦截器。
我的回答受到了
定义注解
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class OfflineProcessing(val allow: Boolean)
定义拦截器
@Component
class CustomRestAnnotationInterceptor:HandlerInterceptor {
private val logger: Logger = LoggerFactory.getLogger(this.javaClass)
override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
if (handler is HandlerMethod) {
var isOffline: OfflineProcessing? = handler.getMethodAnnotation(OfflineProcessing::class.java)
if (null == isOffline) {
isOffline = handler.method.declaringClass
.getAnnotation(OfflineProcessing::class.java)
}
if (null != isOffline) {
logger.info("method called has OfflineProcessing annotation with allow=${isOffline.allow}" )
}
}
return true
}
}
将拦截器添加到路径
@Configuration
class WebConfig : WebMvcConfigurer {
@Autowired
lateinit var customRestAnnotationInterceptor: CustomRestAnnotationInterceptor
override fun addInterceptors(registry: InterceptorRegistry) {
// Custom interceptor, add intercept path and exclude intercept path
registry.addInterceptor(customRestAnnotationInterceptor).addPathPatterns("/**")
}
}
在你的控制器上使用注解
日志会显示
2022-04-14 08:48:58.785 INFO 32595 --- [nio-8080-exec-1] .h.s.k.q.CustomRestAnnotationInterceptor : method called has OfflineProcessing annotation with allow=true