Mybatis源码学习(五)整合Spring原理
思路:
1使用Spring中的组件扫描,扫描所有Mapper注入到Spring容器中
2 mybatis中的SqlSessionFactory注入到spring容器
3 mybatis执行blogMapper.query时,使用动态代理,注入blogMapper时需要处理BeanDefinition时,name为blogMapper,实体类应该对应代理类
App.java
@ComponentScan("szjspringdemo") @Import(SzjImportBeanDefinitionRegistrar.class) public class App { @Bean public SqlSessionFactory sqlSessionFactory() throws IOException{ InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); return sqlSessionFactory; } public static void main( String[] args ) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(App.class); applicationContext.refresh(); SzjService szjService = applicationContext.getBean("szjService", SzjService.class); szjService.test(); } }
SzjService.java
@Component public class SzjService { @Autowired private BlogMapper blogMapper; public void test(){ System.out.println(blogMapper.query(101).getUsername()); } }
Blog.java
public class Blog { private Integer id; private String username; private String context; }
BlogMapper.java
public interface BlogMapper { @Select("select * from blog where id=#{id}") Blog query(Integer id); }
SzjFactoryBean.java
public class SzjFactoryBean implements FactoryBean { private Class mapperClass; private SqlSession sqlSession; public SzjFactoryBean(Class mapperClass){ this.mapperClass = mapperClass; } @Autowired public void setSqlSession(SqlSessionFactory sqlSessionFactory){ sqlSessionFactory.getConfiguration().addMapper(mapperClass); this.sqlSession = sqlSessionFactory.openSession(); } @Override public Object getObject() throws Exception { return sqlSession.getMapper(mapperClass); } @Override public Class<?> getObjectType() { return mapperClass; } }
SzjImportBeanDefinitionRegistrar.java
public class SzjImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //扫描,1扫描路径 2扫描 String path = "szjspringdemo.mapper"; SzjScanner szjScanner = new SzjScanner(registry); szjScanner.addIncludeFilter(new TypeFilter() { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { return true; } }); szjScanner.scan(path); } }
SzjScanner.java
public class SzjScanner extends ClassPathBeanDefinitionScanner { public SzjScanner(BeanDefinitionRegistry registry){ super(registry); } @Override public int scan(String... basePackages) { int i = super.scan(basePackages);//走spring的扫描逻辑 System.out.println(i); return i; } @Override protected Set<BeanDefinitionHolder> doScan(String... basePackages) { Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages); for(BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) { BeanDefinition beanDefinition = beanDefinitionHolder.getBeanDefinition(); beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName()); beanDefinition.setBeanClassName(SzjFactoryBean.class.getName()); } System.out.println(beanDefinitionHolders); return beanDefinitionHolders; } @Override protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { return beanDefinition.getMetadata().isInterface(); } }
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <properties resource="jdbc.properties"/> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> </configuration>
jdbc.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://192.168.*.*:3306/devdb?serverTimezone=UTC
username=***
password=***
分析
@ComponentScan("szjspringdemo") @Import(SzjImportBeanDefinitionRegistrar.class) public class App { @Bean public SqlSessionFactory sqlSessionFactory() throws IOException{ InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); return sqlSessionFactory; } public static void main(String[] args ) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(App.class); applicationContext.refresh(); SzjService szjService = applicationContext.getBean("szjService", SzjService.class); szjService.test(); } }
先看main方法,首先创建一个spring容器。
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
这个容器的构造器中初始化scanner和reader
public AnnotationConfigApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); }
然后回到main方法,注册App类,把App类注入到beanFactory的beanDefinitionMap中
applicationContext.register(App.class);
然后,refresh
applicationContext.refresh();
执行AbstractApplicationContext中的refresh方法
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } } }
执行如下方法
invokeBeanFactoryPostProcessors(beanFactory);
public static void invokeBeanFactoryPostProcessors( ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) { // Invoke BeanDefinitionRegistryPostProcessors first, if any. Set<String> processedBeans = new HashSet<>(); if (beanFactory instanceof BeanDefinitionRegistry) { BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>(); List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>(); // 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. List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>(); // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered. String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); } } invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); // Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered. ... // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear. }
经过几次调用,到达ConfigurationClassPostProcessor.processConfigBeanDefinitions()
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } } Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); do { parser.parse(candidates); parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
this.reader.loadBeanDefinitions(configClasses);
} while (!candidates.isEmpty()); } }
ConfigurationClassUtils的checkConfigurationClassCandidate方法,这里判断是fullConfig还是LiteConfig。如果以注解@Component,@ComponentScan,@Import,@ImportResource标注,则是LiteConfig。
然后执行
parser.parse(candidates);
经过几步调用执行ConfigurationClassParser.doProcessConfigurationClass()。在这里处理@ComponentScan、@Import、@Bean注解。
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { // Process any @ComponentScan annotations Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // The config class is annotated with @ComponentScan -> perform the scan immediately Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if needed for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } } // Process any @Import annotations processImports(configClass, sourceClass, getImports(sourceClass), true); // Process individual @Bean methods Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } }
我们再回到 this.reader.loadBeanDefinitions(configClasses); 这里将@Import和@Bean注解标注的类注册到beanFactory的beanDefinitionMap中
以上总结大篇幅其实是讲spring容器初始化的过程,对于整合mybatis的精髓好像还没找到。