你真正了解@Resource和@Autowired这两个注解吗

你真正了解@Resource和@Autowired这两个注解吗

我们一起来探讨这两个注解以及使用不当引发的案例

大部分人都知道@Resource是通过“beanName”注入,@Autowired是通过类型注入,但是真的只有这么简单吗?

先抛问题

项目中采用spring+mybatis框架,同时引入了zebra(https://github.com/Meituan-Dianping/Zebra),mybatis采用基于2.0方式(自定义DaoImpl,并且命名空间和方法自定义)。
如下面写法:

public interface DemoDao {
	List<Demo> selectByIdList(List<Long> list) 
}

public abstract class BaseDaoImpl {

    @Autowired
    private SqlSessionTemplate sqlSessionTemplate;

    SqlSession getSqlSession() {
        return sqlSessionTemplate;
    }
}

@Repository
@Primary
public class DemoDaoImpl extends BaseDaoImpl implements DemoDao {

    @Override
    public List<Demo> selectByIdList(List<Long> list) {
        return getSqlSession().selectList("demotest.Demo.selectByIdList", list);
    }
}

@Service
public class DemoServiceImpl implements DemoService {

    @Resource
    private DemoDao demoDao;

    @Override
    public List<Demo> selectByIdList(List<Long> list) {
        return getSqlSession().selectList("demotest.Demo.selectByIdList", list);
    }
}

mapper文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="demotest.Demo">
	<select id="selectByIdList" >
      ...
   </select>
</mapper>

这样的项目构建,在启动时是没有任何问题的,但是在运行时就会报异常:

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.test.dao.DemoDao.selectByIdList

问题描述很清晰,异常提示找不到mapper文件的映射,但是项目里是有的,我们这时候把@Resource换成@Autowired试一下,程序正常运行,这是为啥呢??????

首先整理一下问题:
1、我们自定义的命名空间为:demotest.Demo,项目在运行时变为:com.test.dao.DemoDao
2、为什么使用@Resource就会报异常,而@Autowired却不会呢。

我们一起看下启动时mybatis\zebra都干了哪些事

我们在使用mybatis和spring集成的时候,一般都会把接口的包路径(com.demo.dao)配置在mybatis的配置下面,而这时候发生了很多事,我们来深入了解一下:

1、zebra.jar下面的ZebraMapperScannerConfigurer类将通过扫描“com.demo.dao”包下面所有的接口,对一些sql和其它配置做一些封装。

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		if (this.processPropertyPlaceHolders) {
			processPropertyPlaceHolders();
		}
		String[] beanDefinitionNames = registry.getBeanDefinitionNames();
		for (String beanDefinitionName : beanDefinitionNames) {
			BeanDefinition beanDefinition = registry.getBeanDefinition(beanDefinitionName);
			String beanClassName = beanDefinition.getBeanClassName();
			if(SqlSessionFactoryBean.class.getName().equals(beanClassName)){
				beanDefinition.setBeanClassName(FixedSqlSessionFactoryBean.class.getName());
			}
		}

		ZebraClassPathMapperScanner scanner = new ZebraClassPathMapperScanner(registry);
		scanner.setAddToConfig(this.addToConfig);
		scanner.setAnnotationClass(this.annotationClass);
		scanner.setMarkerInterface(this.markerInterface);
		scanner.setSqlSessionFactory(this.sqlSessionFactory);
		scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
		scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
		scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
		scanner.setResourceLoader(this.applicationContext);
		scanner.setBeanNameGenerator(this.nameGenerator);
		scanner.registerFilters();
		scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage,
				ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
	}

