MapperScannerBeanDefinitionParser.java

  1. /*
  2.  * Copyright 2010-2024 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.  *    https://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.config;

  17. import java.lang.annotation.Annotation;
  18. import java.util.ArrayList;
  19. import java.util.HashMap;
  20. import java.util.List;
  21. import java.util.Map;

  22. import org.mybatis.spring.mapper.ClassPathMapperScanner;
  23. import org.mybatis.spring.mapper.MapperFactoryBean;
  24. import org.mybatis.spring.mapper.MapperScannerConfigurer;
  25. import org.springframework.beans.BeanUtils;
  26. import org.springframework.beans.factory.config.BeanDefinition;
  27. import org.springframework.beans.factory.support.AbstractBeanDefinition;
  28. import org.springframework.beans.factory.support.BeanDefinitionBuilder;
  29. import org.springframework.beans.factory.support.BeanNameGenerator;
  30. import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
  31. import org.springframework.beans.factory.xml.ParserContext;
  32. import org.springframework.util.ClassUtils;
  33. import org.springframework.util.StringUtils;
  34. import org.w3c.dom.Element;
  35. import org.w3c.dom.Node;

  36. /**
  37.  * A {#code BeanDefinitionParser} that handles the element scan of the MyBatis. namespace
  38.  *
  39.  * @author Lishu Luo
  40.  * @author Eduardo Macarron
  41.  *
  42.  * @since 1.2.0
  43.  *
  44.  * @see MapperFactoryBean
  45.  * @see ClassPathMapperScanner
  46.  * @see MapperScannerConfigurer
  47.  */
  48. public class MapperScannerBeanDefinitionParser extends AbstractBeanDefinitionParser {

  49.   private static final String ATTRIBUTE_BASE_PACKAGE = "base-package";
  50.   private static final String ATTRIBUTE_ANNOTATION = "annotation";
  51.   private static final String ATTRIBUTE_MARKER_INTERFACE = "marker-interface";
  52.   private static final String ATTRIBUTE_NAME_GENERATOR = "name-generator";
  53.   private static final String ATTRIBUTE_TEMPLATE_REF = "template-ref";
  54.   private static final String ATTRIBUTE_FACTORY_REF = "factory-ref";
  55.   private static final String ATTRIBUTE_MAPPER_FACTORY_BEAN_CLASS = "mapper-factory-bean-class";
  56.   private static final String ATTRIBUTE_LAZY_INITIALIZATION = "lazy-initialization";
  57.   private static final String ATTRIBUTE_DEFAULT_SCOPE = "default-scope";
  58.   private static final String ATTRIBUTE_PROCESS_PROPERTY_PLACEHOLDERS = "process-property-placeholders";
  59.   private static final String ATTRIBUTE_EXCLUDE_FILTER = "exclude-filter";

  60.   @Override
  61.   protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
  62.     var builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);

  63.     var classLoader = ClassUtils.getDefaultClassLoader();

  64.     var processPropertyPlaceHolders = element.getAttribute(ATTRIBUTE_PROCESS_PROPERTY_PLACEHOLDERS);
  65.     builder.addPropertyValue("processPropertyPlaceHolders",
  66.         !StringUtils.hasText(processPropertyPlaceHolders) || Boolean.parseBoolean(processPropertyPlaceHolders));
  67.     try {
  68.       var annotationClassName = element.getAttribute(ATTRIBUTE_ANNOTATION);
  69.       if (StringUtils.hasText(annotationClassName)) {
  70.         @SuppressWarnings("unchecked")
  71.         Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) classLoader
  72.             .loadClass(annotationClassName);
  73.         builder.addPropertyValue("annotationClass", annotationClass);
  74.       }
  75.       var markerInterfaceClassName = element.getAttribute(ATTRIBUTE_MARKER_INTERFACE);
  76.       if (StringUtils.hasText(markerInterfaceClassName)) {
  77.         Class<?> markerInterface = classLoader.loadClass(markerInterfaceClassName);
  78.         builder.addPropertyValue("markerInterface", markerInterface);
  79.       }
  80.       var nameGeneratorClassName = element.getAttribute(ATTRIBUTE_NAME_GENERATOR);
  81.       if (StringUtils.hasText(nameGeneratorClassName)) {
  82.         Class<?> nameGeneratorClass = classLoader.loadClass(nameGeneratorClassName);
  83.         var nameGenerator = BeanUtils.instantiateClass(nameGeneratorClass, BeanNameGenerator.class);
  84.         builder.addPropertyValue("nameGenerator", nameGenerator);
  85.       }
  86.       var mapperFactoryBeanClassName = element.getAttribute(ATTRIBUTE_MAPPER_FACTORY_BEAN_CLASS);
  87.       if (StringUtils.hasText(mapperFactoryBeanClassName)) {
  88.         @SuppressWarnings("unchecked")
  89.         Class<? extends MapperFactoryBean> mapperFactoryBeanClass = (Class<? extends MapperFactoryBean>) classLoader
  90.             .loadClass(mapperFactoryBeanClassName);
  91.         builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
  92.       }

  93.       // parse raw exclude-filter in <mybatis:scan>
  94.       var rawExcludeFilters = parseScanTypeFilters(element, parserContext);
  95.       if (!rawExcludeFilters.isEmpty()) {
  96.         builder.addPropertyValue("rawExcludeFilters", rawExcludeFilters);
  97.       }

  98.     } catch (Exception ex) {
  99.       var readerContext = parserContext.getReaderContext();
  100.       readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
  101.     }

  102.     builder.addPropertyValue("sqlSessionTemplateBeanName", element.getAttribute(ATTRIBUTE_TEMPLATE_REF));
  103.     builder.addPropertyValue("sqlSessionFactoryBeanName", element.getAttribute(ATTRIBUTE_FACTORY_REF));
  104.     builder.addPropertyValue("lazyInitialization", element.getAttribute(ATTRIBUTE_LAZY_INITIALIZATION));
  105.     builder.addPropertyValue("defaultScope", element.getAttribute(ATTRIBUTE_DEFAULT_SCOPE));
  106.     builder.addPropertyValue("basePackage", element.getAttribute(ATTRIBUTE_BASE_PACKAGE));

  107.     // for spring-native
  108.     builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);

  109.     return builder.getBeanDefinition();
  110.   }

  111.   private List<Map<String, String>> parseScanTypeFilters(Element element, ParserContext parserContext) {
  112.     List<Map<String, String>> typeFilters = new ArrayList<>();
  113.     var nodeList = element.getChildNodes();
  114.     for (var i = 0; i < nodeList.getLength(); i++) {
  115.       var node = nodeList.item(i);
  116.       if (Node.ELEMENT_NODE == node.getNodeType()) {
  117.         var localName = parserContext.getDelegate().getLocalName(node);
  118.         if (ATTRIBUTE_EXCLUDE_FILTER.equals(localName)) {
  119.           Map<String, String> filter = new HashMap<>(16);
  120.           filter.put("type", ((Element) node).getAttribute("type"));
  121.           filter.put("expression", ((Element) node).getAttribute("expression"));
  122.           typeFilters.add(filter);
  123.         }
  124.       }
  125.     }
  126.     return typeFilters;
  127.   }

  128.   @Override
  129.   protected boolean shouldGenerateIdAsFallback() {
  130.     return true;
  131.   }

  132. }