当前位置: 首页 > 知识库问答 >
问题:

Spring aop @within对于自定义注释无法正常工作

李睿
2023-03-14

我为某些日志目的创建了一个自定义注释。此注释应用于通过扩展JpaRepository在项目中创建的Spring jpa存储库。所以现在发生的是,对于read方法,它可以正常工作,但对于保存部分,永远不会调用@周围建议。以下是我的@周围建议

@Around("@within(com.myproject.annotations.RepoAware)")
public void log(final ProceedingJoinPoint jp){
  return log(jp,true);
}

我的log方法是采用一个布尔参数,并以此为基础记录一些东西。以下是回购代码

@Repository
@RepoAware
public interface MyRepo extends JpaRepository<Student,Long>{
}

现在,当我调用不属于我的存储库MyRepo的repo方法时,比如save、saveAll或是存在于父层次结构中的方法,@Around建议就不起作用了。当我应用调试器时,我可以看到在保存调用期间,代理的类型是CrudRepository。所以当我在MyRepo中重写save方法时。类它开始工作。我对此感到困惑,因为MyRepo最终通过JpaRepository扩展了CrudRepository。请让我知道如何解决这个问题或我在这里做错了什么。

还提供有关如何在切入点中使用not表达式的帮助。比如上面的例子,我想定位我的所有存储库,除了有@RepoAware注释的。我创建了下面的建议,但它也不起作用。

@Around("target(org.springframework.data.jpa.repository.JpaRepository) and !@within(com.myproject.annotations.RepoAware)")
public Object logDBMetrics(final ProceedingJoinPoint pjp) throws Throwable {
        return log(pjp,false);
}

对于具有@RepoAware注释的存储库,也会调用上述建议。

提前致谢!

共有3个答案

郑燕七
2023-03-14

我终于能够让它工作了,下面是我使用的方法。感谢@Andrey B. Panfilov。

我创建了一个由MyRepo扩展的标记接口,然后在我的周围建议中,我使用类类型来检查它是否可以从该接口分配。如果是,则使用true或false登录。

@Around("target(org.springframework.data.jpa.repository.JpaRepository)")
public void log(final ProceedingJoinPoint jp){
  Class<?> clazz=MyRepoInterface.class;
  return clazz.isAssignableFrom(pjp.getTarget().getClass())?log(jp,true):log(pjp,false);
}

----
@Repository
public interface MyRepo extends JpaRepository<Student,Long>,MyRepoInterface{
}

-----
public interface MyRepoInterface{}

对于某些切入点,spring aop的行为仍然未知。就像使用AND一样,NOT和表达式似乎不起作用。我尝试了上面提到的几种方法,但都没有奏效。

范翰海
2023-03-14

AspectJ在Spring基础结构的情况下具有非常有限的功能,但是可以通过顾问来实现您的要求(另请查看:使用@Bean的Java中的Spring顾问)

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Inherited
public @interface CustomRepositoryAnnotation {
}


@Component
public class CustomRepositoryAnnotationAdvisor extends AbstractPointcutAdvisor {

    private static final Logger log = LoggerFactory.getLogger(CustomRepositoryAnnotationAdvisor.class);

    private final Advice advice;

    private final Pointcut pointcut;

    public CustomRepositoryAnnotationAdvisor() {
        this.advice = new MethodInterceptor() {
            @Nullable
            @Override
            public Object invoke(@NonNull MethodInvocation invocation) throws Throwable {
                log.info("DemoAnnotationAdvisor: {}", invocation.getMethod().getName());
                return invocation.proceed();
            }
        };
        this.pointcut = new AnnotationMatchingPointcut(CustomRepositoryAnnotation.class, null, true);
    }

    @Override
    public Pointcut getPointcut() {
        return pointcut;
    }

    @Override
    public Advice getAdvice() {
        return advice;
    }

}

天然气水合物演示项目

更新。

对这个主题进行了一些研究,发现了一篇关于Spring和AspectJ切入点指示符的精彩文章

首先,@in不应该在你的情况下工作(拦截超接口方法)-你应该使用@Target,不幸的是@Target切入点指示符似乎在Spring AOP中被破坏:存在带有单个@target切入点的建议会导致Spring为每个bean创建CGLIB代理,这是不可取的,甚至是不可能的(jdk类、最终类、没有公共构造函数的类等),尝试缩小建议的范围也失败了:例如Spring不应用建议@target(注释)

其次,在<代码>SpringAPO的情况下

高祺
2023-03-14

如果您想避免运行时反射,那么Spring AOP方面的解决方案,即基于代理的Spring风格AOP框架,如下所示:

@Pointcut(
  "execution(* (" +
  "@com.example.accessingdatajpa.CustomRepositoryAnnotation " +
  "org.springframework.data.repository.CrudRepository+" +
  ").*(..))"
)

