Coverage Report - org.mybatis.spring.mapper.ClassPathMapperScanner
 
Classes in this File Line Coverage Branch Coverage Complexity
ClassPathMapperScanner
85%
61/71
72%
26/36
2.059
ClassPathMapperScanner$1
100%
2/2
N/A
2.059
ClassPathMapperScanner$2
100%
2/2
N/A
2.059
ClassPathMapperScanner$3
100%
3/3
N/A
2.059
 
 1  
 /**
 2  
  *    Copyright 2010-2015 the original author or authors.
 3  
  *
 4  
  *    Licensed under the Apache License, Version 2.0 (the "License");
 5  
  *    you may not use this file except in compliance with the License.
 6  
  *    You may obtain a copy of the License at
 7  
  *
 8  
  *       http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  *    Unless required by applicable law or agreed to in writing, software
 11  
  *    distributed under the License is distributed on an "AS IS" BASIS,
 12  
  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  *    See the License for the specific language governing permissions and
 14  
  *    limitations under the License.
 15  
  */
 16  
 package org.mybatis.spring.mapper;
 17  
 
 18  
 import java.io.IOException;
 19  
 import java.lang.annotation.Annotation;
 20  
 import java.util.Arrays;
 21  
 import java.util.Set;
 22  
 
 23  
 import org.apache.ibatis.session.SqlSessionFactory;
 24  
 import org.mybatis.spring.SqlSessionTemplate;
 25  
 import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
 26  
 import org.springframework.beans.factory.config.BeanDefinition;
 27  
 import org.springframework.beans.factory.config.BeanDefinitionHolder;
 28  
 import org.springframework.beans.factory.config.RuntimeBeanReference;
 29  
 import org.springframework.beans.factory.support.AbstractBeanDefinition;
 30  
 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
 31  
 import org.springframework.beans.factory.support.GenericBeanDefinition;
 32  
 import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
 33  
 import org.springframework.core.type.classreading.MetadataReader;
 34  
 import org.springframework.core.type.classreading.MetadataReaderFactory;
 35  
 import org.springframework.core.type.filter.AnnotationTypeFilter;
 36  
 import org.springframework.core.type.filter.AssignableTypeFilter;
 37  
 import org.springframework.core.type.filter.TypeFilter;
 38  
 import org.springframework.util.StringUtils;
 39  
 
 40  
 /**
 41  
  * A {@link ClassPathBeanDefinitionScanner} that registers Mappers by
 42  
  * {@code basePackage}, {@code annotationClass}, or {@code markerInterface}. If
 43  
  * an {@code annotationClass} and/or {@code markerInterface} is specified, only
 44  
  * the specified types will be searched (searching for all interfaces will be
 45  
  * disabled).
 46  
  * <p>
 47  
  * This functionality was previously a private class of
 48  
  * {@link MapperScannerConfigurer}, but was broken out in version 1.2.0.
 49  
  *
 50  
  * @author Hunter Presnall
 51  
  * @author Eduardo Macarron
 52  
  * 
 53  
  * @see MapperFactoryBean
 54  
  * @since 1.2.0
 55  
  * @version $Id$
 56  
  */
 57  
 public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
 58  
 
 59  39
   private boolean addToConfig = true;
 60  
 
 61  
   private SqlSessionFactory sqlSessionFactory;
 62  
 
 63  
   private SqlSessionTemplate sqlSessionTemplate;
 64  
 
 65  
   private String sqlSessionTemplateBeanName;
 66  
 
 67  
   private String sqlSessionFactoryBeanName;
 68  
 
 69  
   private Class<? extends Annotation> annotationClass;
 70  
 
 71  
   private Class<?> markerInterface;
 72  
 
 73  39
   private MapperFactoryBean mapperFactoryBean = new MapperFactoryBean();
 74  
 
 75  
   public ClassPathMapperScanner(BeanDefinitionRegistry registry) {
 76  39
     super(registry, false);
 77  39
   }
 78  
 
 79  
   public void setAddToConfig(boolean addToConfig) {
 80  13
     this.addToConfig = addToConfig;
 81  13
   }
 82  
 
 83  
   public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
 84  19
     this.annotationClass = annotationClass;
 85  19
   }
 86  
 
 87  
   public void setMarkerInterface(Class<?> markerInterface) {
 88  19
     this.markerInterface = markerInterface;
 89  19
   }
 90  
 
 91  
   public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
 92  13
     this.sqlSessionFactory = sqlSessionFactory;
 93  13
   }
 94  
 
 95  
   public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
 96  13
     this.sqlSessionTemplate = sqlSessionTemplate;
 97  13
   }
 98  
 
 99  
   public void setSqlSessionTemplateBeanName(String sqlSessionTemplateBeanName) {
 100  39
     this.sqlSessionTemplateBeanName = sqlSessionTemplateBeanName;
 101  39
   }
 102  
 
 103  
   public void setSqlSessionFactoryBeanName(String sqlSessionFactoryBeanName) {
 104  39
     this.sqlSessionFactoryBeanName = sqlSessionFactoryBeanName;
 105  39
   }
 106  
 
 107  
   public void setMapperFactoryBean(MapperFactoryBean mapperFactoryBean) {
 108  1
     this.mapperFactoryBean = (mapperFactoryBean != null ? mapperFactoryBean : new MapperFactoryBean());
 109  1
   }
 110  
 
 111  
 
 112  
   /**
 113  
    * Configures parent scanner to search for the right interfaces. It can search
 114  
    * for all interfaces or just for those that extends a markerInterface or/and
 115  
    * those annotated with the annotationClass
 116  
    */
 117  
   public void registerFilters() {
 118  39
     boolean acceptAllInterfaces = true;
 119  
 
 120  
     // if specified, use the given annotation and / or marker interface
 121  39
     if (this.annotationClass != null) {
 122  8
       addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
 123  8
       acceptAllInterfaces = false;
 124  
     }
 125  
 
 126  
     // override AssignableTypeFilter to ignore matches on the actual marker interface
 127  39
     if (this.markerInterface != null) {
 128  10
       addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
 129  
         @Override
 130  
         protected boolean matchClassName(String className) {
 131  312
           return false;
 132  
         }
 133  
       });
 134  10
       acceptAllInterfaces = false;
 135  
     }
 136  
 
 137  39
     if (acceptAllInterfaces) {
 138  
       // default include filter that accepts all classes
 139  25
       addIncludeFilter(new TypeFilter() {
 140  
         @Override
 141  
         public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
 142  358
           return true;
 143  
         }
 144  
       });
 145  
     }
 146  
 
 147  
     // exclude package-info.java
 148  39
     addExcludeFilter(new TypeFilter() {
 149  
       @Override
 150  
       public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
 151  560
         String className = metadataReader.getClassMetadata().getClassName();
 152  560
         return className.endsWith("package-info");
 153  
       }
 154  
     });
 155  39
   }
 156  
 
 157  
   /**
 158  
    * Calls the parent search that will search and register all the candidates.
 159  
    * Then the registered objects are post processed to set them as
 160  
    * MapperFactoryBeans
 161  
    */
 162  
   @Override
 163  
   public Set<BeanDefinitionHolder> doScan(String... basePackages) {
 164  39
     Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
 165  
 
 166  39
     if (beanDefinitions.isEmpty()) {
 167  0
       logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
 168  
     } else {
 169  39
       processBeanDefinitions(beanDefinitions);
 170  
     }
 171  
 
 172  39
     return beanDefinitions;
 173  
   }
 174  
 
 175  
   private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
 176  
     GenericBeanDefinition definition;
 177  39
     for (BeanDefinitionHolder holder : beanDefinitions) {
 178  149
       definition = (GenericBeanDefinition) holder.getBeanDefinition();
 179  
 
 180  149
       if (logger.isDebugEnabled()) {
 181  0
         logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() 
 182  
           + "' and '" + definition.getBeanClassName() + "' mapperInterface");
 183  
       }
 184  
 
 185  
       // the mapper interface is the original class of the bean
 186  
       // but, the actual class of the bean is MapperFactoryBean
 187  149
       definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
 188  149
       definition.setBeanClass(this.mapperFactoryBean.getClass());
 189  
 
 190  149
       definition.getPropertyValues().add("addToConfig", this.addToConfig);
 191  
 
 192  149
       boolean explicitFactoryUsed = false;
 193  149
       if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
 194  37
         definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
 195  37
         explicitFactoryUsed = true;
 196  112
       } else if (this.sqlSessionFactory != null) {
 197  0
         definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
 198  0
         explicitFactoryUsed = true;
 199  
       }
 200  
 
 201  149
       if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
 202  10
         if (explicitFactoryUsed) {
 203  0
           logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
 204  
         }
 205  10
         definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
 206  10
         explicitFactoryUsed = true;
 207  139
       } else if (this.sqlSessionTemplate != null) {
 208  0
         if (explicitFactoryUsed) {
 209  0
           logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
 210  
         }
 211  0
         definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
 212  0
         explicitFactoryUsed = true;
 213  
       }
 214  
 
 215  149
       if (!explicitFactoryUsed) {
 216  102
         if (logger.isDebugEnabled()) {
 217  0
           logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
 218  
         }
 219  102
         definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
 220  
       }
 221  149
     }
 222  39
   }
 223  
 
 224  
   /**
 225  
    * {@inheritDoc}
 226  
    */
 227  
   @Override
 228  
   protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
 229  404
     return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
 230  
   }
 231  
 
 232  
   /**
 233  
    * {@inheritDoc}
 234  
    */
 235  
   @Override
 236  
   protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) {
 237  151
     if (super.checkCandidate(beanName, beanDefinition)) {
 238  149
       return true;
 239  
     } else {
 240  2
       logger.warn("Skipping MapperFactoryBean with name '" + beanName 
 241  
           + "' and '" + beanDefinition.getBeanClassName() + "' mapperInterface"
 242  
           + ". Bean already defined with the same name!");
 243  2
       return false;
 244  
     }
 245  
   }
 246  
 
 247  
 }