2、执行ZebraClassPathMapperScanner.doScan(String… basePackages)生成BeanDefinition并装载到spring的ioc容器里,并且调用“ZebraMapperFactoryBean”为每一个BeanDefinition生成beanClass。

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 {
			for (BeanDefinitionHolder holder : beanDefinitions) {
				GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();

				if (logger.isDebugEnabled()) {
					logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '"
					      + definition.getBeanClassName() + "' mapperInterface");
				}

				// the mapper interface is the original class of the bean
				// but, the actual class of the bean is MapperFactoryBean
				definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
				definition.setBeanClass(ZebraMapperFactoryBean.class);

				definition.getPropertyValues().add("addToConfig", this.addToConfig);

				boolean explicitFactoryUsed = false;
				if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
					definition.getPropertyValues().add("sqlSessionFactory",
					      new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
					explicitFactoryUsed = true;
				} else if (this.sqlSessionFactory != null) {
					definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
					explicitFactoryUsed = true;
				}

				if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
					if (explicitFactoryUsed) {
						logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
					}
					definition.getPropertyValues().add("sqlSessionTemplate",
					      new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
					explicitFactoryUsed = true;
				} else if (this.sqlSessionTemplate != null) {
					if (explicitFactoryUsed) {
						logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
					}
					definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
					explicitFactoryUsed = true;
				}

				if (!explicitFactoryUsed) {
					if (logger.isDebugEnabled()) {
						logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName()
						      + "'.");
					}
					definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
				}
			}
		}

		return beanDefinitions;
	}

3、ZebraClassPathMapperScanner.doScan方法调用了父类(ClassPathBeanDefinitionScanner)的doScan方法生成BeanDefinition,在生成BeanDefinition的时候调用AnnotationBeanNameGenerator.buildDefaultBeanName(BeanDefinition definition)生成beanName。

public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
		for (String basePackage : basePackages) {
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}
}

public class AnnotationBeanNameGenerator implements BeanNameGenerator {
	protected String buildDefaultBeanName(BeanDefinition definition) {
		String shortClassName = ClassUtils.getShortName(definition.getBeanClassName());
		return Introspector.decapitalize(shortClassName);
	}
}

public class Introspector {
	public static String decapitalize(String name) {
        if (name == null || name.length() == 0) {
            return name;
        }
        if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
                        Character.isUpperCase(name.charAt(0))){
            return name;
        }
        char chars[] = name.toCharArray();
        chars[0] = Character.toLowerCase(chars[0]);
        return new String(chars);
    }
}

这个时候可能有些细心的朋友就发现问题了,我们的dao层接口,在启动时生成了2个实现类装载到了spring的IOC容器中,一个为“DemoDaoImpl” ,另一个为mybatis生成了代理类,这两个实现的beanName分别为“demoDaoImpl”和“demoDao”,这时候是不是突然发现问题了??
1、使用@Resource注解通过“demoDao”找到了mybatis生成的代理类,这个代理类的sql命名空间为接口名和包路径。
2、使用@Autowired注解通过类型找,这是有两个实现类,我们自己的实现类中有@Primary这个优先级注解,因此没有问题。
3、这里spring还有一个小坑,如果接口名是”IDemoDao“,也就是前两个字母都是大写,那么beanName依然是”IDemoDao“。

我们来仔细刨析一下这两个注解的区别

@Resource

先举例:

@Resource(name="demoDao1")
private DemoDao demoDao

有些同学可能有疑惑,这样就找不到,我们详细说下@Resource的详细步骤
1、先通过name="demoDao1"去ioc容器找对应的bean,如果找不到,开始执行第二步。
2、以“DemoDao demoDao”的变量名作为beanName去ioc容器里寻找,如果这是还找不到,开始执行第三步
3、寻找DemoDao接口的实现类。

@Autowired

@Autowired
private DemoDao demoDao

详细步骤如下:
1、寻找DemoDao接口的实现类,这时如果有多个实现类,开始执行第二步。
2、查看实现类是有“@Primary”注解,或者在调用类中使用“@Qualifier("")”执行具体的实现类,如果都没有使用,开始执行第三步。
3、以“DemoDao demoDao”的变量名作为beanName,再次从ioc容器中查找,如果找不到抛出异常。

这个时候再回头看,是不是发现了端倪,给大家留一个思考题,我不用Zebra,就用spring和mybatis是否会出现这种情况?

备注:依然会出现这种情况,ZebraMapperScannerConfigurer这个类的实现,完全是copy的mybatis-spring.jar中实现。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值