Mybatis的@MapperScan中的basePackages属性值,只能获取到系统的占位符,不能获取到Application.properties定义的占位符
@MapperScan
的处理类 MapperScannerRegistrar
MapperScannerRegistrar.registerBeanDefinitions()
-> new ClassPathMapperScanner(registry)
-> super(registry, false)
-> this(registry, useDefaultFilters, getOrCreateEnvironment(registry))
->
private static Environment getOrCreateEnvironment(BeanDefinitionRegistry registry) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
if (registry instanceof EnvironmentCapable) {
return ((EnvironmentCapable) registry).getEnvironment();
}
return new StandardEnvironment();
}
这里的registry 不是EnvironmentCapable,所以生成了一个新的环境类
和
@MapperScan
的属性一样只是改变@Import(ExpandMapperScannerRegistrar.class)
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(ExpandMapperScannerRegistrar.class)
public @interface ExpandMapperScan {
/** * Alias for the {@link #basePackages()} attribute. Allows for more concise * annotation declarations e.g.: * {@code @EnableMyBatisMapperScanner("org.my.pkg")} instead of {@code * @EnableMyBatisMapperScanner(basePackages= "org.my.pkg"})}. */
String[] value() default {};
/** * Base packages to scan for MyBatis interfaces. Note that only interfaces * with at least one method will be registered; concrete classes will be * ignored. */
String[] basePackages() default {};
/** * Type-safe alternative to {@link #basePackages()} for specifying the packages * to scan for annotated components. The package of each class specified will be scanned. * Consider creating a special no-op marker class or interface in each package * that serves no purpose other than being referenced by this attribute. */
Class>[] basePackageClasses() default {};
/** * The {@link BeanNameGenerator} class to be used for naming detected components * within the Spring container. */
Class extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
/** * This property specifies the annotation that the scanner will search for. * * The scanner will register all interfaces in the base package that also have * the specified annotation. *
* Note this can be combined with markerInterface. */
Class extends Annotation> annotationClass() default Annotation.class;
/** * This property specifies the parent that the scanner will search for. * * The scanner will register all interfaces in the base package that also have * the specified interface class as a parent. *
* Note this can be combined with annotationClass. */
Class> markerInterface() default Class.class;
/** * Specifies which {@code SqlSessionTemplate} to use in the case that there is * more than one in the spring context. Usually this is only needed when you * have more than one datasource. */
String sqlSessionTemplateRef() default "";
/** * Specifies which {@code SqlSessionFactory} to use in the case that there is * more than one in the spring context. Usually this is only needed when you * have more than one datasource. */
String sqlSessionFactoryRef() default "";
/** * Specifies a custom MapperFactoryBean to return a mybatis proxy as spring bean. * */
Class extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
}
和
MapperScannerRegistrar
内容一样,只是在创建ClassPathMapperScanner
时,手动注入Environment
import org.mybatis.spring.mapper.ClassPathMapperScanner;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
public class ExpandMapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware{
private ResourceLoader resourceLoader;
Environment environment;
/** * {@inheritDoc} */
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(ExpandMapperScan.class.getName()));
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
// 手动注入环境变量
scanner.setEnvironment(environment);
// this check is needed in Spring 3.1
if (resourceLoader != null) {
scanner.setResourceLoader(resourceLoader);
}
Class extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
scanner.setAnnotationClass(annotationClass);
}
Class> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
scanner.setMarkerInterface(markerInterface);
}
Class extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {
scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
}
Class extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
}
scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
List basePackages = new ArrayList();
for (String pkg : annoAttrs.getStringArray("value")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (String pkg : annoAttrs.getStringArray("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (Class> clazz : annoAttrs.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
scanner.registerFilters();
scanner.doScan(StringUtils.toStringArray(basePackages));
}
/** * {@inheritDoc} */
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
}
具体如何解析占位符,可以自行跟踪源码,这里就不描述了