轉自;https://www.cnblogs.com/sonofelice/p/4980161.html
1.
最近在學習spring和ibatis框架。
以前在天貓實習時做過的一個小項目用到的mybatis,在其使用過程中,不加思索的用了比較原始的一種持久化方式:
在一個包中寫一個DAO的接口,在另一個包里面寫DAO的實現,使用sqlMapClient來從***-sql.xml中讀取相應的sql。
1 public interface IBaseDaoiBatis { 2 Object get(String statementName); 3 } 4 public class BaseDaoiBatis implements IBaseDaoiBatis { 5 public Object get(String statementName) { 6 return getSqlMapClientTemplate().queryForObject(statementName); 7 } 8 } 9 //對應的mybatis配置文件里面的sql: 10 <sqlMap> 11 <typeAlias alias="sonarBean" type="com.**--**.SonarScanDataDisplayBean" /> 12 <select id="getSonarScanData" parameterClass="java.lang.Integer" resultClass="java.lang.String"> 13 <![CDATA[ 14 SELECT name FROM mm_test where id=#id#; 15 ]]> 16 </select> 17 </sqlMap>
?
最近搭建了一個spring+ibatis的項目,發現了一種新的持久化方式:
只寫一個dao的接口,在接口的方法中直接注解上用到的sql語句,覺得蠻巧妙的。借來用一下。注意,接口上方多了一個@Mapper注解。而每個方法上都是@Select() 注解,值為對應的sql。
1 @Mapper 2 public interface TestDao { 3 @Select("select id, name, name_pinyin from mm_test; ") 4 List<MmTest> selectAll(); 5 6 @Insert("insert into mm_test(id, name) values(#{id}, #{name})") 7 public void insertUser(MmTest mmtTestS); 8 }
那么這個@Mapper注解究竟是個什么東西,是怎么起到注解的作用的?ibatis是怎么來識別這種注解的呢?對我這個java小白來說,注解,是spring特有的東西嘛?自學java的時候好像很少接觸注解啊。不過竟然有java.lang.annotation 這個包,這到底是怎么回事?
那我們先來看一下Mapper這個自定義注解的定義:
1 import org.springframework.stereotype.Component; 2 3 import java.lang.annotation.*; 4 @Target({ ElementType.TYPE }) 5 @Retention(RetentionPolicy.RUNTIME) 6 @Documented 7 @Component 8 public @interface Mapper { 9 String value() default ""; 10 }
?
?
關于自定義注解:(查的別人的博客:http://www.cnblogs.com/mandroid/archive/2011/07/18/2109829.html)博客里面寫的非常詳細,并且注解的使用機制很容易理解。
拿上述的@Mapper來說,Retention選擇的是RUNTIME策略,就是運行時注入。那么要在運行時獲得注入的值,必然要用到java的反射機制。通過反射,拿到一個類運行時的方法變量等,來進行一系列的操作。
那我要考慮的下一個問題是,我定義的@Mapper,在我的工程里面是怎么識別的呢?
來看一下我spring的配置文件中關于mybatis的配置
1 <!--mybatis--> 2 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> 3 <property name="dataSource" ref="dataSource" /> 4 <property name="configLocation"> 5 <value>classpath:myBatis/mapper.xml</value> 6 </property> 7 </bean> 8 <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> 9 <property name="basePackage" value="com.**.**.**.dao" /> 10 <property name="annotationClass" value="com.nuomi.crm.annotation.Mapper"/> 11 <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> 12 </bean>
在org.mybatis.spring.mapper.MapperScannerConfigurer這個類里面,應該是會去掃描我自定義的com.nuomi.crm.annotation.Mapper這個類的。
?
1 <configuration> 2 <settings> 3 <!-- 將下劃線字段名稱映射為駝峰變量 --> 4 <setting name="mapUnderscoreToCamelCase" value="true" /> 5 <!-- 進制mybatis進行延遲加載 --> 6 <setting name="lazyLoadingEnabled" value="false"/> 7 </settings> 8 <mappers> 9 </mappers> 10 </configuration>
?
在我的mapper.xml里面只需要進行這一簡單的配置就可以了(配置的含義后續補充)
接下來看一下mybatis自帶的這個MapperScannerConfigurer究竟怎么實現的,來使用我這個自定義的注解@Mapper呢。
1 public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware { 2 private Class<? extends Annotation> annotationClass; 3 public void setAnnotationClass(Class<? extends Annotation> annotationClass) { 4 this.annotationClass = annotationClass; 5 }/** 6 * {@inheritDoc} 7 * 8 * @since 1.0.2 9 */ 10 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { 11 if (this.processPropertyPlaceHolders) { 12 processPropertyPlaceHolders(); 13 } 14 15 ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); 16 scanner.setAddToConfig(this.addToConfig); 17 scanner.setAnnotationClass(this.annotationClass); 18 scanner.setMarkerInterface(this.markerInterface); 19 scanner.setSqlSessionFactory(this.sqlSessionFactory); 20 scanner.setSqlSessionTemplate(this.sqlSessionTemplate); 21 scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName); 22 scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName); 23 scanner.setResourceLoader(this.applicationContext); 24 scanner.setBeanNameGenerator(this.nameGenerator); 25 scanner.registerFilters(); 26 scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS)); 27 } 28 29 /* 30 * BeanDefinitionRegistries are called early in application startup, before 31 * BeanFactoryPostProcessors. This means that PropertyResourceConfigurers will not have been 32 * loaded and any property substitution of this class' properties will fail. To avoid this, find 33 * any PropertyResourceConfigurers defined in the context and run them on this class' bean 34 * definition. Then update the values. 35 */ 36 private void processPropertyPlaceHolders() { 37 Map<String, PropertyResourceConfigurer> prcs = applicationContext.getBeansOfType(PropertyResourceConfigurer.class); 38 39 if (!prcs.isEmpty() && applicationContext instanceof GenericApplicationContext) { 40 BeanDefinition mapperScannerBean = ((GenericApplicationContext) applicationContext) 41 .getBeanFactory().getBeanDefinition(beanName); 42 43 // PropertyResourceConfigurer does not expose any methods to explicitly perform 44 // property placeholder substitution. Instead, create a BeanFactory that just 45 // contains this mapper scanner and post process the factory. 46 DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); 47 factory.registerBeanDefinition(beanName, mapperScannerBean); 48 49 for (PropertyResourceConfigurer prc : prcs.values()) { 50 prc.postProcessBeanFactory(factory); 51 } 52 53 PropertyValues values = mapperScannerBean.getPropertyValues(); 54 55 this.basePackage = updatePropertyValue("basePackage", values); 56 this.sqlSessionFactoryBeanName = updatePropertyValue("sqlSessionFactoryBeanName", values); 57 this.sqlSessionTemplateBeanName = updatePropertyValue("sqlSessionTemplateBeanName", values); 58 } 59 } 60 61 }
上面只是截取的關于annotation的代碼片段.
scanner.setAnnotationClass(this.annotationClass);
這里會去掃描配置的那個注解類。
mybatis的內部實現會使用java反射機制來在運行時去解析相應的sql。
?
(上面寫的還不是很完全,后續補充。)
?