最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring中獲取Bean方法上的自定義注解問題解析

 更新時間:2023年06月14日 15:07:16   作者:墨、魚  
這篇文章主要介紹了Spring中如何獲取Bean方法上的自定義注解,基本的思路就是通過Spring提供的ApplicationContext#getBeansWithAnnotation+反射來實現(xiàn),需要的朋友可以參考下

背景描述

項目中需要掃描出來所有 標注了自定義注解A的Service里面標注了自定義注解B的方法 來做后續(xù)處理。

基本的思路就是通過Spring提供的ApplicationContext#getBeansWithAnnotation+反射 來實現(xiàn)。

但是,隨著在Service里面引入了聲明式事務(wù)(@Transactional),上述的方法也就隨之失效。

場景復(fù)現(xiàn)

這里通過構(gòu)造一個case來說明問題

Service上的注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface MyService {
}

方法上的注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface MyAnno {
    String value() default "";
}

Service代碼

public interface UserService {
    void print();
}
@MyService
@Component("annoUserService")
public class UserServiceImpl implements UserService {
    @Override
    @MyAnno("xujianadgdgagg")
    public void print() {
        System.out.println("寫入數(shù)據(jù)庫");
    }
}

自定義注解掃描代碼

@Component
public class FindAnnotationService {
    @Autowired
    private ApplicationContext applicationContext;
    @PostConstruct
    public void init() {
    // 獲取帶有自定義注解的bean
        Map<String, Object> beanMap = applicationContext.getBeansWithAnnotation(MyService.class);
        for (Object bean : beanMap.values()) {
            Class<?> clazz = bean.getClass();
            Method[] declaredMethods = clazz.getDeclaredMethods();
            for (Method declaredMethod : declaredMethods) {
            // 尋找?guī)в凶远x注解的方法
            if (declaredMethod.isAnnotationPresent(MyAnno.class)) {
            // 如果方法上有自定義注解,則獲取這個注解
                MyAnno annotation = declaredMethod.getDeclaredAnnotation(MyAnno.class);
                System.out.println(annotation.value());
            }
        }
    }
}

測試類

@SpringBootTest
public class FindAnnotationServiceTests {
    @Autowired
    private UserService annoUserService;
    @Test
    public void testPrint() {
        annoUserService.print();
    }
}

當(dāng)對UserServiceImpl#print()方法加上@Transactional注解時,上面獲取bean的地方,拿到的已經(jīng)不是UserServiceImpl對象了,而是一個CGLIB代理類,如下所示:

在這里插入圖片描述

我們都知道Spring確實會為聲明式事物生成代理類。

對這個代理類通過反射并沒有獲取到帶有自定義注解的方法。

問題追蹤

最直接的原因推測是生成的代理類并不包含原始類中用戶自定義的注解。

CGLIB動態(tài)代理以及生成的代理類可以參考《深入理解JVM字節(jié)碼》。

為了驗證猜想,我們自己手動為UserServiceImpl生成一個CGLIB代理類,同時去掉@Transactional注解。
這里通過BeanPostProcessor創(chuàng)建代理類:

@Component
public class MyBeanPostProcess implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof UserService) {
            // CGLIB動態(tài)代理
            MyMethodInterceptor myMethodInterceptor = new MyMethodInterceptor();
            myMethodInterceptor.setTarget(bean);
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(bean.getClass());
            enhancer.setCallback(myMethodInterceptor);
            return enhancer.create();
        } else {
            return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
        }
    }
}
public class MyMethodInterceptor implements MethodInterceptor {
    private Object target;
    public void setTarget(Object target) {
        this.target = target;
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib增強目標方法");
        return method.invoke(target,objects);
    }
}

結(jié)果跟剛才一樣,由于生成了代理類而獲取不到自定義注解。

解決方案

既然CGLIB代理類是罪魁禍首,那就得從它下手。

由于CGLIB生成的代理類繼承了原始類,那在拿到這個代理類的時候,去找到它的父類(原始類),不就可以拿到自定義注解了嗎?

對代碼作如下改動:

