编写自定义spring-boot-starter实现功能

简单的说就是扫描依赖starter包下含有MyMapper注解的interface,在即将创建Bean实例前,修改BeanDefinition的BeanClass为FactoryBean接口。Bean类实现FactoryBean接口,spring-boot使用FactoryBean接口定义的工厂方法创建Bean,而不是Bean的构造方法。FactoryBean接口的实现类使用Proxy创建代理对象来替换实际的interface Bean,Proxy的handler通过拦截Method,获取Method上标记的注解来动态实现interface声明的方法。

配置spring.factories,让spring-boot自动导入AutoConfiguration

创建resource/META-INF/spring.factories,配置AutoConfiguration。

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.example.mystarter.autoconfigure.MyAutoConfiguration

spring-boot会读取这个文件的内容,读取rg.springframework.boot.autoconfigure.EnableAutoConfiguration的值来创建AutoConfiguration并注入到容器。

定义需要扫描的注解

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })
public @interface MyMapper {
  
}

定义方法注解,让Proxy动态实现接口定义的方法

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestMapping {
  String name() default "";
  @AliasFor("path")
	String[] value() default {};
  @AliasFor("value")
	String[] path() default {}; 
  RequestMethod[] method() default {};
  String[] params() default {};
}

定义需要动态注入的interface

@MyMapper
public interface DemoMapper {
  @MyRequestMapping(name = "my", path = "hello", method = RequestMethod.GET, params = {"world", "java"})
  String helloMapper();
}

实现ImportBeanDefinitionRegistrar实现动态注入

public class MyAutoConfiguredMapperScannerRegistrar implements  BeanFactoryAware, ImportBeanDefinitionRegistrar {
  private BeanFactory beanFactory;

  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    if (!AutoConfigurationPackages.has(this.beanFactory)) {
      return;
    }
    List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MyMapperScannerConfigurer.class);
    builder.addPropertyValue("annotationClass", MyMapper.class);
    builder.addPropertyValue("factoryBeanClass", MyMapperFactoryBean.class);
    builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
    registry.registerBeanDefinition(MyMapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
  }

  @Override
  public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    this.beanFactory = beanFactory;    
  }
}

List packages = AutoConfigurationPackages.get(this.beanFactory);
这里是为了获取导入自定义sping-boot-starter的工厂包名。不是自定义sping-boot-starter的包名

实现BeanDefinitionRegistryPostProcessor,扫描interface并修改类为BeanFactory类,让BeanFactory类创建interface的Proxy对象。

public class MyMapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {

  private String beanName;
  private ApplicationContext applicationContext;
  private String basePackage;
  private Class<? extends Annotation> annotationClass;
  private Class<? extends FactoryBean<?>> factoryBeanClass;

  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    // TODO Auto-generated method stub
    
  }

  @Override
  public void setBeanName(String name) {
    this.beanName = name;
  }

  public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
    this.annotationClass = annotationClass;
  }

  public void setFactoryBeanClass(Class<? extends FactoryBean<?>> factoryBeanClass) {
    this.factoryBeanClass = factoryBeanClass;
  }

  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    this.applicationContext = applicationContext;
  }

  @Override
  public void afterPropertiesSet() throws Exception {
    // TODO Auto-generated method stub
    
  }

  public void setBasePackage(String basePackage) {
    this.basePackage = basePackage;
  }

  @Override
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    MyClassPathBeanDefinitionScanner scanner = new MyClassPathBeanDefinitionScanner(registry);
    scanner.setAnnotationClass(annotationClass);
    scanner.setFactoryBeanClass(factoryBeanClass);
    scanner.registerFilters();
    scanner.scan(basePackage);
  }
}

BeanDefinitionRegistryPostProcessor接口 在注入容器的Bean的BeanDefinition创建好后还没有创建实例注入到容器前回调,在postProcessBeanDefinitionRegistry方法进一步修改BeanDefinition。这里实现BeanDefinitionRegistryPostProcessor是为了防止扫描到的interface已经被注入到容器,这时已经无法修改BeanDefinition。

