【Spring】通过Spring收集自定义注解标识的方法

文章介绍了如何使用Spring注解创建自定义组件和方法,通过BeanFactoryPostProcessor在Spring容器启动后收集带有特定注解的类和方法,实现通过键值(key)动态查找方法的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

需求:
用key找到对应的方法实现。使用注解的形式增量开发。

@MyComponent
public class Sample1 {

    @MyMethod(key = "key1")
    public String test2() {
        return "Shenzhen";
    }
}

任意时刻都能通过key来进行依赖查找

    @Test
    public void test() {
        Assert.notNull(myBeanFactory.getMethod("key1"), "key1对应的方法不能为空");
    }

实现思路:

  1. 声明自己的类注解,并要求被 Spring 收集
  2. 声明自己的方法注解,确保可以通过反射获取
  3. 借 Spring 的能力,容器启动收集bean完成后,把bean列表交给自己,用于自己的收集策略。

1. 声明注解

  • 类注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 继承Spring的注解,确保类能被Spring扫描
@Component
public @interface MyComponent {
	
    @AliasFor(
            annotation = Component.class
    )
    String value() default "";
}
  • 方法注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyMethod {
	// 用于依赖查找的key
    String key();
}

2. 使用 Spring 的工厂拓展

找到我们用注解标记的类、方法,Spring 收集完bean之后提供了拓展点供我们遍历这些bean。

  • BeanFactoryPostProcessor 接口
  • 其中 ConfigurableListableBeanFactory beanFactory 提供了容器
@Component
public class MyBeanFactory implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
 		// 找到所有注解标记的bean
        Map<String, Object> beanMap = beanFactory.getBeansWithAnnotation(MyComponent.class);

        beanMap.forEach((beanName, bean) -> {
            // 收集bean中用注解标记的方法 (方法先省略)
            // collectMethod(bean);
        });
    }
}

3. 收集策略

  • 收集策略可以很简单,这里就用一个Map收集
  • 用Spring的AnnotationUtils.findAnnotation(method, MyMethod.class) 命中标识的方法
  • 如果命中,则拿到注解中的key,收集key和Method的映射关系
    // 自己的容器
    private static final Map<String, Method> METHOD_MAP = new HashMap<>();
	
    public  Method getMethod(String key) {
        return METHOD_MAP.get(key);
    }

    private void collectMethod(Object bean) {
        Method[] methods = bean.getClass().getDeclaredMethods();

        Arrays.stream(methods)
                // 过滤: 只要MyMethod标识的方法 
                .filter(method -> Objects.nonNull(getAnnotation(method)))
                // 收集: 通过MyMethod的注解key,绑定依赖关系,放到自己的容器
                .forEach(method -> METHOD_MAP.putIfAbsent(getAnnotation(method).key(), method));
    }

    private static MyMethod getAnnotation(Method method) {
        return AnnotationUtils.findAnnotation(method, MyMethod.class);
    }

4. 完整的代码

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class MyBeanFactory implements BeanFactoryPostProcessor {
    
    private static final ConcurrentHashMap<String, Method> METHOD_MAP = new ConcurrentHashMap<>();

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 从Spring容器中获取所有MyController
        Map<String, Object> beanMap = beanFactory.getBeansWithAnnotation(MyComponent.class);

        beanMap.forEach((beanName, bean) -> {
            // 收集bean里面的所有可用方法
            collectMethod(bean);
        });
    }

    private void collectMethod(Object bean) {
        Method[] methods = bean.getClass().getDeclaredMethods();

        Arrays.stream(methods)
                // 过滤: 只要MyMethod标识的方法 
                .filter(method -> Objects.nonNull(getAnnotation(method)))
                // 收集: 通过MyMethod的注解key,绑定依赖关系,放到自己的容器
                .forEach(method -> METHOD_MAP.putIfAbsent(getAnnotation(method).key(), method));
    }

    private static MyMethod getAnnotation(Method method) {
        return AnnotationUtils.findAnnotation(method, MyMethod.class);
    }


    public  Method getMethod(String key) {
        return METHOD_MAP.get(key);
    }
}

后记

记录下用到的 Spring 的 api 和工具

  • 一个核心的接口方法获取bean容器
    BeanFactoryPostProcessor#postProcessBeanFactory

  • 获取指定注解修饰的类
    ConfigurableListableBeanFactory#getBeansWithAnnotation

  • 获取方法上的注解内容
    AnnotationUtils#findAnnotation

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值