一文带你了解Spring是如何整合mybatis、feign等接口的
spring 整合mybatis和OpenFeign时,都是通过接口来实现的,如:
@Repository
public interface OrderMapper {
    int deleteByPrimaryKey(Long codId);
}
@FeignClient("coupon")
public interface MmsService {
    @RequestMapping("/coupon/spubounds/save")
    R saveSpuBounds(@RequestBody SpuBoundsDTO spuBounds);
}
spring容器的bean默认是不支持接口或者抽象类的,那上面的接口是如何整合到spring中的呢?
在回答上述问题前,我们需要了解spring容器的一些入门知识点:
- BeanDefinition (创建bean的模板)
 我们知道,一个class对象可以通过new的方式创建一个对象,但在spring中,这样简单的通过Class对象去创建Bean是远远不够的,例如一个Bean需要有名称BeanName,需要有属性判断是否是单例,需要有属性判断是否懒加载,需要在对象创建完成后,执行初始化方法 等,简单的用一个class对象来描述,不足以实现这个功能,因此spring 使用 BeanDefinition来描述这些特征
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor implements BeanDefinition {
 
    @Nullable
    private volatile Object beanClass;  // 原始的class对象
    @Nullable
    private String scope;
    private boolean abstractFlag;
    private boolean lazyInit; //是否懒加载
    private int autowireMode;
    private int dependencyCheck;
    @Nullable
    private String[] dependsOn;
    private boolean autowireCandidate;
    private boolean primary; //如果容器中有多个相同类型的bean,当其他bean注入该类型的Bean时,以哪个为准
    private final Map<String, AutowireCandidateQualifier> qualifiers;
    @Nullable
    private String factoryBeanName; //如果是@Bean方式创建,这里指的是工厂Bean的名称
    @Nullable
    private String factoryMethodName; //如果是@Bean方式创建,这里指的是工厂Bean对应的方法
    @Nullable
    private String initMethodName; //初始化方法
    @Nullable
    private String destroyMethodName; //销毁方法
    ... ...
}
所以,spring创建Bean需要先创建BeanDefitition对象
| 模板 | 对象 | 说明 | 
|---|---|---|
| Class对象 | Object对象 | Object对象通过Class对象创建 | 
| BeanDefinition对象 | bean | bean是通过 BeanDefinition对象创建 | 
spring 创建Bean的简单过程
- 
首先扫描指定路径下的.class结尾的文件,然后用类加载器加载成class对象 
- 
判断这些class对象是否需要创建Bean,通过注解过滤器来实现,如有没@Component注解(第一轮过滤方法) 
- 
之后对找到的class对象,再加一次判断,如判断是否接口,抽象类等,(会忽略接口和抽象类)(第二轮过滤方法) 
- 
将这些需要创建Bean的class对象封装成一个个 BeanDefinition对象,并且用一个Map<String, BeanDefinition>保存起来 
- 
上一步的map,key为Bean的名称,spring还使用一个List 集合保存了所有BeanName,然后遍历list集合,通过map找到 
 一个个的BeanDefinition对象,然后创建出对应的Bean,如果是单例Bean,最终会保存到一个Map<String,Object> singleObjects这个Map集合中,key为Bean的名字
