[英]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的一般信息
(如果该代码有效,我尚未尝试从我的项目中复制粘贴的内容)
首先,这种功能已经存在。 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.