Solve @ MapperScan scanning package mixed with @ Service and other problems

Posted by malam on Wed, 15 Jan 2020 13:35:10 +0100

Problem description

@MapperScan annotation is generally configured to use dao or mapper's scanning package to operate the database, which is generally an interface. If there are other interfaces in dao layer, such as @ Service, an error will be reported

Solution 1

Remove the service package. The method is feasible

Solution 2

Do not use @ MapperScan. Add @ Mapper annotation to each dao or Mapper. The method is feasible

Solution 3

With user-defined annotations, you don't need to do much by yourself when mybatis's annotations are relatively complete

1. Create annotation @ MyMapperScan

All the attributes in it are copied from @ MapperScan
MapperScannerRegistrar to its own

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MyMapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {

  String[] value() default {};

  String[] basePackages() default {};

 
  Class<?>[] basePackageClasses() default {};

 
  Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

  Class<? extends Annotation> annotationClass() default Annotation.class;

  Class<?> markerInterface() default Class.class;

 
  String sqlSessionTemplateRef() default "";


  String sqlSessionFactoryRef() default "";

  
  Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;

  String lazyInitialization() default "";

}

Mymapperscannerregistratir scan registrar

This class is exactly the same as mybatis. The only difference is that MyClassPathMapperScanner is its own scan.

public class MyMapperScannerRegistrar extends MapperScannerRegistrar {

    private ResourceLoader resourceLoader;

    /**
     * {@inheritDoc}
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
        //This is my own
        ClassPathMapperScanner scanner = new MyClassPathMapperScanner(registry);

        // 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<String> basePackages = new ArrayList<String>();
        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;
    }

}

MyClassPathMapperScanner

My own scanning class is also mybatis, which is a little bit changed in judgment

public class MyClassPathMapperScanner extends ClassPathMapperScanner {
    public MyClassPathMapperScanner(BeanDefinitionRegistry registry) {
        super(registry);
    }

    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        boolean flag = super.isCandidateComponent(beanDefinition);
        //The package with Mapper will be represented by mybatis
        boolean mapper = beanDefinition.getBeanClassName().contains("Mapper");
        return flag && mapper;
    }
}

Now you only need to use your own scanning annotation, which is exactly the same as mybatis.

Solution 4

This is for the third point. When the author uses the third point, mybatis version is 3.4.6
Mybatis spring version 1.3.2, spring version 5.x

When mybatis spring version 3.5.2 is 2.0.2
The way MapperScannerRegistrar class scans has changed a little,
The MapperScannerConfigurer class needs to be overridden, and the others remain unchanged

Complete!
Others can refer to other articles of the author spring custom component scanning, imitating @ MapperScan

Published 42 original articles, won praise 13, visited 6623
Private letter follow

Topics: Mybatis Spring Database