实现ClassPathBeanDefinitionScanner扫描interface的BeanDefinition并修改BeanClass为FactoryBean类

public class MyClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {

  private Class<? extends Annotation> annotationClass;
  private Class<? extends FactoryBean<?>> factoryBeanClass;

  public MyClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
    super(registry, false);
  }

  public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
    this.annotationClass = annotationClass;
  }

  public void setFactoryBeanClass(Class<? extends FactoryBean<?>> factoryBeanClass) {
    this.factoryBeanClass = factoryBeanClass;
  }
  
  public void registerFilters() {
    addIncludeFilter(new AnnotationTypeFilter(annotationClass));
  }

  @Override
  protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
    for (BeanDefinitionHolder holder : beanDefinitions) {
      AbstractBeanDefinition definition = (AbstractBeanDefinition)holder.getBeanDefinition();
      String beanClassName = definition.getBeanClassName();
      definition.setBeanClass(factoryBeanClass);
      // 从构造方法传入参数
      definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
    }
    return beanDefinitions;
  }

  @Override
  protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
    AnnotationMetadata metadata = beanDefinition.getMetadata();
    return metadata.isInterface() && metadata.isIndependent();
  }
}

AnnotationTypeFilter扫描MyMapper注解的类和接口,isCandidateComponent(AnnotatedBeanDefinition beanDefinition)回调,再次判断beanDefinition是否需要实例化为组件并注入到容器。这里只包含interface类型且标记MyMapper注解。

实现FactoryBean,创建代理对象并注入到容器

public class MyMapperFactoryBean<T> implements FactoryBean<T> {

  private Class<T> mapperInterface;

  MyMapperFactoryBean() {

  }

  MyMapperFactoryBean(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  @Override
  public T getObject() throws Exception {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, new MyMapperImpl<T>());
  }

  @Override
  public Class<?> getObjectType() {
    return this.mapperInterface;
  }
  
}

FactoryBean接口标记这个Bean类是具有工厂方法的Bean类,直接通过工厂方法创建Bean,不通过构造方法创建。这里的Bean类就是之前扫描的interface

实现Proxy代理handler

public class MyMapperImpl<T> implements InvocationHandler {


  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (Object.class.equals(method.getDeclaringClass())) {
      return method.invoke(this, args);
    } else {
      if (method.isAnnotationPresent(MyRequestMapping.class)) {
        MyRequestMapping mapping = method.getAnnotation(MyRequestMapping.class);
        String pathString = "";
        for (String path : mapping.path()) {
            pathString += (path + " ");
        }
        String methodString = "";
        for (RequestMethod m : mapping.method()) {
          methodString += (m.name() + " ");
        }
        String paramsString = "";
        for (String param : mapping.params()) {
          paramsString += (param + " ");
        }

        return String.format("name=%s path=%s method=%s params=%s", mapping.name(), pathString, methodString, paramsString);
      }
      return "hello world";
    }
  }
  
}

代理handler根据代理的interface类的方法注解,动态实现interface的方法。

最终效果

@EnableAutoConfiguration
@SpringBootTest(classes = MyAutoConfiguration.class)
class MystarterApplicationTests {

	@Resource
	DemoMapper mapper;

	@Test
	void contextLoads() {
		System.out.println(mapper.helloMapper());
	}

}

输出

name=my path=hello  method=GET  params=world java
GitHub 加速计划 / sp / spring-boot
40
9
下载
spring-projects/spring-boot: 是一个用于简化Spring应用开发的框架。适合用于需要快速开发企业级Java应用的项目。特点是可以提供自动配置、独立运行和内置的Tomcat服务器,简化Spring应用的构建和部署。
最近提交(Master分支:3 个月前 )
54fc77b5 This commit improves the builder to provide the message factory upfront rather than setting it after the WebServiceTemplate has been instantiated. By providing the factory upfront (if it is set), it prevents the default strategy to be created first to be then erased by the custom factory. Closes gh-48615 3 天前
a00f6c0b - 3 天前
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