View Javadoc
1   /*
2    * Copyright 2010-2023 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.annotation;
17  
18  import java.lang.annotation.Annotation;
19  import java.util.ArrayList;
20  import java.util.Arrays;
21  import java.util.List;
22  import java.util.stream.Collectors;
23  
24  import org.mybatis.spring.mapper.ClassPathMapperScanner;
25  import org.mybatis.spring.mapper.MapperFactoryBean;
26  import org.mybatis.spring.mapper.MapperScannerConfigurer;
27  import org.springframework.beans.BeanUtils;
28  import org.springframework.beans.factory.config.BeanDefinition;
29  import org.springframework.beans.factory.support.AbstractBeanDefinition;
30  import org.springframework.beans.factory.support.BeanDefinitionBuilder;
31  import org.springframework.beans.factory.support.BeanDefinitionRegistry;
32  import org.springframework.beans.factory.support.BeanNameGenerator;
33  import org.springframework.context.ResourceLoaderAware;
34  import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
35  import org.springframework.core.annotation.AnnotationAttributes;
36  import org.springframework.core.io.ResourceLoader;
37  import org.springframework.core.type.AnnotationMetadata;
38  import org.springframework.util.ClassUtils;
39  import org.springframework.util.StringUtils;
40  
41  /**
42   * A {@link ImportBeanDefinitionRegistrar} to allow annotation configuration of MyBatis mapper scanning. Using
43   * an @Enable annotation allows beans to be registered via @Component configuration, whereas implementing
44   * {@code BeanDefinitionRegistryPostProcessor} will work for XML configuration.
45   *
46   * @author Michael Lanyon
47   * @author Eduardo Macarron
48   * @author Putthiphong Boonphong
49   *
50   * @see MapperFactoryBean
51   * @see ClassPathMapperScanner
52   *
53   * @since 1.2.0
54   */
55  public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
56  
57    /**
58     * {@inheritDoc}
59     *
60     * @deprecated Since 2.0.2, this method not used never.
61     */
62    @Override
63    @Deprecated
64    public void setResourceLoader(ResourceLoader resourceLoader) {
65      // NOP
66    }
67  
68    /**
69     * {@inheritDoc}
70     */
71    @Override
72    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
73      AnnotationAttributes mapperScanAttrs = AnnotationAttributes
74          .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
75      if (mapperScanAttrs != null) {
76        registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
77            generateBaseBeanName(importingClassMetadata, 0));
78      }
79    }
80  
81    void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
82        BeanDefinitionRegistry registry, String beanName) {
83  
84      BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
85      builder.addPropertyValue("processPropertyPlaceHolders", annoAttrs.getBoolean("processPropertyPlaceHolders"));
86  
87      Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
88      if (!Annotation.class.equals(annotationClass)) {
89        builder.addPropertyValue("annotationClass", annotationClass);
90      }
91  
92      Class<?> markerInterface = annoAttrs.getClass("markerInterface");
93      if (!Class.class.equals(markerInterface)) {
94        builder.addPropertyValue("markerInterface", markerInterface);
95      }
96  
97      Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
98      if (!BeanNameGenerator.class.equals(generatorClass)) {
99        builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));
100     }
101 
102     Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
103     if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
104       builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
105     }
106 
107     String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
108     if (StringUtils.hasText(sqlSessionTemplateRef)) {
109       builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
110     }
111 
112     String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");
113     if (StringUtils.hasText(sqlSessionFactoryRef)) {
114       builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
115     }
116 
117     List<String> basePackages = new ArrayList<>();
118 
119     basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
120         .collect(Collectors.toList()));
121 
122     basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName)
123         .collect(Collectors.toList()));
124 
125     if (basePackages.isEmpty()) {
126       basePackages.add(getDefaultBasePackage(annoMeta));
127     }
128 
129     String lazyInitialization = annoAttrs.getString("lazyInitialization");
130     if (StringUtils.hasText(lazyInitialization)) {
131       builder.addPropertyValue("lazyInitialization", lazyInitialization);
132     }
133 
134     String defaultScope = annoAttrs.getString("defaultScope");
135     if (!AbstractBeanDefinition.SCOPE_DEFAULT.equals(defaultScope)) {
136       builder.addPropertyValue("defaultScope", defaultScope);
137     }
138 
139     builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
140 
141     // for spring-native
142     builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
143 
144     registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
145 
146   }
147 
148   private static String generateBaseBeanName(AnnotationMetadata importingClassMetadata, int index) {
149     return importingClassMetadata.getClassName() + "#" + MapperScannerRegistrar.class.getSimpleName() + "#" + index;
150   }
151 
152   private static String getDefaultBasePackage(AnnotationMetadata importingClassMetadata) {
153     return ClassUtils.getPackageName(importingClassMetadata.getClassName());
154   }
155 
156   /**
157    * A {@link MapperScannerRegistrar} for {@link MapperScans}.
158    *
159    * @since 2.0.0
160    */
161   static class RepeatingRegistrar extends MapperScannerRegistrar {
162     /**
163      * {@inheritDoc}
164      */
165     @Override
166     public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
167       AnnotationAttributes mapperScansAttrs = AnnotationAttributes
168           .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScans.class.getName()));
169       if (mapperScansAttrs != null) {
170         AnnotationAttributes[] annotations = mapperScansAttrs.getAnnotationArray("value");
171         for (int i = 0; i < annotations.length; i++) {
172           registerBeanDefinitions(importingClassMetadata, annotations[i], registry,
173               generateBaseBeanName(importingClassMetadata, i));
174         }
175       }
176     }
177   }
178 
179 }