@Mapper和@Reference

关于Mybatis中的@Mapper以及Dubbo中的@Reference


在应用层面,在Mybatis中的@Mapper以及Dubbo中的@Reference有着异曲同工之妙。在Mybatis中,在Dao接口上加上@Mapper注解,就可以通过@Autowired注入bean,而XML文件通常放在resource目录中;在Dubbo中,consumer端通过@Reference注入bean就能使用接口中定义的service bean,而service impl通常放在provider端。但是@Mapper和@Reference的实现大不相同,先来看看@Mapper是如何实现的。

@Mapper

首先MapperScannerConfigurer类实现了BeanDefinitionRegistryPostProcessor接口,并且重写postProcessBeanDefinitionRegistry()方法,在该方法中可以通过传入的参数BeanDefinitionRegistry来更改beanDefinition。

public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor{
   public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
	   ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); 
	   scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
   }
}

scanner.scan()会调用ClassPathCopycatServiceApiScanner的doScan()方法

public class ClassPathCopycatServiceApiScanner extends ClassPathBeanDefinitionScanner {
	public ClassPathMapperScanner(BeanDefinitionRegistry registry) {  
	  super(registry, false);  
	}
	
	@Override  
	public Set<BeanDefinitionHolder> doScan(String... basePackages) {  
	  Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);  
	  
	  if (beanDefinitions.isEmpty()) {  
	    LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)  
	        + "' package. Please check your configuration.");  
	  } else {  
	    processBeanDefinitions(beanDefinitions);  
	  }  
	  
	  return beanDefinitions;  
	}
	
	private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
	for (BeanDefinitionHolder holder : beanDefinitions) {
		AbstractBeanDefinition definition = (AbstractBeanDefinition) holder.getBeanDefinition();
		String beanClassName = definition.getBeanClassName();
		definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
		definition.setBeanClass(this.mapperFactoryBeanClass);
	}
	}
}

this.basePackage就是要扫描的Dao接口的所在包位置,因此beanDefinitions就是Dao接口。
processBeanDefinitions()方法增加beanDefinition的构造函数的传入值,这个传入值的作用后面再讲。并且beanDefinitions设置beanClass为MapperFactoryBean,也就是说当实例化bean时,这个bean的class就是MapperFactoryBean。

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

	private Class<T> mapperInterface;

	public MapperFactoryBean(Class<T> mapperInterface) {  
	  this.mapperInterface = mapperInterface;  
	}
	
	/**  
	 * {@inheritDoc}  
	 */@Override  
	public T getObject() throws Exception {  
	  return getSqlSession().getMapper(this.mapperInterface);  
	}  
	  
	/**  
	 * {@inheritDoc}  
	 */@Override  
	public Class<T> getObjectType() {  
	  return this.mapperInterface;  
	}  
	  
	/**  
	 * {@inheritDoc}  
	 */@Override  
	public boolean isSingleton() {  
	  return true;  
	}
}

而MapperFactoryBean实现了FactoryBean接口,注意MapperFactoryBean的构造函数,它的参数是mapperInterface,这个构造函数就对应着上文中processBeanDefinitions()方法中的definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
它会将Dao接口的接口名传入。MapperFactoryBean实现了FactoryBean接口,意味着当从beanFactory获取bean时,实际上得到的是MapperFactoryBean的getObject()方法返回的对象。

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {  
	MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);  
	if (mapperProxyFactory == null) {  
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");  
	} else {  
	    try {  
	        return mapperProxyFactory.newInstance(sqlSession);  
	    } catch (Exception var5) {  
            throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);  
        }  
    }  
}

public T newInstance(SqlSession sqlSession) {  
	MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, 		this.mapperInterface, this.methodCache);  
	return this.newInstance(mapperProxy);  
}

getMapper()方法返回的是Dao接口的代理对象。
最后,来看一下MapperScannerConfigurer是如何被调用的,首先查看@MapperScan注解类

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
    String[] basePackages() default {};
}

可以看到MapperScan通过@Import引入MapperScannerRegistrar

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    AnnotationAttributes mapperScanAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
        if (mapperScanAttrs != null) {
            registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
            generateBaseBeanName(importingClassMetadata, 0));
        }
    }

    void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
      BeanDefinitionRegistry registry, String beanName) {

    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);

    builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));

    registry.registerBeanDefinition(beanName, builder.getBeanDefinition());

  }
}

Import引入MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar,并且通过registerBeanDefinitions()可以动态注册bean,mybaits通过该方法获得@MapperScan注解的参数,然后将参数注入MapperScannerConfigurer的beanDefinition中,然后注册该beanDefinition

@Mapper总结

首先,通过我们指定的包扫描路径找到Dao接口,然后通过更改beanDefinitions的构造函数,更改为传入接口名(以此获取到接口名),然后更改beanDefinitions的beanClass为MapperFactoryBean,MapperFactoryBean又实现了FactoryBean。这样一来,当我们通过@Autowired注入bean时,获得的bean是getObject()方法返回的对象,最终getObject()方法返回的是接口的代理对象。

@Reference

首先来看AbstractAnnotationBeanPostProcessor这个类,它继承了InstantiationAwareBeanPostProcessorAdapter,InstantiationAwareBeanPostProcessorAdapter的postProcessPropertyValues()方法将在bean实例化之后,属性注入时调用。

public abstract class AbstractAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor {

	@Override  
	public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {  
	  try {  
		  AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);  
		  prepareInjection(metadata);  
		  metadata.inject(bean, beanName, pvs);  
	  } catch (BeansException ex) {  
	        throw ex;  
	  } catch (Throwable ex) {  
	        throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getSimpleName() + " dependencies is failed", ex);  
	  }  
	    return pvs;  
	}

findInjectionMetadata()方法作用是扫描获得annotationType类型注解的字段和方法,并且保存这些字段和方法上的参数,封装为AnnotatedInjectionMetadata

    protected AnnotatedInjectionMetadata findInjectionMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
        // Fall back to class name as cache key, for backwards compatibility with custom callers.
        String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
        // Quick check on the concurrent map first, with minimal locking.
        AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
        metadata = buildAnnotatedMetadata(clazz);
        return metadata;
    }

    private AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class<?> beanClass) {
        Collection<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> fieldElements = findFieldAnnotationMetadata(beanClass);
        Collection<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> methodElements = findAnnotatedMethodMetadata(beanClass);
        return new AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements);
    }
	
    private List<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> findFieldAnnotationMetadata(final Class<?> beanClass) {

        final List<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> elements = new LinkedList<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement>();

        ReflectionUtils.doWithFields(beanClass, new ReflectionUtils.FieldCallback() {
            @Override
            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {

                for (Class<? extends Annotation> annotationType : getAnnotationTypes()) {

                    AnnotationAttributes attributes = getAnnotationAttributes(field, annotationType, getEnvironment(), true, true);

                    elements.add(new AnnotatedFieldElement(field, attributes));
                    
                }
            }
        });

        return elements;

    }

    private List<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> findAnnotatedMethodMetadata(final Class<?> beanClass) {

        final List<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> elements = new LinkedList<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement>();

        ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() {
            @Override
            public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {

                if (method.getAnnotation(Bean.class) != null) {
                    // DO NOT inject to Java-config class's @Bean method
                    return;
                }

                for (Class<? extends Annotation> annotationType : getAnnotationTypes()) {

                    AnnotationAttributes attributes = getAnnotationAttributes(bridgedMethod, annotationType, getEnvironment(), true, true);

                    if (attributes != null && method.equals(ClassUtils.getMostSpecificMethod(method, beanClass))) {
                        PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, beanClass);
                        elements.add(new AnnotatedMethodElement(method, pd, attributes));
                    }
                }
            }
        });

        return elements;
    }
}

接着来看ReferenceAnnotationBeanPostProcessor,可以发现annotationType就是DubboReference.class, Reference.class, com.alibaba.dubbo.config.annotation.Reference.class这三个,因此被这三个注解标注的字段/方法会被封装成AnnotatedInjectionMetadata

public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBeanPostProcessor implements ApplicationContextAware, BeanFactoryPostProcessor {

    public ReferenceAnnotationBeanPostProcessor() {
        super(DubboReference.class, Reference.class, com.alibaba.dubbo.config.annotation.Reference.class);
    }
}

然后进入prepareInjection()方法

    protected void prepareInjection(AnnotatedInjectionMetadata metadata) throws BeansException {
        try {
            //find and registry bean definition for @DubboReference/@Reference
            for (AnnotatedFieldElement fieldElement : metadata.getFieldElements()) {
                if (fieldElement.injectedObject != null) {
                    continue;
                }
                Class<?> injectedType = fieldElement.field.getType();
                AnnotationAttributes attributes = fieldElement.attributes;
                String referenceBeanName = registerReferenceBean(fieldElement.getPropertyName(), injectedType, attributes, fieldElement.field);

                //associate fieldElement and reference bean
                fieldElement.injectedObject = referenceBeanName;
                injectedFieldReferenceBeanCache.put(fieldElement, referenceBeanName);

            }

            for (AnnotatedMethodElement methodElement : metadata.getMethodElements()) {
                if (methodElement.injectedObject != null) {
                    continue;
                }
                Class<?> injectedType = methodElement.getInjectedType();
                AnnotationAttributes attributes = methodElement.attributes;
                String referenceBeanName = registerReferenceBean(methodElement.getPropertyName(), injectedType, attributes, methodElement.method);

                //associate fieldElement and reference bean
                methodElement.injectedObject = referenceBeanName;
                injectedMethodReferenceBeanCache.put(methodElement, referenceBeanName);
            }
        } catch (ClassNotFoundException e) {
            throw new BeanCreationException("prepare reference annotation failed", e);
        }
    }

	public String registerReferenceBean(String propertyName, Class<?> injectedType, Map<String, Object> attributes, Member member) throws BeansException {
		RootBeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClassName(ReferenceBean.class.getName());
        beanDefinitionRegistry.registerBeanDefinition(referenceBeanName, beanDefinition);
	}

prepareInjection()方法主要就是通过创建beanDefinition注册字段/方法引入的bean,可以发现beanDefinition的beanClassName被设置为ReferenceBean,而ReferenceBean是一个FactoryBean,这和上文中@Mapper的做法类似,也就是说当注入@DubboReference字段时,实际上返回ReferenceBean的getObject()方法,然后由createLazyProxy()方法通过ProxyFactory来创建动态代理,ProxyFactory的目标对像是DubboReferenceLazyInitTargetSource,继承了AbstractLazyCreationTargetSource,并且重写了createObject()方法,这个方法的作用就是当第一次调用时通过这个方法创建对象。最终调用getCallProxy()方法,这个方法最终会创建@DubboReference标记字段的代理对象

public class ReferenceBean<T> implements FactoryBean {

	@Override
    public Object getObject() {
        if (lazyProxy == null) {
            createLazyProxy();
        }
        return lazyProxy;
    }

    @Override
    public Class<?> getObjectType() {
        return getInterfaceClass();
    }

    @Override
    @Parameter(excluded = true)
    public boolean isSingleton() {
        return true;
    }

    private void createLazyProxy() {

        //set proxy interfaces
        //see also: org.apache.dubbo.rpc.proxy.AbstractProxyFactory.getProxy(org.apache.dubbo.rpc.Invoker<T>, boolean)
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTargetSource(new DubboReferenceLazyInitTargetSource());
        proxyFactory.addInterface(interfaceClass);
        Class<?>[] internalInterfaces = AbstractProxyFactory.getInternalInterfaces();
        for (Class<?> anInterface : internalInterfaces) {
            proxyFactory.addInterface(anInterface);
        }
        if (!StringUtils.isEquals(interfaceClass.getName(), interfaceName)) {
            //add service interface
            try {
                Class<?> serviceInterface = ClassUtils.forName(interfaceName, beanClassLoader);
                proxyFactory.addInterface(serviceInterface);
            } catch (ClassNotFoundException e) {
                // generic call maybe without service interface class locally
            }
        }

        this.lazyProxy = proxyFactory.getProxy(this.beanClassLoader);
    }

    private class DubboReferenceLazyInitTargetSource extends AbstractLazyCreationTargetSource {

        @Override
        protected Object createObject() throws Exception {
            return getCallProxy();
        }

        @Override
        public synchronized Class<?> getTargetClass() {
            return getInterfaceClass();
        }
    }

    private Object getCallProxy() throws Exception {
        if (referenceConfig == null) {
            throw new IllegalStateException("ReferenceBean is not ready yet, please make sure to call reference interface method after dubbo is started.");
        }
        //get reference proxy
        return referenceConfig.get();
    }
}

最后调用metadata.inject(bean, beanName, pvs),通过field.set()将刚刚创建好的代理对象赋值给field

        @Override
        protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {

            Object injectedObject = getInjectedObject(attributes, bean, beanName, getInjectedType(), this);

            if (member instanceof Field) {
                Field field = (Field) member;
                ReflectionUtils.makeAccessible(field);
                field.set(bean, injectedObject);
            } else if (member instanceof Method) {
                Method method = (Method) member;
                ReflectionUtils.makeAccessible(method);
                method.invoke(bean, injectedObject);
            }
        }

@Reference总结

首先通过postProcessPropertyValues()在属性注入时找到bean中@DubboReference,@Reference注解的字段,并将字段上的属性封装为AnnotatedInjectionMetadata,然后创建字段的beanDefinition,并将beanDefinition的beanClass设为ReferenceBean,也就是说当注入@DubboReference字段时,实际上返回ReferenceBean的getObject()方法,最终在第一次调用时创建接口的代理对象,然后将其赋值给字段。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值