栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

spring-boot动态注入interface并实现定义的方法

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

spring-boot动态注入interface并实现定义的方法

编写自定义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 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 annotationClass;
  private Class> 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 annotationClass) {
    this.annotationClass = annotationClass;
  }

  public void setFactoryBeanClass(Class> 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 annotationClass;
  private Class> factoryBeanClass;

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

  public void setAnnotationClass(Class annotationClass) {
    this.annotationClass = annotationClass;
  }

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

  @Override
  protected Set doScan(String... basePackages) {
    Set 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 implements FactoryBean {

  private Class mapperInterface;

  MyMapperFactoryBean() {

  }

  MyMapperFactoryBean(Class mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

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

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

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

实现Proxy代理handler
public class MyMapperImpl 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
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/777451.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号