这是针对目标

    < li >任何方法的执行 < li >在任何班级中 < ul > < li >该类是< code>CrudRepository或它的一个子类(< code> ), < li >类类型由< code > @ CustomRepositoryAnnotation 批注。

更新2:解决方案切入点的更简单、更通用的版本是:

@Pointcut("execution(* (@com.example.accessingdatajpa.CustomRepositoryAnnotation *..*).*(..))")

仅供参考,通常*而不是*…*应该作为AeyJ语法中“任何包中的任何类”的更简单替代品,但在这种情况下它似乎不起作用。

什么不起作用以及为什么:

    < Li > < code > @ within(CustomRepositoryAnnotation):未在由< code > @ CustomRepositoryAnnotation 批注的类中定义< code>save*、< code>findAll*、< code>delete*方法,而是在< code>CrudRepository中定义。 < Li > < code > @ target(CustomRepositoryAnnotation):接口批注不由Java中的实现类继承(因此也不由动态代理继承)。这是一个普通的JDK问题,与Spring甚至Spring AOP无关。在这里看到我的回答,向下滚动到“更新”部分。

更新1,回答评论中提出的问题:

< code > @ target(CustomRepositoryAnnotation)...-您是如何开始申请的?

通过排除我在这里的回答中描述的Spring包,并从JDK中排除最终类型,这些类型当然不能子类化,因此也不能被代理:

"@target(com.example.accessingdatajpa.CustomRepositoryAnnotation)"
    + " && !within(org.springframework..*) && !within(is(FinalType))"

但是在这种情况下,由于我上面解释的内容,切入点不匹配。

顺便说一句,它没有解释为什么目标(标记接口)确实有效,似乎注释支持在Spring AOP中被破坏了。

不,它没有被破坏,只是你对实现注释接口的类继承注释的期望是错误的。

target(标记接口)工作,因为运行时类型实现接口,因此切入点匹配。类继承与注释继承不同。无论喜欢与否,后者只存在于我链接的答案中解释的有限程度,并在@Inherited javadoc中陈述。

 类似资料:
  • 我在整个代码中有一堆常量,用于系统的各种可调整属性。我正在将它们全部移动到一个中央文件中。我目前的解决方案是有一个静态加载文件并公开各种getter方法,如下所示: 唯一的问题是,对于我从这个文件中获得的每个常量,我都有一些样板: 我不认为我想使用Spring或类似的东西,因为那看起来更像是boilerplae。我希望使用自定义注释来解决这个问题。我找到了这个教程,但是我真的不能弄清楚如何从注释处

  • 问题内容: 在ajax中使用base_url()从codeigniter项目中获取数据库。给定的base_url就像http://domainname.com。很好 如果我可以在地址栏中输入http://www.domainname.com之类的网址,则无法正常工作。该代码是 请帮助解决此问题。谢谢 问题答案: 我认为最好的解决方案是: 只需在HTML的标头部分中添加以下脚本即可。 然后在您的Aj

  • 我目前正在用JavaFX做一个小项目。我使用SceneBuilder创建了我的GUI的第一个草图。它仍然需要一些调整和样式,但我想看看它是否工作到目前为止。 我在GUI上有两个超链接,如果用户单击其中一个,默认的系统浏览器将打开一个特定的URL。 到目前为止,我得到了这个: 主要的爪哇: 数据库。爪哇: 测试视图。爪哇: 在我的控制器中,我想将ActionHandler设置为超链接,但它不起作用,

  • 我在textInputEditText中使用textInputLayout,我必须为editText设置背景,以实现editText的边框视图。但是当我调用textInputLayout上的setError()时,整个editText颜色将变为红色。但我只想更改错误文本的颜色,而不是整个视图。 设置前错误: 请帮帮我,我做错了什么?

  • 如果Spring批处理作业业务逻辑出现ArrayIndexOutOfBoundsException,我必须从我的自定义映射器FieldSetMapper of reader FlatFileItemReader中跳过一个自定义异常(CSVFieldMappingException)。为此,我添加了作业步骤的配置,如下所示。 但这不起作用,因为每当我抛出自定义RunTimeException时,就会

  • 问题内容: 注释如何与Java一起使用?以及如何创建这样的自定义注释: 基本上,我需要保留的POJO在持久化时像这样进行序列化: 这样,实际的生成/持久对象是这样的: 任何想法如何实现这一点? 问题答案: 如果创建自定义注释,则必须使用此处的 API 示例进行处理。您可以参考如何声明注释。 这是Java中的示例注释声明的样子。 并被称为。 表示您想在运行时保留注释,并且可以在运行时访问它。 表示您