@Component
public class FindAnnotationService {
    @Autowired
    private ApplicationContext applicationContext;
    @PostConstruct
    public void init() {
    // 獲取帶有自定義注解的bean
        Map<String, Object> beanMap = applicationContext.getBeansWithAnnotation(MyService.class);
        for (Object bean : beanMap.values()) {
            Class<?> clazz = bean.getClass();
            // 獲取父類(代理類的原始類)
            clazz = clazz.getSuperclass();
            Method[] declaredMethods = clazz.getDeclaredMethods();
            for (Method declaredMethod : declaredMethods) {
            // 尋找?guī)в凶远x注解的方法
            if (declaredMethod.isAnnotationPresent(MyAnno.class)) {
                MyAnno annotation = declaredMethod.getDeclaredAnnotation(MyAnno.class);
                System.out.println(annotation.value());
            }
        }
    }
}

在這里插入圖片描述

這樣果然拿到了自定義注解。

對于這種情況,Spring早已預(yù)判到了,并提供了一個工具方法AnnotationUtils.findAnnotation用來獲取bean方法上的注解,不管這個bean是否被代理。

通過這個工具方法優(yōu)化代碼如下:

@Component
public class FindAnnotationService {
    @Autowired
    private ApplicationContext applicationContext;
    @PostConstruct
    public void init() {
        Map<String, Object> beanMap = applicationContext.getBeansWithAnnotation(MyService.class);
        for (Object bean : beanMap.values()) {
            Class<?> clazz = bean.getClass();
            Method[] declaredMethods = clazz.getDeclaredMethods();
            for (Method declaredMethod : declaredMethods) {
            if (declaredMethod.isAnnotationPresent(MyAnno.class)) {
                MyAnno annotation = declaredMethod.getDeclaredAnnotation(MyAnno.class);
                System.out.println(annotation.value());
            }
        }
    }
}

擴展思考

既然CGLIB動態(tài)代理有這種問題,那JDK動態(tài)代理呢?

手動為UserServiceImpl生成JDK動態(tài)代理:

@Component
public class MyBeanPostProcess implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof UserService) {
            // JDK動態(tài)代理
            MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
            myInvocationHandler.setTarget(bean);
            return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), bean.getClass().getInterfaces(), myInvocationHandler);
        } else {
            return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
        }
    }
}
public class MyInvocationHandler implements InvocationHandler {
    private Object target;
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("增強目標方法");
        return method.invoke(target,args);
    }
    public void setTarget(Object target) {
        this.target = target;
    }
}

在不使用AnnotationUtils.findAnnotation的時候果然還是獲取不到自定義注解。

但是加上AnnotationUtils.findAnnotation以后發(fā)現(xiàn)還是獲取不到?。。?/p>

為了探究原因,對AnnotationUtils.findAnnotation源碼作簡要分析以后發(fā)現(xiàn):

AnnotationsScanner#processMethodHierarchy(C context, int[] aggregateIndex, Class<?> sourceClass, AnnotationsProcessor<C, R> processor, Method rootMethod, boolean includeInterfaces)

            // 如果當(dāng)前代理類實現(xiàn)了接口(JDK動態(tài)代理方式)
            if (includeInterfaces) {
                Class[] var14 = sourceClass.getInterfaces();
                var9 = var14.length;
                for(var10 = 0; var10 < var9; ++var10) {
                    Class<?> interfaceType = var14[var10];
                    // 對實現(xiàn)的接口遞歸尋找注解
                    R interfacesResult = processMethodHierarchy(context, aggregateIndex, interfaceType, processor, rootMethod, true);
                    if (interfacesResult != null) {
                        return interfacesResult;
                    }
                }
            }
            // 如果當(dāng)前代理類有父類(CGLIB動態(tài)代理方式)
            Class<?> superclass = sourceClass.getSuperclass();
            if (superclass != Object.class && superclass != null) {
                // 對父類遞歸尋找注解
                R superclassResult = processMethodHierarchy(context, aggregateIndex, superclass, processor, rootMethod, includeInterfaces);
                if (superclassResult != null) {
                    return superclassResult;
                }
            }

我們知道CGLIB代理是基于繼承原始類來實現(xiàn)的,而JDK代理是基于實現(xiàn)接口來實現(xiàn)的。

從上面的源碼可以大致判斷出:對于CGLIB代理通過遞歸搜尋父類來找注解;對于JDK代理通過遞歸搜尋實現(xiàn)的接口來找注解。

那么在使用JDK生成代理的時候,把自定義注解放在接口UserService的方法上,而不是實現(xiàn)類UserServiceImpl上:

