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

Spring中獲取Bean方法上的自定義注解問(wèn)題解析

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

背景描述

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

基本的思路就是通過(guò)Spring提供的ApplicationContext#getBeansWithAnnotation+反射 來(lái)實(shí)現(xiàn)。

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

場(chǎng)景復(fù)現(xiàn)

這里通過(guò)構(gòu)造一個(gè)case來(lái)說(shuō)明問(wèn)題

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ù)庫(kù)");
    }
}

自定義注解掃描代碼

@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)) {
            // 如果方法上有自定義注解,則獲取這個(gè)注解
                MyAnno annotation = declaredMethod.getDeclaredAnnotation(MyAnno.class);
                System.out.println(annotation.value());
            }
        }
    }
}

測(cè)試類

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

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

在這里插入圖片描述

我們都知道Spring確實(shí)會(huì)為聲明式事物生成代理類。

對(duì)這個(gè)代理類通過(guò)反射并沒(méi)有獲取到帶有自定義注解的方法。

問(wèn)題追蹤

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

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

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

@Component
public class MyBeanPostProcess implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof UserService) {
            // CGLIB動(dòng)態(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增強(qiáng)目標(biāo)方法");
        return method.invoke(target,objects);
    }
}

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

解決方案

既然CGLIB代理類是罪魁禍?zhǔn)祝蔷偷脧乃率帧?/p>

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

對(duì)代碼作如下改動(dòng):

@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());
            }
        }
    }
}

在這里插入圖片描述

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

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

通過(guò)這個(gè)工具方法優(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());
            }
        }
    }
}

擴(kuò)展思考

既然CGLIB動(dòng)態(tài)代理有這種問(wèn)題,那JDK動(dòng)態(tài)代理呢?

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

@Component
public class MyBeanPostProcess implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof UserService) {
            // JDK動(dòng)態(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("增強(qiáng)目標(biāo)方法");
        return method.invoke(target,args);
    }
    public void setTarget(Object target) {
        this.target = target;
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

相關(guān)文章

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

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

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

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

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

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

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

    解決spring.thymeleaf.cache=false不起作用的問(wèn)題

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

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

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

    SpringBoot Maven Clean報(bào)錯(cuò)解決方案

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

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

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

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

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

    聊一聊new對(duì)象與Spring對(duì)bean的初始化的差別

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

    Java final與泛型詳細(xì)解析

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

最新評(píng)論

湖北省| 旬阳县| 德兴市| 芮城县| 始兴县| 大港区| 泰宁县| 柘城县| 高青县| 固安县| 乡宁县| 河西区| 怀宁县| 增城市| 本溪| 潞城市| 大新县| 铅山县| 天水市| 黑河市| 竹山县| 建宁县| 石嘴山市| 瓦房店市| 石楼县| 云龙县| 得荣县| 平昌县| 岳阳县| 淮北市| 左权县| 鄂州市| 永顺县| 昌都县| 鹤山市| 阜新市| 镇安县| 太和县| 田东县| 土默特右旗| 德江县|