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

前言

需求:
用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. Spring通过Spring收集定义注解标识方法

    2024-04-01 13:14:03       19 阅读
  2. Spring定义注解

    2024-04-01 13:14:03       17 阅读
  3. Spring Boot 中定义中文校验注解实现

    2024-04-01 13:14:03       34 阅读
  4. Spring Boot编写定义校验注解

    2024-04-01 13:14:03       40 阅读
  5. Spring Boot中定义注解来统计方法执行时间

    2024-04-01 13:14:03       10 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-01 13:14:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-01 13:14:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-01 13:14:03       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-01 13:14:03       20 阅读

热门阅读

  1. 03-28 周四 Linux 并行工具使用xargs和parallel

    2024-04-01 13:14:03       20 阅读
  2. 装饰器模式:灵活增强功能的利器

    2024-04-01 13:14:03       16 阅读
  3. 手机投屏到电脑

    2024-04-01 13:14:03       18 阅读
  4. Leetcode 2810. 故障键盘

    2024-04-01 13:14:03       18 阅读
  5. Python PyQt5——QThread使用方法与代码实践

    2024-04-01 13:14:03       14 阅读
  6. Beginning of Device Change operation

    2024-04-01 13:14:03       16 阅读
  7. 安卓开发中的LiveData深度解析与实践

    2024-04-01 13:14:03       14 阅读
  8. 学会了JsonPath,你的Python接口脚本才算完整

    2024-04-01 13:14:03       14 阅读