Spring单测中的@Autowired和@Qualifier
@Autowired
注释进行自动注入时,Spring 容器中匹配的候选 Bean 数目必须有且仅有一个。当找不到一个匹配的 Bean 时,Spring 容器将抛出BeanCreationException
@Autowired(required = false)
这等于告诉 Spring:在找不到匹配 Bean 时也不报错。
和找不到一个类型匹配 Bean 相反的一个错误是:如果 Spring 容器中拥有多个候选 Bean,Spring 容器在启动时也会抛出BeanCreationException
此时该如何处理呢?答案是是使用@Qualifier。
@Autowired与@Qualifier配合使用时将会以byName方式进行依赖注入。以byName方式进行依赖注入正是为了避免相同类型的不同POJOs在注入时发生冲突。@Qualifier作用于类的成员变量、类的setter方法中的参数或类的构造函数中的参数。
@Autowired
public void setOffice(@Qualifier("office")Office office) {
this.office = office;
}
下面给出一个常见的Spring测试用例做参考:
import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import javax.sql.DataSource; import junit.framework.Assert; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests; /** * * “搜客”和“投放网络”列表筛选后不能删除问题回归测试用例 <p> * * Hibernate3.3.2GA在使用hql删除时,如果where id in(XXX)里面的XXX太多了,会导致StackOverFlowError。使用hibernate3-fix.jar后该问题修复。<br> * 详参考https://hibernate.onjira.com/browse/HHH-2166?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=35008#action_35008。<p> * * 复现场景:<br> * 1. 使用Hibernate3.3.2GA版本hibernate3.jar部署web程序。<br> * 2. 登陆http://beidoulocal.baidu.com:8080/shifenLogin.action。<br> * 3. 推广组 -> 搜客,新建1万个搜索关键词。<br> * 4. 停止web应用程序,执行测试用例,应抛出如下错误<br> * <code> * java.lang.StackOverflowError * at org.hibernate.hql.ast.util.NodeTraverser.visitDepthFirst(NodeTraverser.java:64) * at org.hibernate.hql.ast.util.NodeTraverser.visitDepthFirst(NodeTraverser.java:66) * </code> * <p> * * 5. 使用使用Hibernate3.3.2GA-fixByZhangxu版本hibernate3-fix.jar部署web应用程序<br> * 6. 登陆http://beidoulocal.baidu.com:8080/shifenLogin.action。<br> * 7. 推广组 -> 搜客,新建1万个搜索关键词。<br> * 8. 停止web应用程序,执行测试用例,执行通过,可以观察打印出所有QT关键词信息。<br> * * @author zhangxu * @since 20110826 * */ @ContextConfiguration(locations = { "/applicationContext.xml" }) public class CproQTKeywordMgrTest extends AbstractTransactionalJUnit4SpringContextTests { //////////////////////////////////////////////////////////////// // 修改userid和groupid进行测试 private final int userid = 480787; private final int groupid = 905276; //////////////////////////////////////////////////////////////// private static final Log log = LogFactory.getLog(CproQTKeywordMgrTest.class); @Autowired private CproQTKeywordMgr cproQTKeywordMgr; private JdbcTemplate jdbcTemplate; @Autowired public void setDataSource(@Qualifier("dataSource") DataSource dataSource){ super.setDataSource(dataSource); } @Autowired public void setJdbcTemplate(@Qualifier("dynamicJdbcTemplate") JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } private PartitionStrategy strategy = null; private final int mod = 64; private String range = "2000000"; private final String tablename = "cproqtkeyword"; private final String poname = "com.baidu.beidou.cprogroup.bo.CproQtKeyword"; /** * 构造一个分页策略 <bean id="keywordPartitionStrategy" class="com.baidu.beidou.util.partition.strategy.impl.HashPartitionStrategy"> <constructor-arg index="0" value="cproqtkeyword" /> <constructor-arg index="1" value="com.baidu.beidou.cprogroup.bo.CproQtKeyword" /> <constructor-arg index="2" value="64"></constructor-arg> <constructor-arg index="3" value="2000000"></constructor-arg> </bean> */ public void setUp(){ try { log.info("初始化分页策略"); strategy = new HashPartitionStrategy(tablename, poname, mod, range); }catch(Exception e){ log.error(e.getMessage(),e); } } @Test public void testSelectHugeNumberQTKeywords(){ setUp(); log.info("Begin======================="); //1. 用sql查询所有keywordIds log.info("Start to get all keywordIds from userid=" + userid + ",groupid=" + groupid); log.info("(The method used here is not hql but pure jdbc sql)"); MockCproQTKeywordDaoImpl mockCproQTKeywordDaoImpl = new MockCproQTKeywordDaoImpl(); List<Integer> keywordIds = mockCproQTKeywordDaoImpl.findKeywordIdsByUserid(userid, groupid); log.info("Get " + keywordIds.size() + " keywordIds from userid=" + userid + ",groupid=" + groupid + ", there ids are " + keywordIds); log.info("Start to get all keywordIds from userid=" + userid + ",groupid=" + groupid); log.info("(The method used here is cproQTKeywordMgr.findByIds(keywordIds, userid) and it used hql)"); //2. 用service层提供到方法查询所有CproQTKeyword;如果用旧到hibernate,此处应该会抛出StackOverFlowError。 List<CproQTKeyword> result = cproQTKeywordMgr.findByIds(keywordIds, userid); Assert.assertNotNull(result); log.info("Get " + result.size() + " CproQTKeyword object back"); log.info("Print all of returned CproQTKeyword"); for(CproQTKeyword cproQTKeyword: result){ logger.info(cproQTKeyword.getKeywordid() + "|" + cproQTKeyword.getKeyword() + "|" + cproQTKeyword.getWordid() + "|" + cproQTKeyword.getPlanid() + "|" + cproQTKeyword.getGroupid() + "|" + cproQTKeyword.getUserid() + "|" + cproQTKeyword.getAdduser() + "|" + cproQTKeyword.getAddtime()); } log.info("End======================="); } /** * * 自行构造到使用sql查询到DAO * * @author zhangxu * */ public class MockCproQTKeywordDaoImpl extends GenericDaoImpl<Object, Integer>{ /** * 不用hql查询,用JDBC写SQL */ public List<Integer> findKeywordIdsByUserid(Integer userid, Integer groupid) { PartID part = strategy.getPartitions(new PartKeyBDidImpl(userid)); String sql = "select keywordid from " + part.getTablename() + " where userid = " + userid + " and groupid = " + groupid; log.info("sql : " + sql); // 不调用父类到findBySql方法 return findBySql(new GenericRowMapping<Integer>() { public Integer mapRow(ResultSet rs, int rowNum) throws SQLException { return rs.getInt(1); } }, sql, new Object[] {}, new int[] {}); } /** * 用了测试用例手工注入到jdbcTemplate */ protected <E> List<E> findBySql(GenericRowMapping<E> mappper, String sql, Object[] parameters, int[] argTypes) { if(sql == null){ return null; } return jdbcTemplate.query(sql, parameters, argTypes, mappper); } } } |