[英]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.