1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.mybatis.spring.boot.autoconfigure;
17
18 import java.beans.PropertyDescriptor;
19 import java.util.List;
20 import java.util.Set;
21 import java.util.stream.Collectors;
22 import java.util.stream.Stream;
23
24 import javax.sql.DataSource;
25
26 import org.apache.ibatis.annotations.Mapper;
27 import org.apache.ibatis.mapping.DatabaseIdProvider;
28 import org.apache.ibatis.plugin.Interceptor;
29 import org.apache.ibatis.scripting.LanguageDriver;
30 import org.apache.ibatis.session.Configuration;
31 import org.apache.ibatis.session.ExecutorType;
32 import org.apache.ibatis.session.SqlSessionFactory;
33 import org.apache.ibatis.type.TypeHandler;
34 import org.mybatis.spring.SqlSessionFactoryBean;
35 import org.mybatis.spring.SqlSessionTemplate;
36 import org.mybatis.spring.mapper.MapperFactoryBean;
37 import org.mybatis.spring.mapper.MapperScannerConfigurer;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 import org.springframework.beans.BeanWrapper;
42 import org.springframework.beans.BeanWrapperImpl;
43 import org.springframework.beans.factory.BeanFactory;
44 import org.springframework.beans.factory.BeanFactoryAware;
45 import org.springframework.beans.factory.InitializingBean;
46 import org.springframework.beans.factory.ObjectProvider;
47 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
48 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
49 import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
50 import org.springframework.boot.autoconfigure.AutoConfigureAfter;
51 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
52 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
53 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
54 import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
55 import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
56 import org.springframework.boot.context.properties.EnableConfigurationProperties;
57 import org.springframework.context.annotation.Bean;
58 import org.springframework.context.annotation.Import;
59 import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
60 import org.springframework.core.io.Resource;
61 import org.springframework.core.io.ResourceLoader;
62 import org.springframework.core.type.AnnotationMetadata;
63 import org.springframework.util.Assert;
64 import org.springframework.util.CollectionUtils;
65 import org.springframework.util.ObjectUtils;
66 import org.springframework.util.StringUtils;
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81 @org.springframework.context.annotation.Configuration
82 @ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
83 @ConditionalOnSingleCandidate(DataSource.class)
84 @EnableConfigurationProperties(MybatisProperties.class)
85 @AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
86 public class MybatisAutoConfiguration implements InitializingBean {
87
88 private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);
89
90 private final MybatisProperties properties;
91
92 private final Interceptor[] interceptors;
93
94 private final TypeHandler[] typeHandlers;
95
96 private final LanguageDriver[] languageDrivers;
97
98 private final ResourceLoader resourceLoader;
99
100 private final DatabaseIdProvider databaseIdProvider;
101
102 private final List<ConfigurationCustomizer> configurationCustomizers;
103
104 public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider,
105 ObjectProvider<TypeHandler[]> typeHandlersProvider, ObjectProvider<LanguageDriver[]> languageDriversProvider,
106 ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider,
107 ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
108 this.properties = properties;
109 this.interceptors = interceptorsProvider.getIfAvailable();
110 this.typeHandlers = typeHandlersProvider.getIfAvailable();
111 this.languageDrivers = languageDriversProvider.getIfAvailable();
112 this.resourceLoader = resourceLoader;
113 this.databaseIdProvider = databaseIdProvider.getIfAvailable();
114 this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
115 }
116
117 @Override
118 public void afterPropertiesSet() {
119 checkConfigFileExists();
120 }
121
122 private void checkConfigFileExists() {
123 if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {
124 Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());
125 Assert.state(resource.exists(),
126 "Cannot find config location: " + resource + " (please add config file or check your Mybatis configuration)");
127 }
128 }
129
130 @Bean
131 @ConditionalOnMissingBean
132 public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
133 SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
134 factory.setDataSource(dataSource);
135 factory.setVfs(SpringBootVFS.class);
136 if (StringUtils.hasText(this.properties.getConfigLocation())) {
137 factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
138 }
139 applyConfiguration(factory);
140 if (this.properties.getConfigurationProperties() != null) {
141 factory.setConfigurationProperties(this.properties.getConfigurationProperties());
142 }
143 if (!ObjectUtils.isEmpty(this.interceptors)) {
144 factory.setPlugins(this.interceptors);
145 }
146 if (this.databaseIdProvider != null) {
147 factory.setDatabaseIdProvider(this.databaseIdProvider);
148 }
149 if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
150 factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
151 }
152 if (this.properties.getTypeAliasesSuperType() != null) {
153 factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
154 }
155 if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
156 factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
157 }
158 if (!ObjectUtils.isEmpty(this.typeHandlers)) {
159 factory.setTypeHandlers(this.typeHandlers);
160 }
161 if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
162 factory.setMapperLocations(this.properties.resolveMapperLocations());
163 }
164 Set<String> factoryPropertyNames = Stream
165 .of(new BeanWrapperImpl(SqlSessionFactoryBean.class).getPropertyDescriptors()).map(PropertyDescriptor::getName)
166 .collect(Collectors.toSet());
167 Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
168 if (factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers)) {
169
170 factory.setScriptingLanguageDrivers(this.languageDrivers);
171 if (defaultLanguageDriver == null && this.languageDrivers.length == 1) {
172 defaultLanguageDriver = this.languageDrivers[0].getClass();
173 }
174 }
175 if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) {
176
177 factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver);
178 }
179
180 return factory.getObject();
181 }
182
183 private void applyConfiguration(SqlSessionFactoryBean factory) {
184 Configuration configuration = this.properties.getConfiguration();
185 if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
186 configuration = new Configuration();
187 }
188 if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
189 for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
190 customizer.customize(configuration);
191 }
192 }
193 factory.setConfiguration(configuration);
194 }
195
196 @Bean
197 @ConditionalOnMissingBean
198 public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
199 ExecutorType executorType = this.properties.getExecutorType();
200 if (executorType != null) {
201 return new SqlSessionTemplate(sqlSessionFactory, executorType);
202 } else {
203 return new SqlSessionTemplate(sqlSessionFactory);
204 }
205 }
206
207
208
209
210
211
212 public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar {
213
214 private BeanFactory beanFactory;
215
216 @Override
217 public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
218
219 if (!AutoConfigurationPackages.has(this.beanFactory)) {
220 logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");
221 return;
222 }
223
224 logger.debug("Searching for mappers annotated with @Mapper");
225
226 List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
227 if (logger.isDebugEnabled()) {
228 packages.forEach(pkg -> logger.debug("Using auto-configuration base package '{}'", pkg));
229 }
230
231 BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
232 builder.addPropertyValue("processPropertyPlaceHolders", true);
233 builder.addPropertyValue("annotationClass", Mapper.class);
234 builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
235 BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);
236 Stream.of(beanWrapper.getPropertyDescriptors())
237
238 .filter(x -> x.getName().equals("lazyInitialization")).findAny()
239 .ifPresent(x -> builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}"));
240 registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
241 }
242
243 @Override
244 public void setBeanFactory(BeanFactory beanFactory) {
245 this.beanFactory = beanFactory;
246 }
247
248 }
249
250
251
252
253
254 @org.springframework.context.annotation.Configuration
255 @Import(AutoConfiguredMapperScannerRegistrar.class)
256 @ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class })
257 public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
258
259 @Override
260 public void afterPropertiesSet() {
261 logger.debug(
262 "Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.");
263 }
264
265 }
266
267 }