這篇文章主要介紹“Mybatis-spring自動(dòng)注入機(jī)制原理”,在日常操作中,相信很多人在Mybatis-spring自動(dòng)注入機(jī)制原理問題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”Mybatis-spring自動(dòng)注入機(jī)制原理”的疑惑有所幫助!接下來,請(qǐng)跟著小編一起來學(xué)習(xí)吧!
成都創(chuàng)新互聯(lián)公司從2013年成立,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目成都網(wǎng)站制作、成都做網(wǎng)站、外貿(mào)營銷網(wǎng)站建設(shè)網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元新林做網(wǎng)站,已為上家服務(wù),為新林各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:13518219792
本篇文章的主要目的有兩個(gè):
項(xiàng)目中使用mybatis,我們只定義了mapper的接口,并沒有實(shí)現(xiàn),那這些實(shí)現(xiàn)類的bean是如何生成的?
mapper接口的實(shí)例bean是如何注入到spring容器的?
包名 | 版本號(hào) |
---|---|
spring-boot | 2.1.9.RELEASE |
mybatis-spring | 2.0.0 |
mybatis-plus-boot-starter | 3.1.0 |
mybatis | 3.5.0 |
項(xiàng)目中我們使用@MapperScan來配置mapper接口的路徑,如下圖
@EnableTransactionManagement @Configuration @MapperScan("com.ke.newhouse.agency.project.dao.mapper") public class MybatisPlusConfiguration { @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } }
在這些mapper路徑下都是我們定義的接口,@MapperScan的作用有哪些呢,我們先看下@MapperScan的定義
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(MapperScannerRegistrar.class) @Repeatable(MapperScans.class) public @interface MapperScan { }
在MapperScan的定義中我們目前只需要關(guān)注@Import(MapperScannerRegistrar.class),其含義是將MapperScannerRegistrar.class引入到spring容器中
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware { private ResourceLoader resourceLoader; /** * {@inheritDoc} */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AnnotationAttributes mapperScanAttrs = AnnotationAttributes .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName())); if (mapperScanAttrs != null) { registerBeanDefinitions(mapperScanAttrs, registry); } } void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry) { ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); // this check is needed in Spring 3.1 Optional.ofNullable(resourceLoader).ifPresent(scanner::setResourceLoader); 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<>(); 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())); scanner.registerFilters(); scanner.doScan(StringUtils.toStringArray(basePackages)); } }
可以看到MapperScannerRegistrar是繼承于ImportBeanDefinitionRegistrar接口,在spring中ImportBeanDefinitionRegistrar的也是創(chuàng)建bean的一種方式,它首先會(huì)定義好bean的一些基礎(chǔ)信息,如beanName, class等等,然后在spring啟動(dòng)的時(shí)候調(diào)用其registerBeanDefinitions方法,來生成和注冊(cè)bean。
我們直接看scanner.doScan(StringUtils.toStringArray(basePackages));這行的工作,最終會(huì)調(diào)用下面的方法 ClassPathMapperScanner
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) { GenericBeanDefinition definition; for (BeanDefinitionHolder holder : beanDefinitions) { definition = (GenericBeanDefinition) holder.getBeanDefinition(); //這里的beanClassName是mapper接口的接口名(第一個(gè)字母小寫) 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 definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59 //這行很重要,表示bean的實(shí)際class為MapperFactoryBean definition.setBeanClass(this.mapperFactoryBean.getClass()); definition.getPropertyValues().add("addToConfig", this.addToConfig); 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; } 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() + "'."); //將該bean設(shè)置成按類型自動(dòng)裝配,目的在該bean中如果有依賴其他bean,會(huì)按類型自動(dòng)注入到該bean中 definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); } } }
我們?cè)倏碝apperFactoryBean類
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; } /** * {@inheritDoc} */ @Override protected void checkDaoConfig() { super.checkDaoConfig(); notNull(this.mapperInterface, "Property 'mapperInterface' is required"); //父類中的屬性SqlSessionTemplate,該字段是在上面講的自動(dòng)裝配中注入的 Configuration configuration = getSqlSession().getConfiguration(); if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) { try { //將該mapper接口添加到MapperRegistry中去 configuration.addMapper(this.mapperInterface); } catch (Exception e) { logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e); throw new IllegalArgumentException(e); } finally { ErrorContext.instance().reset(); } } } /** * {@inheritDoc} */ @Override public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); } ····· ····· }
MapperFactoryBean的父類是SqlSessionDaoSupport,同時(shí)實(shí)現(xiàn)了接口FactoryBean
我們先分析SqlSessionDaoSupport。
使得MapperFactoryBean擁有了SqlSessionTemplate屬性,而同時(shí)SqlSessionDaoSupport繼承于DaoSupport,DaoSupport是InitializingBean的一個(gè)抽象類并實(shí)現(xiàn)了afterPropertiesSet方法,該方法使得在bean生成時(shí)執(zhí)行一些操作checkDaoConfig。在checkDaoConfig方法中會(huì)將該mapper接口添加到MapperRegistry中去,MapperRegistry可以看作是一個(gè)mapper的注冊(cè)中心
MapperRegistry
public class MapperRegistry { private final Configuration config; //保存mapper接口與代理工廠類的映射 private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>(); public MapperRegistry(Configuration config) { this.config = config; } @SuppressWarnings("unchecked") public <T> T getMapper(Class<T> type, SqlSession sqlSession) { final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); if (mapperProxyFactory == null) { throw new BindingException("Type " + type + " is not known to the MapperRegistry."); } try { return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } } public <T> boolean hasMapper(Class<T> type) { return knownMappers.containsKey(type); } public <T> void addMapper(Class<T> type) { if (type.isInterface()) { if (hasMapper(type)) { throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { //將mapper接口與代理工廠類對(duì)應(yīng)起來 knownMappers.put(type, new MapperProxyFactory<>(type)); // It's important that the type is added before the parser is run // otherwise the binding may automatically be attempted by the // mapper parser. If the type is already known, it won't try. MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } } /** * @since 3.2.2 */ public Collection<Class<?>> getMappers() { return Collections.unmodifiableCollection(knownMappers.keySet()); } /** * @since 3.2.2 */ public void addMappers(String packageName, Class<?> superType) { ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>(); resolverUtil.find(new ResolverUtil.IsA(superType), packageName); Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses(); for (Class<?> mapperClass : mapperSet) { addMapper(mapperClass); } } /** * @since 3.2.2 */ public void addMappers(String packageName) { addMappers(packageName, Object.class); } }
MapperProxyFactory是MapperProxy的工廠類,MapperProxy是Mapper的代理類
MapperProxy
public class MapperProxy<T> implements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L; private final SqlSession sqlSession; private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache; public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; this.methodCache = methodCache; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } else if (isDefaultMethod(method)) { return invokeDefaultMethod(proxy, method, args); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } //使用緩存來保存MapperMethod final MapperMethod mapperMethod = cachedMapperMethod(method); //最終會(huì)調(diào)用這里來執(zhí)行mapper中的方法 return mapperMethod.execute(sqlSession, args); } private MapperMethod cachedMapperMethod(Method method) { return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())); } private Object invokeDefaultMethod(Object proxy, Method method, Object[] args) throws Throwable { final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class .getDeclaredConstructor(Class.class, int.class); if (!constructor.isAccessible()) { constructor.setAccessible(true); } final Class<?> declaringClass = method.getDeclaringClass(); return constructor .newInstance(declaringClass, MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC) .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args); } }
我們?cè)诮又治鯩apperFactoryBean的另一個(gè)功能接口FactoryBean,實(shí)現(xiàn)FactoryBean這個(gè)接口使得MapperFactoryBean成為了一個(gè)工廠bean,它的功能是:從spring容器中獲取該工廠bean的具體實(shí)例是通過getObject方法
MapperFactoryBean
public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); } ···· public <T> T getMapper(Class<T> type) { return getConfiguration().getMapper(type, this); } ··· public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); } ··· public <T> T getMapper(Class<T> type, SqlSession sqlSession) { final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); if (mapperProxyFactory == null) { throw new BindingException("Type " + type + " is not known to the MapperRegistry."); } try { return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } } ··· public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }
可以看到最終從spring中獲取mapper的實(shí)例的時(shí)候,是獲取mapper的代理類
使用mybaits,其中mapper接口最終的實(shí)例是MapperProxy生成的代理類
通過ImportBeanDefinitionRegistrar這種方式來注入到spring容器中
到此,關(guān)于“Mybatis-spring自動(dòng)注入機(jī)制原理”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!
分享題目:Mybatis-spring自動(dòng)注入機(jī)制原理
鏈接分享:http://chinadenli.net/article40/jsijeo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供虛擬主機(jī)、動(dòng)態(tài)網(wǎng)站、自適應(yīng)網(wǎng)站、網(wǎng)站維護(hù)、網(wǎng)站內(nèi)鏈、網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)