• Mybatis源码:整合Spring和Springboot原理


    看现象

    本文基于mybatis-spring 2.0以上版本,低版本的源码与本文所展示的不同。

    整合Spring

    依赖:

        implementation group: 'org.mybatis', name: 'mybatis', version: '3.5.5'
        implementation group: 'org.mybatis', name: 'mybatis-spring', version: '2.0.5'
        implementation group: 'mysql', name: 'mysql-connector-java', version: '8.0.23'
        api(project(":spring-jdbc"))
    

    主配置类:

    @Configuration
    @MapperScan("com.wj.scan.bean")
    public class ScanConfig {
    
        //配置数据源
    	@Bean
    	public DataSource dataSource(){
    		DriverManagerDataSource driverManagerDataSource
    				= new DriverManagerDataSource();
    		driverManagerDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
    		driverManagerDataSource.setPassword("1234");
    		driverManagerDataSource.setUsername("root");
    		driverManagerDataSource.setUrl("jdbc:mysql://localhost:3306/study?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC");
    		return driverManagerDataSource;
    	}
        
        //配置SqlSessionFactory
    	@Bean
    	public SqlSessionFactory sqlSessionFactory() throws Exception {
    		SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
    		factoryBean.setDataSource(dataSource());
    		return factoryBean.getObject();
    	}
    
    }
    

    mapper类:在com.wj.scan.bean包下

    public interface IM {
    
    	@Select("select * from tb_resume")
    	List<Map<String, Object>> select();
    
    }
    

    数据库和数据表:

    image-20220720133753566

    main方法:

    public class ScanMain {
    	public static void main(String[] args) {
    		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
    		applicationContext.register(ScanConfig.class);
    		applicationContext.refresh();
    
    		IM im = applicationContext.getBean(IM.class);
    		System.out.println(im.select());
    
    	}
    }
    

    image-20220720134031552

    可以看到,Mybatis整合Spring时,通过在配置类上添加@MapperScan扫描指定包,就可以扫描到Mapper接口。

    整合Spring Boot

    依赖:

        <dependencies>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.2.2</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    

    mapper:

    @Mapper
    public interface IM {
    
        @Select("select * from tb_resume")
        public List<Map<String, Object>> select();
    }
    

    配置文件application.yaml:

    spring:
      datasource:
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/study?serverTimezone=GMT%2B8&useSSL=true
        username: root
        password: 1234
    

    springboot主启动类:

    @SpringBootApplication
    public class BootMybatisApplication {
        public static void main(String[] args) {
            SpringApplication.run(BootMybatisApplication.class, args);
        }
    }
    

    测试方法:

    @SpringBootTest
    class BootMybatisApplicationTests {
        @Autowired
        IM im;
        @Test
        void contextLoads() {
            System.out.println(im.select());
        }
    }
    

    运行结果:

    image-20220720135010682

    可以看到,Mybatis整合Spring boot时候,是不需要加上@MapperScan的,但是必须要在Mapper接口上标注@Mapper注解。

    Mybatis整合Spring和Springboot是不一样的,所需要的配置和需要添加的注解也是不同的,那么造成这种现象必须要深究源码。

    前置知识

    FactoryBean

    FactoryBean是spring提供的一个接口,通过该接口我可以高度定制bean的创建过程。

    下面通过示例说明FactoryBean的使用:

    public class Book {
    }
    
    @Component
    public class BookFactoryBean implements FactoryBean<Book> {
        //用来创建Bean。当IoC容器通过getBean方法来FactoryBean创建的实例时实际获取的不是FactoryBean 本身而是具体创建的T泛型实例
    	@Override
    	public Book getObject() throws Exception {
    		return new Book();
    	}
    
        //getObjectType() 获取 T getObject()中的返回值 T 的具体类型
    	@Override
    	public Class<?> getObjectType() {
    		return Book.class;
    	}
    }
    
    @ComponentScan("com.wj.factorybean")
    public class MainConfig {
    }
    
    public class Main {
    	public static void main(String[] args) {
    		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
    		System.out.println(applicationContext.getBean(Book.class));
    	}
    }
    

    运行效果:

    image-20220720203645337

    ConfigurationClassPostProcessor和BeanDefinitionRegistryPostProcessor

    ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor接口,该接口回调于AbstractApplicationContext#refresh()方法中的invokeBeanFactoryPostProcessors(beanFactory),通过此接口可以向BeanDefinitionRegistry中批量注册BeanDefinition

    public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
    		PriorityOrdered, ResourceLoaderAware, ApplicationStartupAware, BeanClassLoaderAware, EnvironmentAware {
    

    ConfigurationClassPostProcessor的注册:

    ConfigurationClassPostProcessor接口的注册早在new AnnotationConfigApplicationContext()的时候已经添加到ioc容器中:

    源码如下:这里内部会创建出一个AnnotatedBeanDefinitionReader对象,该对象可将一个Class渲染成BeanDefinition并注册到BeanDefinitionRegistry中:

    	public AnnotationConfigApplicationContext() {
    		StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
    		this.reader = new AnnotatedBeanDefinitionReader(this);
    		createAnnotatedBeanDefReader.end();
    		this.scanner = new ClassPathBeanDefinitionScanner(this);
    	}
    

    AnnotatedBeanDefinitionReader构造方法如下:

    	public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
    		this(registry, getOrCreateEnvironment(registry));
    	}
    
    	public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
    		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    		Assert.notNull(environment, "Environment must not be null");
    		this.registry = registry;
    		this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
    		AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    	}
    

    最后调用AnnotationConfigUtils类的registerAnnotationConfigProcessors方法:在这里ConfigurationClassPostProcessor就已经注册到ioc容器中的,这时候尚未实例化。

    	public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
    			BeanDefinitionRegistry registry, @Nullable Object source) {
    		//获取到DefaultListableBeanFactory,便于注册bean定义信息
    		DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
    		if (beanFactory != null) {
    			if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
    				//设置比较器(@Order @Priority Ordered)
    				beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
    			}
    			if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
    				beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
    			}
    		}
    		//创建BeanDefinitionHolder集合
    		Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
    		//注册ConfigurationClassPostProcessor  ==> BeanDefinitionRegistryPostProcessor ==> BeanFactoryPostProcessor
    		if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
    			RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
    			def.setSource(source);
    			beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
    		}
    		//注册AutowiredAnnotationBeanPostProcessor
    		if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
    			RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
    			def.setSource(source);
    			beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
    		}
    
    		// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
    		//注册CommonAnnotationBeanPostProcessor,用于解析@PostConstruct等jsr250注解
    		if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
    			RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
    			def.setSource(source);
    			beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
    		}
    
    		// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
    		if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
    			RootBeanDefinition def = new RootBeanDefinition();
    			try {
    				def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
    						AnnotationConfigUtils.class.getClassLoader()));
    			}
    			catch (ClassNotFoundException ex) {
    				throw new IllegalStateException(
    						"Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
    			}
    			def.setSource(source);
    			beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
    		}
    
    		//支持EventListener方法
    		if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
    			RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
    			def.setSource(source);
    			beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
    		}
    
    		if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
    			RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
    			def.setSource(source);
    			beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
    		}
    
    		return beanDefs;
    	}
    

    ConfigurationClassPostProcessor的执行:

    前面说到该接口回调与AbstractApplicationContext#refresh()方法中的invokeBeanFactoryPostProcessors(beanFactory),代码如下:

    	protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    		PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
    
    		// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
    		// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
    		if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
    			beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
    			beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    		}
    	}
    

    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors())代码如下:

    	//beanFactoryPostProcessors:通过API注册的BeanFactoryPostProcessor
    	public static void invokeBeanFactoryPostProcessors(
    			ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
    
    		// WARNING: Although it may appear that the body of this method can be easily
    		// refactored to avoid the use of multiple loops and multiple lists, the use
    		// of multiple lists and multiple passes over the names of processors is
    		// intentional. We must ensure that we honor the contracts for PriorityOrdered
    		// and Ordered processors. Specifically, we must NOT cause processors to be
    		// instantiated (via getBean() invocations) or registered in the ApplicationContext
    		// in the wrong order.
    		//
    		// Before submitting a pull request (PR) to change this method, please review the
    		// list of all declined PRs involving changes to PostProcessorRegistrationDelegate
    		// to ensure that your proposal does not result in a breaking change:
    		// https://github.com/spring-projects/spring-framework/issues?q=PostProcessorRegistrationDelegate+is%3Aclosed+label%3A%22status%3A+declined%22
    
    		// Invoke BeanDefinitionRegistryPostProcessors first, if any.
    		//存放已经处理完了的BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor
    		//防止重复执行
    		Set<String> processedBeans = new HashSet<>();
    
    		if (beanFactory instanceof BeanDefinitionRegistry) {
    			BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
    			//存放所有实现了BeanFactoryPostProcessor的
    			List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
    			//存放所有实现了BeanDefinitionRegistryPostProcessor的
    			List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
    
    			for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
    				if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
    					BeanDefinitionRegistryPostProcessor registryProcessor =
    							(BeanDefinitionRegistryPostProcessor) postProcessor;
    					//执行postProcessBeanDefinitionRegistry
    					registryProcessor.postProcessBeanDefinitionRegistry(registry);
    					registryProcessors.add(registryProcessor);
    				}
    				else {
    					regularPostProcessors.add(postProcessor);
    				}
    			}
    
    			// Do not initialize FactoryBeans here: We need to leave all regular beans
    			// uninitialized to let the bean factory post-processors apply to them!
    			// Separate between BeanDefinitionRegistryPostProcessors that implement
    			// PriorityOrdered, Ordered, and the rest.
    			//存放当前需要执行的BeanDefinitionRegistryPostProcessor
    			List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
    
    			// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
    			//根据类型查询bean的名字 (默认情况找到ConfigurationClassPostProcessor)
    			String[] postProcessorNames =
    					beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
    			for (String ppName : postProcessorNames) {
    				//实现了PriorityOrdered接口的
    				if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
    					//这里就会创建BeanDefinitionRegistryPostProcessor,存放到单例池中
    					currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
    					processedBeans.add(ppName);
    				}
    			}
    			sortPostProcessors(currentRegistryProcessors, beanFactory);
    			registryProcessors.addAll(currentRegistryProcessors);
                //默认情况下在此执行ConfigurationClassPostProcessor
    			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
    			currentRegistryProcessors.clear();
    
    			// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
    			//实现了Ordered接口的
    			postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
    			for (String ppName : postProcessorNames) {
    				if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
    					currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
    					processedBeans.add(ppName);
    				}
    			}
    			sortPostProcessors(currentRegistryProcessors, beanFactory);
    			registryProcessors.addAll(currentRegistryProcessors);
    			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
    			currentRegistryProcessors.clear();
    
    			//调用所有其他 BeanDefinitionRegistryPostProcessor 直到不再出现。
    			// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
    			boolean reiterate = true;
    			while (reiterate) {
    				//终止循环
    				reiterate = false;
    				postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
    				for (String ppName : postProcessorNames) {
    					if (!processedBeans.contains(ppName)) {
    						currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
    						processedBeans.add(ppName);
    						//如果找到了,后面还需要继续循环 继续找
    						//找出来必然是子类 有可能会动态添加新的BeanDefinitionRegistryPostProcessor
    						reiterate = true;
    					}
    				}
    				sortPostProcessors(currentRegistryProcessors, beanFactory);
    				registryProcessors.addAll(currentRegistryProcessors);
    				invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
    				currentRegistryProcessors.clear();
    			}
    
    			// Now, invoke the postProcessBeanFactory callback of all processors handled so far.
    			//调用到目前为止处理的所有处理器的 postProcessBeanFactory 回调
    			invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
    			invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
    		}
    
    		else {
    			// Invoke factory processors registered with the context instance.
    			invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
    		}
    
    		// Do not initialize FactoryBeans here: We need to leave all regular beans
    		// uninitialized to let the bean factory post-processors apply to them!
    		String[] postProcessorNames =
    				beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
    
    		// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
    		// Ordered, and the rest.
    		//实现了priorityOrdered的父类集合
    		List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
    		//实现了ordered的父类集合
    		List<String> orderedPostProcessorNames = new ArrayList<>();
    		//没有实现排序
    		List<String> nonOrderedPostProcessorNames = new ArrayList<>();
    		for (String ppName : postProcessorNames) {
    			if (processedBeans.contains(ppName)) {
    				// skip - already processed in first phase above
    			}
    			else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
    				//为什么这里先实例化
    				//因为此时修改bean定义信息,在后面会生效
    				priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
    			}
    			else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
    				//这里不实例化
    				orderedPostProcessorNames.add(ppName);
    			}
    			else {
    				nonOrderedPostProcessorNames.add(ppName);
    			}
    		}
    
    		// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
    		//执行实现PriorityOrdered接口的BeanFactoryPostProcessor
    		sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
    		invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
    
    		// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
    		//执行实现Ordered接口的BeanFactoryPostProcessor
    		List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
    		for (String postProcessorName : orderedPostProcessorNames) {
    			orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
    		}
    		sortPostProcessors(orderedPostProcessors, beanFactory);
    		invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
    
    		// Finally, invoke all other BeanFactoryPostProcessors.
    		//执行剩下的BeanFactoryPostProcessor
    		List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
    		for (String postProcessorName : nonOrderedPostProcessorNames) {
    			nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
    		}
    		invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
    
    		// Clear cached merged bean definitions since the post-processors might have
    		// modified the original metadata, e.g. replacing placeholders in values...
    		beanFactory.clearMetadataCache();
    	}
    

    那么ConfigurationClassPostProcessor类做了什么事情?

    该类会解析所有当前ioc容器中已经解析的配置类,对各种注解的解析:@Bean,@ComponentScan,@Import,@Primary等等

    限于篇幅原因,这里不对ConfigurationClassPostProcessor类中的方法做解释,可看我之前写的一篇文章:Spring源码分析:@Configuration原理,该文章中对ConfigurationClassPostProcessor源码做了详细解释。

    ImportBeanDefinitionRegistrar

    该接口中有两个方法:通过该接口可以实现批量注册BeanDefinitionBeanDefinitionRegistry

    public interface ImportBeanDefinitionRegistrar {
    
    	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
    			BeanNameGenerator importBeanNameGenerator) {
    
    		registerBeanDefinitions(importingClassMetadata, registry);
    	}
    
    	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    	}
    
    }
    

    关于该接口方法的回调在ConfigurationClassParser#processImports()方法中。

    @ComponentScan原理

    ComponentScanAnnotationParser类中的parse方法:内部创建一个ClassPathBeanDefinitionScanner执行doScan方法进行扫描。

    题外话:AnnotationConfigApplicationContext类中也有个scan方法,如果传入包名进行扫描。默认情况下,@ComponentScanAnnotationConfigApplicationContext调用scan进行扫描最后的效果都是一样的。

    	public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
    		ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
    				componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
        
        	//中间部分代码省略...
            
            for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
    			for (TypeFilter typeFilter : typeFiltersFor(filter)) {
    				scanner.addIncludeFilter(typeFilter);
    			}
    		}
    		for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
    			for (TypeFilter typeFilter : typeFiltersFor(filter)) {
    				scanner.addExcludeFilter(typeFilter);
    			}
    		}
            boolean lazyInit = componentScan.getBoolean("lazyInit");
    		if (lazyInit) {
    			scanner.getBeanDefinitionDefaults().setLazyInit(true);
    		}
    
    		Set<String> basePackages = new LinkedHashSet<>();
    		String[] basePackagesArray = componentScan.getStringArray("basePackages");
    		for (String pkg : basePackagesArray) {
    			String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
    					ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
    			Collections.addAll(basePackages, tokenized);
    		}
    		for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
    			basePackages.add(ClassUtils.getPackageName(clazz));
    		}
    		if (basePackages.isEmpty()) {
    			basePackages.add(ClassUtils.getPackageName(declaringClass));
    		}
    
    		//排除自己,不需要扫描
    		scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
    			@Override
    			protected boolean matchClassName(String className) {
    				return declaringClass.equals(className);
    			}
    		});
    		//开始扫描
    		return scanner.doScan(StringUtils.toStringArray(basePackages));
        }
    

    先看ClassPathBeanDefinitionScanner的构造方法(比较重要):

    这里如果我们ComponentScan中没有配置useDefaultFilters属性,默认为true,就会默认注册三个includeFilters提供了对@Component@ManagedBean@Named这三个注解的扫描支持(如果添加了jsr250和jsr303 jar包的情况下)。

    ExcludeFilter默认会有一个排除自己不用扫描的AbstractTypeHierarchyTraversingFilter匿名内部类.

    	public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
    			Environment environment, @Nullable ResourceLoader resourceLoader) {
    
    		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    		this.registry = registry;
    
    		if (useDefaultFilters) {
    			//注册默认的过滤器
    			registerDefaultFilters();
    		}
    		//设置Environment和ResourceLoader
    		setEnvironment(environment);
    		setResourceLoader(resourceLoader);
    	}
    
    	protected void registerDefaultFilters() {
    		//提供对@Component注解的支持
    		this.includeFilters.add(new AnnotationTypeFilter(Component.class));
    		ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
    		try {
    			//提供对@ManagedBean注解的支持
    			this.includeFilters.add(new AnnotationTypeFilter(
    					((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
    			logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
    		}
    		catch (ClassNotFoundException ex) {
    			// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
    		}
    		try {
    			//提供对@Named注解的支持
    			this.includeFilters.add(new AnnotationTypeFilter(
    					((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
    			logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
    		}
    		catch (ClassNotFoundException ex) {
    			// JSR-330 API not available - simply skip.
    		}
    	}
    

    ClassPathBeanDefinitionScanner类的scan方法如下:主要调用doScan:

    	public int scan(String... basePackages) {
    		int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
    
    		doScan(basePackages);
    
    		// Register annotation config processors, if necessary.
    		if (this.includeAnnotationConfig) {
    			AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    		}
    
    		return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
    	}
    

    doScan方法如下:

    	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    		Assert.notEmpty(basePackages, "At least one base package must be specified");
    		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    		for (String basePackage : basePackages) {
    			//查找所有候选的组件
    			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
    			for (BeanDefinition candidate : candidates) {
    				//解析出ScopeMetadata
    				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
    				candidate.setScope(scopeMetadata.getScopeName());
    				//通过名字生成器生成bean的名字
    				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
    				if (candidate instanceof AbstractBeanDefinition) {
    					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
    				}
    				if (candidate instanceof AnnotatedBeanDefinition) {
    					//解析配置类上的@Lazy、@Primary、@DependsOn、@Role、@Description的注解信息
    					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
    				}
    				//判断是否注册了,以及需要注册的BeanDefinition和现有的BeanDefinition是否有冲突
    				if (checkCandidate(beanName, candidate)) {
    					//注册bean定义信息
    					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
    					definitionHolder =
    							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    					beanDefinitions.add(definitionHolder);
    					registerBeanDefinition(definitionHolder, this.registry);
    				}
    			}
    		}
    		return beanDefinitions;
    	}
    

    不难观察到,查找BeanDefinition的核心代码在findCandidateComponents方法中:

    	public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    		if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
                //这里是用来处理META-INF/spring.components配置的component,很少用到
    			return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
    		}
    		else {
                //默认进入这里
    			return scanCandidateComponents(basePackage);
    		}
    	}
    

    scanCandidateComponents方法如下:

    1. 这里先将需要扫描包下面的所有文件扫描成Resource,内部就是一个InputStream
    2. 然后通过ClassReader(ASM知识)去读取类的信息(这里不用Class.forName读取类的信息是因为可以避免类加载到JVM中)
    3. 扫描到类的信息后,通过调用isCandidateComponent方法判断时候是spring识别的组件,如果是,则添加到candidates集合中并返回。
    	private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    		Set<BeanDefinition> candidates = new LinkedHashSet<>();
    		//通过包名获取包所在的路径得到一个文件 流
    		try {
    			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
    					resolveBasePackage(basePackage) + '/' + this.resourcePattern;
    			Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
    			boolean traceEnabled = logger.isTraceEnabled();
    			boolean debugEnabled = logger.isDebugEnabled();
    			for (Resource resource : resources) {
    				if (traceEnabled) {
    					logger.trace("Scanning " + resource);
    				}
    				if (resource.isReadable()) {
    					try {
    						//通过ASM读取类信息
    						MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
    						//判断是否是候选的组件
    						if (isCandidateComponent(metadataReader)) {
    							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
    							sbd.setSource(resource);
    							if (isCandidateComponent(sbd)) {
    								if (debugEnabled) {
    									logger.debug("Identified candidate component class: " + resource);
    								}
    								candidates.add(sbd);
    							}
    							else {
    								if (debugEnabled) {
    									logger.debug("Ignored because not a concrete top-level class: " + resource);
    								}
    							}
    						}
    						else {
    							if (traceEnabled) {
    								logger.trace("Ignored because not matching any filter: " + resource);
    							}
    						}
    					}
    					catch (Throwable ex) {
    						throw new BeanDefinitionStoreException(
    								"Failed to read candidate component class: " + resource, ex);
    					}
    				}
    				else {
    					if (traceEnabled) {
    						logger.trace("Ignored because not readable: " + resource);
    					}
    				}
    			}
    		}
    		catch (IOException ex) {
    			throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
    		}
    		return candidates;
    	}
    

    再看isCandidateComponent方法(重点),这里就是辨别一个类是否是ioc容器中的bean:

    默认情况下:excludeFilters中只有一个AbstractTypeHierarchyTraversingFilter排除自己,includeFilters会判断是否标注了@Component

    	protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
    		//首先判断这个类有没有被排除
    		for (TypeFilter tf : this.excludeFilters) {
    			if (tf.match(metadataReader, getMetadataReaderFactory())) {
    				return false;
    			}
    		}
    		for (TypeFilter tf : this.includeFilters) {
    			if (tf.match(metadataReader, getMetadataReaderFactory())) {
    				//判断@Condition注解,默认没加@Condition注解会返回true
    				return isConditionMatch(metadataReader);
    			}
    		}
    		return false;
    	}
    

    SpringBoot自动配置原理

    springboot启动时候,会扫描类路径下的所有META-INF/spring.factories文件,文件中的配置的所有配置类,会根据条件自动扫描到ioc容器。

    详情可见:Spring Boot:自动配置原理

    Mybatis整合Spring原理

    Mapper接口的扫描

    Mybatis整合Spring,主要通过添加@MapperScan,批量扫描Mapper接口,与@ComponentScan类似,这就前面我花大量篇幅写@ComponentScan原理的原因。

    所以我们从@MapperScan开始着手:这里@Import了一个MapperScannerRegistrar

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

    再看MapperScannerRegistrar类:实现了ImportBeanDefinitionRegistrar,前面提到通过该接口可以向ioc容器注册bean定义信息

    public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
        //其他不重要代码省略
    
      @Override
      public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
          //拿到MapperScan注解中的所有元信息
        AnnotationAttributes mapperScanAttrs = AnnotationAttributes
            .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
        if (mapperScanAttrs != null) {
            //调用registerBeanDefinitions注册bean定义信息
          registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
              generateBaseBeanName(importingClassMetadata, 0));
        }
      }
    
      void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
          BeanDefinitionRegistry registry, String beanName) {
    
          //创建BeanDefinitionBuilder
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
          //通过builder.addPropertyValue向bean定义信息中设置属性值,这样通过BeanDefinition创建bean的时候,就会把属性值设置到bean的字段中。
        builder.addPropertyValue("processPropertyPlaceHolders", true);
    
        Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
          //判断annotationClass是否是Annotation.class
        if (!Annotation.class.equals(annotationClass)) {
            //只有不为Annotation.class,才会把annotationClass的值设置进去
            //因为@MapperScan默认annotationClass的值就是@Annotation,所以Spring创建出来的MapperScannerConfigurer类中annotationClass就为null
          builder.addPropertyValue("annotationClass", annotationClass);
        }
          
          //中间一些属性设置,这里省略
    
        List<String> basePackages = new ArrayList<>();
        basePackages.addAll(
            Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
    
        basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
            .collect(Collectors.toList()));
    
        basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName)
            .collect(Collectors.toList()));
    
        if (basePackages.isEmpty()) {
          basePackages.add(getDefaultBasePackage(annoMeta));
        }
    
        String lazyInitialization = annoAttrs.getString("lazyInitialization");
        if (StringUtils.hasText(lazyInitialization)) {
          builder.addPropertyValue("lazyInitialization", lazyInitialization);
        }
    
        builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
    
        //注册bean定义信息
        registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
    
      }
    

    因为MapperScannerRegistrar向容器中添加了MapperScannerConfigurer组件,再看MapperScannerConfigurer类:

    实现了BeanDefinitionRegistryPostProcessor,根据前文所说,该类会被执行在PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors())方法中,并且回调postProcessBeanDefinitionRegistry方法,用于注册bean定义信息,所以mybatis极大可能就是通过该类注册Mapper接口的。

    public class MapperScannerConfigurer
        implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
    

    再看MapperScannerConfigurer实现的方法:

    postProcessBeanFactory方法主要用于修改bean的定义信息,或者修改BeanFactory的配置,这里只是一个空方法。

    postProcessBeanDefinitionRegistry中创建了一个ClassPathMapperScanner,然后设置了一些属性,调用其scan方法。

      @Override
      public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // left intentionally blank
      }
    
      @Override
      public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        if (this.processPropertyPlaceHolders) {
          processPropertyPlaceHolders();
        }
    
        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
        scanner.setAddToConfig(this.addToConfig);
          //这里annotationClass默认是null
        scanner.setAnnotationClass(this.annotationClass默认是null);
        scanner.setMarkerInterface(this.markerInterface);
        scanner.setSqlSessionFactory(this.sqlSessionFactory);
        scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
        scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
        scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
        scanner.setResourceLoader(this.applicationContext);
        scanner.setBeanNameGenerator(this.nameGenerator);
        scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
        if (StringUtils.hasText(lazyInitialization)) {
          scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
        }
        scanner.registerFilters();
        scanner.scan(
            StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
      }
    

    那么Mapper扫描的核心代码就在ClassPathMapperScanner中,先看该类的类图:

    image-20220720160257499

    ClassPathMapperScanner是mybatis-spring包中的,它继承了spring的ClassPathBeanDefinitionScanner,先看ClassPathMapperScanner类的构造方法:

    useDefaultFilters传的false,这样就不会创建默认的Filter

      public ClassPathMapperScanner(BeanDefinitionRegistry registry) {
        super(registry, false);
      }
    

    然后创建ClassPathMapperScanner的时候调用了registerFilters方法:

    因为this.annotationClass 是null,所以addIncludeFilter(new AnnotationTypeFilter(this.annotationClass))这行代码走不到。

      public void registerFilters() {
        boolean acceptAllInterfaces = true;
    
        // if specified, use the given annotation and / or marker interface
          //annotationClass默认为null
        if (this.annotationClass != null) {
          addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
          acceptAllInterfaces = false;
        }
    
        // override AssignableTypeFilter to ignore matches on the actual marker interface
        if (this.markerInterface != null) {
          addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
            @Override
            protected boolean matchClassName(String className) {
              return false;
            }
          });
          acceptAllInterfaces = false;
        }
    
          //acceptAllInterfaces默认为true,会进入到这个if
          //而这里这个filter内部match方法直接返回true,默认不过滤,即扫描所有的类
        if (acceptAllInterfaces) {
          // default include filter that accepts all classes
          addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
        }
    
        // exclude package-info.java
        addExcludeFilter((metadataReader, metadataReaderFactory) -> {
          String className = metadataReader.getClassMetadata().getClassName();
          return className.endsWith("package-info");
        });
      }
    

    之后调用ClassPathMapperScannerscan方法,仍然是父类的scan方法:

    	public int scan(String... basePackages) {
    		int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
    
    		doScan(basePackages);
    
    		// Register annotation config processors, if necessary.
    		if (this.includeAnnotationConfig) {
    			AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    		}
    
    		return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
    	}
    

    但是子类重写了doScan方法:

      @Override
      public Set<BeanDefinitionHolder> doScan(String... basePackages) {
          //调用的仍然是父类ClassPathBeanDefinitionScanner的doScan方法
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
    
        if (beanDefinitions.isEmpty()) {
          LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
              + "' package. Please check your configuration.");
        } else {
          processBeanDefinitions(beanDefinitions);
        }
    
        return beanDefinitions;
      }
    

    尽管说ClassPathMapperScannerdoScan方法先调用了父类ClassPathBeanDefinitionScannerdoScan方法,扫描逻辑与父类一样,但是要注意ClassPathMapperScanner重写了isCandidateComponent方法,它这里跟父类的isCandidateComponent判断逻辑不一样。

    第一个判断条件是判断扫描出来的类是否是接口:

      @Override
      protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
      }
    

    第二个判断条件isIndependent可能不好理解,我们可以看该方法上作者给的注释:判断该类是否是top-level class或者是nested class (static inner class)

    	/**
    	 * Determine whether the underlying class is independent, i.e. whether
    	 * it is a top-level class or a nested class (static inner class) that
    	 * can be constructed independently from an enclosing class.
    	 * top level class 或者 nested class,任意成立返回true
    	 */
    	boolean isIndependent();
    

    那么是什么意思?

    其实在jdk中的Class类的getEnclosingClass方法中有一段注释:总共有五种类

        @CallerSensitive
        public Class<?> getEnclosingClass() throws SecurityException {
            // There are five kinds of classes (or interfaces):
            // a) Top level classes 顶级类
            // b) Nested classes (static member classes) 静态内部类
            // c) Inner classes (non-static member classes) 非静态内部类
            // d) Local classes (named classes declared within a method) 在方法内定义的类
            // e) Anonymous classes 匿名类
    

    image-20220720194803117

    所以说@MapperScan扫描的是接口,并且是顶级类或者是静态内部类。(静态内部类也可以作为Mapper接口扫描进入ioc容器,我私下测试过了)

    至此就解释了Spring整合Mybatis只需要加上@MapperScan就可以扫描到Mapper接口。

    Bean定义信息的注册

    那么Mapper接口对应的bean定义信息是怎么创建成ioc容器的单例bean的?当执行doScan方法扫描到Mapper接口后,会执行processBeanDefinitions对当前扫描到的bean定义信息进行处理:

      private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
        GenericBeanDefinition definition;
          //遍历所有的beanDefinition
        for (BeanDefinitionHolder holder : beanDefinitions) {
          definition = (GenericBeanDefinition) holder.getBeanDefinition();
            //获取到bean的全类名
          String beanClassName = definition.getBeanClassName();
          LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
              + "' mapperInterface");
    
          // the mapper interface is the original class of the bean
          // but, the actual class of the bean is MapperFactoryBean
            //设置创建bean的构造器使用参数就是bean的全类名
          definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
            //mapperFactoryBeanClass对应的Class就是MapperFactoryBean.class
          definition.setBeanClass(this.mapperFactoryBeanClass);
    
          definition.getPropertyValues().add("addToConfig", this.addToConfig);
    
            //设置后面bean属性注入的sqlSessionFactory属性的值
          boolean explicitFactoryUsed = false;
          if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
            definition.getPropertyValues().add("sqlSessionFactory",
                new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
            explicitFactoryUsed = true;
          } else if (this.sqlSessionFactory != null) {
            definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
            explicitFactoryUsed = true;
          }
    
            //设置后面bean属性注入的sqlSessionTemplate属性的值
          if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
            if (explicitFactoryUsed) {
              LOGGER.warn(
                  () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
            }
            definition.getPropertyValues().add("sqlSessionTemplate",
                new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
            explicitFactoryUsed = true;
          } else if (this.sqlSessionTemplate != null) {
            if (explicitFactoryUsed) {
              LOGGER.warn(
                  () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
            }
            definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
            explicitFactoryUsed = true;
          }
    
          if (!explicitFactoryUsed) {
            LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
            definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
          }
          definition.setLazyInit(lazyInitialization);
        }
      }
    

    通过这里,可见Mapper接口创建成ioc容器单例bean的端倪:当容器创建bean的时候,进入bean的生命周期,首先spring会推断该类的构造方法,是MapperFactoryBean有参的构造方法,并且是Mapper接口的全类名字符串,

    再看MapperFactoryBean类(这里我只摘取了部分重要的代码在这里):

    1. 首先该类实现了spring中的FactoryBean接口,然而这里只有一个有参的构造方法,并且参数是Class类型,与上面提到的String类型有差异。这是因为spring在调用构造方法时候,会进行校验判断是否需要类型转换。
    2. (Spring类型转换不知道的可以阅读Spring源码分析:类型转换(一)之PropertyEditor和ConversionService)和 Spring源码分析:类型转换(二)之TypeConverter
    3. spring在创建MapperFactoryBean时会将String类型的全类名转换成Class类型。
    4. FactoryBean也是Spring的重要内容,这里getObject内部调用getSqlSession().getMapper(this.mapperInterface)方法生成指定接口的代理类,而这个方法就是Mybatis生成代理类的原理,比较简单,可参考:Mybatis源码:getMapper获取接口代理对象
    public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
      private Class<T> mapperInterface;
    
      private boolean addToConfig = true;
    
      public MapperFactoryBean() {
        // intentionally empty
      }
    
      public MapperFactoryBean(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
      }
        
      @Override
      public T getObject() throws Exception {
        return getSqlSession().getMapper(this.mapperInterface);
      }
        
      @Override
      public Class<T> getObjectType() {
        return this.mapperInterface;
      }
        
      @Override
      public boolean isSingleton() {
        return true;
      }
    
    }
    

    至此,Springmybatis是整合结束,后面就是FactoryBean的生命周期,属于Spring的内容,这里不再赘述。

    Mybatis整合Spring Boot原理

    先看mybatis-spring-boot-starter中添加了哪些依赖,然后再做具体分析:

    image-20220720195819047

    根据依赖图,不难得出springboot整合mybatis的核心应该在mybatis-spring-boot-autoconfigure中,而自己开发过springboot的场景启动器的同学们,也知道自定义场景启动器约定俗成的名字规范就是xxx-spring-boot-mybatis

    直接去该jar中看对应的spring.factories文件,springboot会扫描其中配置的两个自动配置类。而核心就在MybatisAutoConfiguration中。

    image-20220720200137532

    该类大致结构如下,部分代码省略:

    @org.springframework.context.annotation.Configuration
    @ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
    @ConditionalOnSingleCandidate(DataSource.class)
    @EnableConfigurationProperties(MybatisProperties.class)
    @AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
    public class MybatisAutoConfiguration implements InitializingBean {
      @Bean
      @ConditionalOnMissingBean
      public SqlSessionFactory sqlSessionFactory(DataSource dataSource){....}
        
      @Bean
      @ConditionalOnMissingBean
      public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {....}
        
      @org.springframework.context.annotation.Configuration
      @Import(AutoConfiguredMapperScannerRegistrar.class)
      @ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class })
      public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
    }
    
    1. 注册了SqlSessionFactorySqlSessionTemplate
    2. 添加了MapperScannerRegistrarNotFoundConfiguration配置类,而在这个类上判断IOC容器中没有MapperFactoryBean类和MapperScannerConfigurer类则会生效,默认是生效的,因为我没有手动注册或者手动扫描到ioc容器
    3. MapperScannerRegistrarNotFoundConfiguration类上@Import了一个类AutoConfiguredMapperScannerRegistrar

    再看AutoConfiguredMapperScannerRegistrar类:

    1. 该类通过实现ImportBeanDefinitionRegistrar接口向容器中注册MapperScannerConfigurer组件,这就是前面MapperScannerRegistrarNotFoundConfiguration生效的条件,只有容器中没有MapperScannerConfigurer组件时,才会向容器中注册默认的MapperScannerConfigurer
    2. 这里需要注意的是MapperScannerConfigurer中的annotationClass属性会被赋值,默认是Mapper.class,然后导致ClassPathMapperScanner中会创建一个关于@Mapper注解的AnnotationTypeFilter,而不是所有扫描的接口TypeFilter.match都返回true。
    3. 这与前面Spring整合Mybatis是不同的,前面注册到ioc容器中MapperScannerConfigurerannotationClass属性默认是null,最终导致Mapper接口不需要添加@Mapper注解也能扫描到。但是对于SpringBoot来说,因为MapperScannerConfigurer中的annotationClass默认是Mapper.class,导致Mybtis在扫描包的时候会看接口上是否存在@Mapper注解。
     	//实现了ImportBeanDefinitionRegistrar接口,通过该接口可以注册bean的定义信息
      public static class AutoConfiguredMapperScannerRegistrar
          implements BeanFactoryAware, EnvironmentAware, ImportBeanDefinitionRegistrar {
    
        private BeanFactory beanFactory;
        private Environment environment;
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    
          if (!AutoConfigurationPackages.has(this.beanFactory)) {
            logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");
            return;
          }
    
          logger.debug("Searching for mappers annotated with @Mapper");
    
            //获取扫描包的路径,默认是SpringBoot主启动类所在包的路径
          List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
          if (logger.isDebugEnabled()) {
            packages.forEach(pkg -> logger.debug("Using auto-configuration base package '{}'", pkg));
          }
    		//以下就是创建MapperScannerConfigurer类的bean定义信息过程
          BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
          builder.addPropertyValue("processPropertyPlaceHolders", true);
            //设置annotationClass为@Mapper注解
            //当MapperScannerConfigurer创建时,走到populateBean方法时,会把属性值设置到属性中
          builder.addPropertyValue("annotationClass", Mapper.class);
          builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
          BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);
          Set<String> propertyNames = Stream.of(beanWrapper.getPropertyDescriptors()).map(PropertyDescriptor::getName)
              .collect(Collectors.toSet());
          if (propertyNames.contains("lazyInitialization")) {
            // Need to mybatis-spring 2.0.2+
            builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}");
          }
          if (propertyNames.contains("defaultScope")) {
            // Need to mybatis-spring 2.0.6+
            builder.addPropertyValue("defaultScope", "${mybatis.mapper-default-scope:}");
          }
    
          // for spring-native
          boolean injectSqlSession = environment.getProperty("mybatis.inject-sql-session-on-mapper-scan", Boolean.class,
              Boolean.TRUE);
          if (injectSqlSession && this.beanFactory instanceof ListableBeanFactory) {
            ListableBeanFactory listableBeanFactory = (ListableBeanFactory) this.beanFactory;
            Optional<String> sqlSessionTemplateBeanName = Optional
                .ofNullable(getBeanNameForType(SqlSessionTemplate.class, listableBeanFactory));
            Optional<String> sqlSessionFactoryBeanName = Optional
                .ofNullable(getBeanNameForType(SqlSessionFactory.class, listableBeanFactory));
            if (sqlSessionTemplateBeanName.isPresent() || !sqlSessionFactoryBeanName.isPresent()) {
              builder.addPropertyValue("sqlSessionTemplateBeanName",
                  sqlSessionTemplateBeanName.orElse("sqlSessionTemplate"));
            } else {
              builder.addPropertyValue("sqlSessionFactoryBeanName", sqlSessionFactoryBeanName.get());
            }
          }
          builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    
          registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
        }
    

    至此:mybatis整合Spring和整合SpringBoot的区别和原理讲完。

  • 相关阅读:
    Eclipse常用开发插件
    getOutputStream() 的问题
    JSP内置对象之WEB安全性及config对象
    windows开机后键盘失灵(非硬件原因)解决办法
    Eclipse下如何导入jar包
    更改Eclipse下Tomcat的部署目录 ,防止上传的文件是到eclipse的克隆的tomcat上的webapp,而不是tomcat本身的webapp
    大数据的挑战:数据质量和历史偏见
    HR数据分析常用的50个公式
    HR数据分析常用的50个公式
    python中的随机函数random的用法示例
  • 原文地址:https://www.cnblogs.com/wwjj4811/p/16499883.html
Copyright © 2020-2023  润新知