- 
FactoryBean 
public interface FactoryBean<T> {
    @Nullable
    T getObject() throws Exception;
    @Nullable
    Class<?> getObjectType();
    default boolean isSingleton() {
        return true;
    }
}
spring专门提供了FactoryBean
当你通过BeanName获取对应的Bean时,会返回getObject()的执行结果,当你通过类型T去获取Bean时,
spring 会遍历 Map<String, BeanDefinition> map,然后判断 BeanDefinition中的BeanClass是不是FactoryBean,
如果是的话,就会调用 getObjectType()方法返回的类似跟类型T比较,相等就返回getObject()对应的对象; 下面通过一个Mapper接口简单讲解其实现:
@Repository
public interface OrderMapper {
    int deleteByPrimaryKey(Long codId);
}
public class MapperFactoryBean implements FactoryBean<OrderMapper> {
    private Class<OrderMapper> aClass;
    public MapperFactoryBean(Class<OrderMapper> aClass) {
        this.aClass = aClass;
    }
    @Override
    public OrderMapper getObject() throws Exception {
        Object proxyInstance = Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{aClass}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return null;
            }
        });
        return (OrderMapper) proxyInstance;
    }
    @Override
    public Class<?> getObjectType() {
        return aClass;
    }
}
public class AService
{
    @Autowired
    private OrderMapper orderMapper;
}
上述,MapperFactoryBean通过构造器传入参数OrderMapper.class,让它们产生了关联,在spring的单例池Map<String,Object> singleObjects中,beanName为orderMapper,Object对象为MapperFactoryBean的实列,当AService中注入OrderMapper接口时,
spring会遍历singleObjects,然后判断对应的Object对象是不是FactoryBean,是的话,就会调用 getObjectType()获得对应的类型,然后跟OrderMapper.class比较,如果相等,就会调用 getObject()方法对应的对象,该方法最终返回了OrderMapper接口的代理类,从而实现了接口在spring中的管理
spring 是如何查找哪些类需要创建Bean的呢?
通过前面知道,接口是通过FactoryBean来注入spring容器的,在bean创建的过程中,需要判断哪些Class对象需要注入spring容器,spring先通过扫描器,扫描所有的class对象,然后将它们封装成BeanDefinition,最后再创建对应的Bean,默认的扫描器是不支持接口或者抽象类的,因此,对于接口,需要覆盖对应的方法
- 扫描器的顶级父类:ClassPathScanningCandidateComponentProvider
public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
    static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
    protected final Log logger;
    private String resourcePattern;
    private final List<TypeFilter> includeFilters;
    private final List<TypeFilter> excludeFilters;
   ... ...
   
   //扫描给定的包名下的Class对象,如@MapperScan和 @ComponentScan中指定的包名
    private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {
		   //将包名转成文件路径,并且通过流的方式读取所有.class文件
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + '/' + this.resourcePattern;
			Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
		for (Resource resource : resources) {
			
		if (resource.isReadable()) {
			try {
				MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
				//第一轮过滤方法,通过过滤器判断 如:
				@MapperScan(basePackages = { "com.yang" },annotationClass = Repository.class),可以指定只有带@Repository注解的类或者接口
				if (isCandidateComponent(metadataReader)) {
					ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
					sbd.setResource(resource);
					sbd.setSource(resource);
					//第二轮过滤方法,判断上一轮过滤后的class对象是否接口或者抽象类
					if (isCandidateComponent(sbd)) {
						candidates.add(sbd);
					 }
			
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
		}
		return candidates;
	}
	
//这是上面的第一轮过滤方法,排除指定路径下的class对象,哪些需要包含,哪些需要排除,
这里一般是指带某种注解的,如@Component注解	
  protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
		for (TypeFilter tf : this.excludeFilters) {
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return false;
			}
		}
		for (TypeFilter tf : this.includeFilters) {
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return isConditionMatch(metadataReader);
			}
		}
		return false;
	}
	
	//第二轮过滤器,默认class对象不能是抽象的,如果是抽象的,必须方法含有@Lookup注解
	protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
		AnnotationMetadata metadata = beanDefinition.getMetadata();
		return (metadata.isIndependent() && (metadata.isConcrete() ||
				(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
	}	
	
	
	 ... ...
}
小结:由上面的扫描器知道,spring默认会将接口和抽象类过滤掉,即使加了这些接口或者抽象类加了@componet @Configuration等注解,因此,对于接口,需要重新写个扫描器,重写第二轮过滤方法
- spring 扫描器的默认实现 ClassPathBeanDefinitionScanner,它没改变父类的过滤逻辑
public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		for (String basePackage : basePackages) {
		   //调用父类的扫描方法,获得BeanDefinition对象
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
		  ... ...
		}
		return beanDefinitions;
	}
    
    //这是父类的方法
    public Set<BeanDefinition> findCandidateComponents(String basePackage) {
		if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
			return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
		}
		else {
		   //这里调用的是父类的那个扫描方法
			return scanCandidateComponents(basePackage);
		}
	}
    
}
- Mybatis中Mapper接口的扫描器:ClassPathMapperScanner
//继承spring默认的扫描器
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
     public Set<BeanDefinitionHolder> doScan(String... basePackages) {
       //调用的是父类的扫描方法
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
        if (beanDefinitions.isEmpty()) {
            ... ...
        } else {
            //扫描到的BeanDefinition是接口,需要转成FactoryBean
            this.processBeanDefinitions(beanDefinitions);
        }
        return beanDefinitions;
    }
    
    //重点在这里,重写了父类的第二轮过滤器,判断只有接口才是我们要的
     protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
    }
    
