簡體   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