Spring整合MyBatis(二)源码分析
来源:互联网 发布:男士须后水推荐知乎 编辑:程序博客网 时间:2024/05/17 09:36
在Spring配置Mybatis的文件中我们可以看到如下代码:-
- <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
- <property name="basePackage" value="org.tarena.note.dao">
- </property>
MapperScannerConfigurer,让它扫描特定的包,自动帮我们成批地创建映射器。这样就大大减少了配置的工作量。
basePackage属性是让你为映射器接口文件设置基本的包路径。可以使用分号或逗号作为分隔符设置多于一个的包路径。每个映射器都会在指定的包路径中递归地被搜索到。被发现的映射器将会使用Spring对自动侦测组件默认的命名策略来命名。也就是说,如果没有发现注解,它就会使用映射器的非大写的非完全限定类名。如果发现了@Component或JSR-330@Named注解,它会获取名称。
通过上面的配置,Spring就会帮助我们对test.mybatis.dao下面所有接口进行自动的注入,而不需要为每个接口重复在Spring配置文件中进行声明了。那么这个功能如何做到的呢?MapperScanner Configurer中又有哪些核心操作呢?同样首先看下这个类实现了InitializingBean接口。马上查找afterPropertiesSet方法来看看类的初始化逻辑。
- public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
但没有任何有意义的实现,我们看到MapperScannerConfigurer还实现了接口BeanDefinitionRegistryPostProcessor接口的方法:- public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
- if (this.processPropertyPlaceHolders) {
- processPropertyPlaceHolders();
- }
-
- ClassPathMapperScanner scanner = new ClassPathMapperScanner(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));
- }
正是这里。大致看下代码实现,正是完成了对指定路径扫描的逻辑。那么,我们就以此为入口,详细地分析MapperScannerConfigurer所提供的逻辑实现下载 1.processPropertyPlaceHolders属性的处理
首先,难题就是processPropertyPlaceHolders属性的处理。或许许多人并未接触过此属性。我们只能查看processPropertyPlaceHolders()函数来反推此属性所代表的功能。
- private void processPropertyPlaceHolders() {
- Map<String, PropertyResourceConfigurer> prcs = applicationContext.getBeansOfType(PropertyResourceConfigurer.class);
-
- if (!prcs.isEmpty() && applicationContext instanceof GenericApplicationContext) {
- BeanDefinition mapperScannerBean = ((GenericApplicationContext) applicationContext)
- .getBeanFactory().getBeanDefinition(beanName);
-
-
-
-
- DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
- factory.registerBeanDefinition(beanName, mapperScannerBean);
-
- for (PropertyResourceConfigurer prc : prcs.values()) {
- prc.postProcessBeanFactory(factory);
- }
-
- PropertyValues values = mapperScannerBean.getPropertyValues();
-
- this.basePackage = updatePropertyValue("basePackage", values);
- this.sqlSessionFactoryBeanName = updatePropertyValue("sqlSessionFactoryBeanName", values);
- this.sqlSessionTemplateBeanName = updatePropertyValue("sqlSessionTemplateBeanName", values);
- }
- }
此函数的说明给我嗯进行了说明:BeanDefinitionRegistries会在应用启动的时候调用,并且会早于BeanFactoryPostProcessors的调用,这就意味着PropertiesResourceConfigurers还没有被加载所有对于属性文件的引用将会失效,为避免此种情况发生,此方法手动地找出定义的PropertyResourceConfigurers并进行调用以以保证对于属性的引用可以正常工作。下面我们举个例子说明:
如果在配置文件中添加如下代码下载 :
- <pre name="code" class="java"><bean class="org.mybatis.Spring.mapper.MapperScannerConfigurer">
- <property name="basePackage" value="${basePackage}"/>
- </bean>
此时你会发现这个配置并没有生效。因为在解析MapperScannerConfigurer这个bean的时候,配置文件还没有被加载,为了解决这个问题,Spring提供了processPropertyPlaceHloder属性,你需要这样配置MapperScannerConfigurer类型的bean:
- <pre name="code" class="java"><bean class="org.mybatis.Spring.mapper.MapperScannerConfigurer">
- <property name="basePackage" value="${basePackage}"/>
- <property name="processPropertyPlaceHolders" value="true">
- </bean>
通过processPropertyPlaceHolders属性的配置,将程序引入我们正在分析的processPropertyPlaceHolders函数中来完成属性文件的加载。至此,我们理清了这个函数的属性,再回顾看下这个函数做的事情:
(1)找到所有已经注册的PropertyResourceConfigurer类型的bean。
(2)模拟Spring的环境来用处理器。这里通过使用呢new DefaultlistableBeanFactory()来模拟Spring中的环境(完成处理器的调用后便失效),将映射的bean,也就是MapperScannerConfigurer类型bean注册到环境中来进行后处理器的调用。
2.根据配置属性生成过滤器下载
在postProcessBeanDefinitionRegistry方法中可以看到,配置中支持很多属性的设定,但是我们感兴趣的或者说影响扫描结果的并不多,属性设置后通过在scanner.registerFilters()代码中生成对应的过滤器来控制扫描结果。
- public void registerFilters() {
- boolean acceptAllInterfaces = true;
-
-
-
-
-
-
-
-
- if (this.annotationClass != null) {
- addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
- acceptAllInterfaces = false;
- }
-
-
-
- if (this.markerInterface != null) {
- addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
- @Override
- protected boolean matchClassName(String className) {
- return false;
- }
- });
- acceptAllInterfaces = false;
- }
-
-
-
-
-
- if (acceptAllInterfaces) {
-
- addIncludeFilter(new TypeFilter() {
- public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
- return true;
- }
- });
- }
-
-
-
- addExcludeFilter(new TypeFilter() {
- public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
- String className = metadataReader.getClassMetadata().getClassName();
- return className.endsWith("package-info");
- }
- });
- }
从上面的函数我们可以看出,控制扫描文件Spring通过不同的过滤器完成,这些定义的过滤器记录在了includeFilters和excludeFilters属性中。
- public void addIncludeFilter(TypeFilter includeFilter){
- this.includeFilters.add(includeFilter);
- }
-
- public void addExcludeFilter(TypeFilter excludeFilter){
- this.excludeFilters.add(0,excludeFilter);
- }
至于过滤器为什么会在扫描过程中起作用,我们在后面分析扫描实现的时候再深入研究。3.扫描java文件
设置了相关属性以及生成了对应的过滤器后就可以进行文件的扫描了,扫描工作是有ClassPathMapperScanner类的父类ClassPathBeanDefinitionScanner的scan方法完成的。
- public int scan(String... basePackages) {
- int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
-
- doScan(basePackages);
-
-
- if (this.includeAnnotationConfig) {
- AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
- }
-
- return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
- }
scan是个全局方法,扫描工作通过委托给了doScan方法,同时,还包括了includeAnnotationConfig属性的处理,AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);代码主要是完成对于注解处理器的简单注册,我们下面主要分析下扫描功能的实现下载 。- 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");
- }
-
-
-
-
- definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
- definition.setBeanClass(MapperFactoryBean.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;
- }
此时,虽然还没有完成介绍到扫描的过程,但是我们也应该理解了Spring中对于自动扫描的注册,声明MapperScannerConfigurer类型的bean目的是不需要我们对于每个接口都注册一个MapperFactoryBean类型的对应的bean,但是,不再配置文件中注册并不代表这个bean不存在,而是在扫描的过程中通过编码的方式动态注册。实现过程我们在上面的函数中可以看得非常清楚下载 。- 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 Set<BeanDefinition> findCandidateComponents(String basePackage) {
- Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
- try {
- String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
- resolveBasePackage(basePackage) + "/" + this.resourcePattern;
- Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
- boolean traceEnabled = logger.isTraceEnabled();
- boolean debugEnabled = logger.isDebugEnabled();
- for (Resource resource : resources) {
- if (traceEnabled) {
- logger.trace("Scanning " + resource);
- }
- if (resource.isReadable()) {
- try {
- MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
- if (isCandidateComponent(metadataReader)) {
- ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
- sbd.setResource(resource);
- sbd.setSource(resource);
- if (isCandidateComponent(sbd)) {
- if (debugEnabled) {
- logger.debug("Identified candidate component class: " + resource);
- }
- candidates.add(sbd);
- }
- else {
- if (debugEnabled) {
- logger.debug("Ignored because not a concrete top-level class: " + resource);
- }
- }
- }
- else {
- if (traceEnabled) {
- logger.trace("Ignored because not matching any filter: " + resource);
- }
- }
- }
- catch (Throwable ex) {
- throw new BeanDefinitionStoreException(
- "Failed to read candidate component class: " + resource, ex);
- }
- }
- else {
- if (traceEnabled) {
- logger.trace("Ignored because not readable: " + resource);
- }
- }
- }
- }
- catch (IOException ex) {
- throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
- }
- return candidates;
- }
findCandidateComponents方法根据传入的包路径信息并结合类文件路径拼接成文件的绝对路径,同时完成了文件的扫描过程并且根据对应的文件生成了对应的bean,使用ScannedGenericBeanDefinition类型的bean承载信息,bean中值记录了resource和source信息。这里,我们更感兴趣的是isCandidateCompanent(metadataReader),此句代码用于判断当前扫描的文件是否符合要求,而我们之前注册的过滤器也是在此派上用场的。- protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
- for (TypeFilter tf : this.excludeFilters) {
- if (tf.match(metadataReader, this.metadataReaderFactory)) {
- return false;
- }
- }
- for (TypeFilter tf : this.includeFilters) {
- if (tf.match(metadataReader, this.metadataReaderFactory)) {
- AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
- if (!metadata.isAnnotated(Profile.class.getName())) {
- return true;
- }
- AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class);
- return this.environment.acceptsProfiles(profile.getStringArray("value"));
- }
- }
- return false;
- }
0 0