//重点:需要将BeanDefinion中的类型替换成FactoryBean    
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();
      //旧的接口,作为构造器的参数
      definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); 
      //将类替换成FactoryBean
      definition.setBeanClass(this.mapperFactoryBean.getClass());
     definition.getPropertyValues().add("addToConfig", this.addToConfig);
     
      }
    }
  }
    
}
//小结:spring整合mybatis,第一步需要重写扫描器的第二轮过滤方法,改成只有接口才能通过,其次,将符合要求的
BeanDefinition对象中的类型换成FactoryBean,之前的class类型,需要作为构造器参数传入
- spring整合Fegin的扫描器:通过匿名内部类实现:
在FeignClientsRegistrar这个类中:
  protected ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
            //核心点,放开了父类中接口和抽象类的限制条件
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                boolean isCandidate = false;
                if (beanDefinition.getMetadata().isIndependent() && !beanDefinition.getMetadata().isAnnotation()) {
                    isCandidate = true;
                }
                return isCandidate;
            }
        };
    }
//扫描器似乎没有指定Feign一定是接口,并且也没实现将BeanDefinition中的class转成FactoryBean的逻辑,那它们在哪里实现
,这一切都在FeignClientsRegistrar中实现
	public void registerFeignClients(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
		ClassPathScanningCandidateComponentProvider scanner = getScanner();
		
		Set<String> basePackages;
		Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
		//扫描器第一轮过滤方法指定的过滤器,只包含@FeignClient注解的类
		AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(FeignClient.class);
		final Class<?>[] clients = attrs == null ? null
				: (Class<?>[]) attrs.get("clients");
		if (clients == null || clients.length == 0) {
			scanner.addIncludeFilter(annotationTypeFilter);
			basePackages = getBasePackages(metadata);
		}
		else {
			final Set<String> clientClasses = new HashSet<>();
			basePackages = new HashSet<>();
			for (Class<?> clazz : clients) {
				basePackages.add(ClassUtils.getPackageName(clazz));
				clientClasses.add(clazz.getCanonicalName());
			}
			AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
				@Override
				protected boolean match(ClassMetadata metadata) {
					String cleaned = metadata.getClassName().replaceAll("\\$", ".");
					return clientClasses.contains(cleaned);
				}
			};
			//给扫描器第一轮过滤方法指定过滤器
			scanner.addIncludeFilter(
					new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
		}
		for (String basePackage : basePackages) {
		  //调用父类的扫描方法,得到BeanDefinition对象
			Set<BeanDefinition> candidateComponents = scanner
					.findCandidateComponents(basePackage);
			for (BeanDefinition candidateComponent : candidateComponents) {
				if (candidateComponent instanceof AnnotatedBeanDefinition) {
					// verify annotated class is an interface
					AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
					AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
					
					//重点,前面说过,扫描器第二轮过滤方法,没有指定一定是接口,所以这里用了断言来判断
					Assert.isTrue(annotationMetadata.isInterface(),
							"@FeignClient can only be specified on an interface");
					Map<String, Object> attributes = annotationMetadata
							.getAnnotationAttributes(
									FeignClient.class.getCanonicalName());
					String name = getClientName(attributes);
					registerClientConfiguration(registry, name,
							attributes.get("configuration"));
                    //这里是将其转成FactoryBean
					registerFeignClient(registry, annotationMetadata, attributes);
				}
			}
		}
	} 
	
	//这里是将其转成FactoryBean
	private void registerFeignClient(BeanDefinitionRegistry registry,
			AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
		String className = annotationMetadata.getClassName();
		
		//直接创建一个FactoryBean
		BeanDefinitionBuilder definition = BeanDefinitionBuilder
				.genericBeanDefinition(FeignClientFactoryBean.class);
		
		definition.addPropertyValue("url", getUrl(attributes));
		definition.addPropertyValue("path", getPath(attributes));
		String name = getName(attributes);
		definition.addPropertyValue("name", name);
		//通过属性,将原来接口的类型注入到FactoryBean中,后续其getObject方法就可以返回代理类了
		definition.addPropertyValue("type", className);
		... ...
		BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
				new String[] { alias });
		BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
	}
  
    
    
总结: spring整合Fegin接口,意义上通过改变扫描器的第二轮过滤方法来实现接口的扫描,之后也会转成对应的FactoryBean
思考:如果我们想实现类似Mapper接口,Fegin接口的方式,你会了吗?
 
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号