public interface UserService {
    @MyAnno("xujianadgdgagg")
    void print();
}

這樣就可以通過AnnotationUtils.findAnnotation成功獲取自定義注解了~

其實現(xiàn)在Spring大部分都是通過CGLIB生成的代理,所以無需將自定義注解放在接口上,畢竟放在實現(xiàn)類上才是常規(guī)操作。

到此這篇關(guān)于Spring中如何獲取Bean方法上的自定義注解的文章就介紹到這了,更多相關(guān)Spring Bean注解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java對象克隆實現(xiàn)方法詳解

    java對象克隆實現(xiàn)方法詳解

    這篇文章主要給大家介紹了關(guān)于java對象克隆實現(xiàn)的相關(guān)資料,克隆就是復(fù)制一個對象的副本,Java支持我們對一個對象進行克隆,通常用在裝飾模式和原型模式中,需要的朋友可以參考下
    2023-06-06
  • RabbitMQ?延遲隊列實現(xiàn)訂單支付結(jié)果異步階梯性通知(實例代碼)

    RabbitMQ?延遲隊列實現(xiàn)訂單支付結(jié)果異步階梯性通知(實例代碼)

    這篇文章主要介紹了RabbitMQ?延遲隊列實現(xiàn)訂單支付結(jié)果異步階梯性通知,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-02-02
  • dubbo服務(wù)整合zipkin詳解

    dubbo服務(wù)整合zipkin詳解

    這篇文章主要介紹了dubbo服務(wù)整合zipkin,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • 解決spring.thymeleaf.cache=false不起作用的問題

    解決spring.thymeleaf.cache=false不起作用的問題

    這篇文章主要介紹了解決spring.thymeleaf.cache=false不起作用的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • SpringCloud之consul服務(wù)注冊與發(fā)現(xiàn)、配置管理、配置持久化方式

    SpringCloud之consul服務(wù)注冊與發(fā)現(xiàn)、配置管理、配置持久化方式

    這篇文章主要介紹了SpringCloud之consul服務(wù)注冊與發(fā)現(xiàn)、配置管理、配置持久化方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • SpringBoot Maven Clean報錯解決方案

    SpringBoot Maven Clean報錯解決方案

    這篇文章主要介紹了SpringBoot Maven Clean報錯解決方案,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-03-03
  • Java基于LinkedHashMap實現(xiàn)LRU緩存

    Java基于LinkedHashMap實現(xiàn)LRU緩存

    在很多實際的應(yīng)用中,尤其是需要緩存數(shù)據(jù)的場景下,我們經(jīng)常會遇到?LRU緩存,LRU?緩存是通過淘汰最久未使用的緩存數(shù)據(jù)來節(jié)省內(nèi)存空間,在Java中,基于?LinkedHashMap實現(xiàn)LRU緩存是非常簡便和高效的,所以本文介紹了如何基于LinkedHashMap實現(xiàn)LRU緩存,需要的朋友可以參考下
    2025-06-06
  • Spring事務(wù)控制策略及@Transactional失效問題解決避坑

    Spring事務(wù)控制策略及@Transactional失效問題解決避坑

    這篇文章主要為大家介紹了Spring事務(wù)控制策略及@Transactional失效問題解決避坑,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-06-06
  • 聊一聊new對象與Spring對bean的初始化的差別

    聊一聊new對象與Spring對bean的初始化的差別

    這篇文章主要介紹了聊一聊new對象與Spring對bean的初始化的差別,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • Java final與泛型詳細解析

    Java final與泛型詳細解析

    文章主要介紹了Java中的final關(guān)鍵字、泛型以及包裝類,final關(guān)鍵字用于確保變量、方法和類不會被修改,本文結(jié)合實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2025-11-11

最新評論

全州县| 西林县| 诸暨市| 红原县| 临西县| 怀来县| 东城区| 台北县| 桂林市| 唐海县| 乌鲁木齐市| 酒泉市| 余庆县| 定远县| 绍兴市| 大城县| 上犹县| 南郑县| 宿州市| 商丘市| 柳江县| 屯留县| 朝阳县| 榆林市| 房产| 桃源县| 易门县| 盐池县| 外汇| 隆昌县| 攀枝花市| 榆林市| 石门县| 太湖县| 库车县| 车险| 东至县| 河北区| 台山市| 汾阳市| 富民县|