繁体   English   中英

我的BeanPostProcessor在Spring Boot中不起作用

[英]My BeanPostProcessor doesn't work in Spring Boot

我编写了BeanPostProcessor,以便所有标有@Timing批注的方法在控制台中显示其执行时间。

我使用Spring Boot。

我的BeanPostProcessor看起来像这样:

    import com.example.version2.annotation.Timing;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    import org.springframework.stereotype.Component;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;

    @Component
    public class TimingBeanPostProcessor implements BeanPostProcessor {

        @Override
        public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
            Class type = bean.getClass();
            Method[] methods = type.getMethods();
            for (Method method : methods) {
                if (method.isAnnotationPresent(Timing.class)) {
                    Object proxy = Proxy.newProxyInstance(type.getClassLoader(),type.getInterfaces(), new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            long before = System.nanoTime();
                            Object retVal = method.invoke(bean, args);
                            long after = System.nanoTime();
                            System.out.println("Method worked: " + (after - before) + " nano seconds");
                            return retVal;
                        }
                    });
                    return proxy;
                } else {
                    return bean;
                }
            }

             return bean;
        }

        @Override
        public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
            return bean;
        }

    }

这是我的注释@Timing

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface Timing {
}

我在dao类中的一种方法上声明了这个注释:

@Timing
public  List<Map<String, Object>> selectQuery() {
    String selectQuery = prop.getMYSQL_SELECT();
    return mysqlTemplate.queryForList(selectQuery);
}

当应用程序启动时,没有问题,但是当我执行请求时,控制台中什么也没有。 看来BeanPostProcessor本身编写正确。 我找不到错误所在。

我还想知道如何将有关方法执行时间的信息传输到json或某些List(不重要)中的前端。

我通常为此使用方面

@Aspect
public class TimedAspect {

    @Around("@annotation(some.thing.Timed)")
    public Object timeSomething(ProceedingJoinPoint joinPoint) throws Throwable {
        final long before = System.nanoTime();
        final Object returnValue = joinPoint.proceed()
        final long after = System.nanoTime();
        System.out.println("Method worked: " + (after - before) + " nano seconds");
        return returnValue;       
    }
}

时控

package some.thing;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Timed {


}

依赖性:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

有关Spring AOP的一般信息

春季AOP

(如果该代码有效,我尚未尝试从我的项目中复制粘贴的内容)

首先,这种功能已经存在。 Spring Boot与允许这种行为的Micrometer框架集成(Spring Boot 1.x使用dropwizard度量标准以及可选的micrometer反向端口支持都允许这种声明式样式的注释)。

是千分尺文档的相关章节。

到目前为止,这是我所知道的最佳选择,但是,如果您仍然喜欢自己动手(原因可能是所有这些计量框架都围绕指标维护了一些数学模型(带有滑动窗口和所有内容),并且如果您想要更多的选择,类似于出于调试目的之类的概要分析,那么您可以考虑自己做一些事情。

现在,其他答案暗示了有关Spring AOP的信息。 我(这只是我的看法)认为,在这种情况下,使用bean后处理器比AOP具有优势。 首先,也许您根本不使用spring AOP,而仅使用普通spring。 选择这种实现方式的第二个原因是性能,AOP向Stack添加了很多调用。 AOP的明显优势是实现的简单性。

因此,假设您确实要使用BPP方式:

我认为首先,您应该检查Bean Post Processor在应用程序启动期间是否在春季之前被“识别”。

为了检查这一点,您可以在BPP中创建一个无参构造器,并在其中打印“ Hello from BPP”之类的内容,或使用调试器。

现在,关于建议的实现:您只需遍历方法并仅创建一次代理。 没有必要在代理之上创建代理。...因此,所提供的代码是错误的。

我认为您应该遍历方法,准备方法列表并记住该方法集,然后创建一个具有invoke方法的代理,该方法将检查该方法是否在方法集中,如果是,则使用代理魔术,否则将调用委托给基础bean。

当您采用这种方式时,应牢记两件事:

  • 代理仅适用于接口,不适用于实际的类。 如果您有一个课程并且无法使用该界面,则需要弄弄CGLIB

  • 其他bean后处理器也可以将您的bean包装在某种代理中,例如,如果测量带有@Transactional注释的方法,该怎么办?

您正在遍历所有方法,但是如果第一个方法没有Timing注释,则返回Bean:

for (Method method : methods) {
    if (method.isAnnotationPresent(Timing.class)) {
        Object proxy = ...
         return proxy;
     } else {
         return bean;
     }

这意味着只有在找到第一个方法作为注释时,您才可以创建自定义代理。

您可以摆脱else子句,并让for循环之后的return bean处理没有方法具有注释的情况。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM