关于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()方法,最终在第一次调用时创建接口的代理对象,然后将其赋值给字段。