我们都知道类的调用是不能直接调用没有实现的接口的,所以需要通过代理的方式给接口生成对应的实现类。接下来再通过把代理类放到 Spring 的 FactoryBean 的实现中,最后再把这个 FactoryBean 实现类注册到 Spring 容器。那么现在你的代理类就已经被注册到 Spring 容器了,接下来就可以通过注解的方式注入到属性中。
按照这个实现方式,我们来操作一下,看看一个 Bean 的注册过程在代码中是如何实现的。
1. 定义接口
publicinterfaceIUserDao{StringqueryUserInfo();}
先定义一个类似 DAO 的接口,基本这样的接口在使用 MyBatis 时还是非常常见的。后面我们会对这个接口做代理和注册。
22:53:14.759[main] DEBUG o.s.c.e.PropertySourcesPropertyResolver-Could not find key 'spring.liveBeansView.mbeanDomain' in any property source
22:53:14.760[main] DEBUG o.s.b.f.s.DefaultListableBeanFactory-Returning cached instance of singleton bean 'userDao'22:53:14.796[main] INFO org.itstack.interview.test.ApiTest- 测试结果:你被代理了 queryUserInfo
Process finished withexit code 0
从测试结果可以看到,我们已经可以通过注入到Spring的代理Bean对象,实现我们的预期结果。
其实这个过程也是很多框架中用到的方式,尤其是在一些中间件开发,类似的 ORM 框架都需要使用到。
三、手写个Mybatis
扩展上一篇源码分析工程;itstack-demo-mybatis,增加 like 包,模仿 Mybatis 工程。完整规程下载 https://github.com/fuzhengwei/CodeGuide/wiki
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapper PUBLIC "-////DTD Mapper 3.0//EN""http:///dtd/mybatis-3-mapper.dtd"><mapper namespace="org.itstack.demo.dao.IUserDao"><select id="queryUserInfoById" parameterType="java.lang.Long" resultType="org.itstack.demo.po.User">SELECT id, name, age, createTime, updateTime
FROM user
where id = #{id}</select><select id="queryUserList" parameterType="org.itstack.demo.po.User" resultType="org.itstack.demo.po.User">SELECT id, name, age, createTime, updateTime
FROM user
where age = #{age}</select></mapper>
在前面的 xml 内容中可以看到,我们需要解析出数据库连接池信息 datasource,还有数据库语句映射关系 mappers
SqlSessionFactoryBuilder.dataSource() & 解析出数据源
privateMap<String,String>dataSource(List<Element> list){Map<String,String> dataSource =newHashMap<>(4);Element element = list.get(0);List content = element.content();for(Object o : content){Element e =(Element) o;String name = e.attributeValue("name");String value = e.attributeValue("value");
dataSource.put(name, value);}return dataSource;}
<selectid="queryUserInfoById"parameterType="java.lang.Long"resultType="org.itstack.demo.po.User">
SELECT id, name, age, createTime, updateTime
FROM user
where id = #{id}
</select>
获取 sql 语句后交给 jdbc 的 PreparedStatement 类进行执行
这里还需要设置入参,我们将入参设置进行抽取,如下;
privatevoidbuildParameter(PreparedStatement preparedStatement,Object parameter,Map<Integer,String> parameterMap)throwsSQLException,IllegalAccessException{int size = parameterMap.size();// 单个参数if(parameter instanceofLong){for(int i =1; i <= size; i++){
preparedStatement.setLong(i,Long.parseLong(parameter.toString()));}return;}if(parameter instanceofInteger){for(int i =1; i <= size; i++){
preparedStatement.setInt(i,Integer.parseInt(parameter.toString()));}return;}if(parameter instanceofString){for(int i =1; i <= size; i++){
preparedStatement.setString(i, parameter.toString());}return;}Map<String,Object> fieldMap =newHashMap<>();// 对象参数Field[] declaredFields = parameter.getClass().getDeclaredFields();for(Field field : declaredFields){String name = field.getName();
field.setAccessible(true);Object obj = field.get(parameter);
field.setAccessible(false);
fieldMap.put(name, obj);}for(int i =1; i <= size; i++){String parameterDefine = parameterMap.get(i);Object obj = fieldMap.get(parameterDefine);if(obj instanceofShort){
preparedStatement.setShort(i,Short.parseShort(obj.toString()));continue;}if(obj instanceofInteger){
preparedStatement.setInt(i,Integer.parseInt(obj.toString()));continue;}if(obj instanceofLong){
preparedStatement.setLong(i,Long.parseLong(obj.toString()));continue;}if(obj instanceofString){
preparedStatement.setString(i, obj.toString());continue;}if(obj instanceofDate){
preparedStatement.setDate(i,(java.sql.Date) obj);}}}
publicclassXMLMapperEntityResolverimplementsEntityResolver{privatestaticfinalString IBATIS_CONFIG_SYSTEM ="ibatis-3-config.dtd";privatestaticfinalString IBATIS_MAPPER_SYSTEM ="ibatis-3-mapper.dtd";privatestaticfinalString MYBATIS_CONFIG_SYSTEM ="mybatis-3-config.dtd";privatestaticfinalString MYBATIS_MAPPER_SYSTEM ="mybatis-3-mapper.dtd";privatestaticfinalString MYBATIS_CONFIG_DTD ="org/apache/ibatis/builder/xml/mybatis-3-config.dtd";privatestaticfinalString MYBATIS_MAPPER_DTD ="org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd";/*
* Converts a public DTD into a local one
*
* @param publicId The public id that is what comes after "PUBLIC"
* @param systemId The system id that is what comes after the public id.
* @return The InputSource for the DTD
*
* @throws org.xml.sax.SAXException If anything goes wrong
*/@OverridepublicInputSourceresolveEntity(String publicId,String systemId)throwsSAXException{try{if(systemId !=null){String lowerCaseSystemId = systemId.toLowerCase(Locale.ENGLISH);if(lowerCaseSystemId.contains(MYBATIS_CONFIG_SYSTEM)|| lowerCaseSystemId.contains(IBATIS_CONFIG_SYSTEM)){returngetInputSource(MYBATIS_CONFIG_DTD, publicId, systemId);}elseif(lowerCaseSystemId.contains(MYBATIS_MAPPER_SYSTEM)|| lowerCaseSystemId.contains(IBATIS_MAPPER_SYSTEM)){returngetInputSource(MYBATIS_MAPPER_DTD, publicId, systemId);}}returnnull;}catch(Exception e){thrownewSAXException(e.toString());}}privateInputSourcegetInputSource(String path,String publicId,String systemId){InputSource source =null;if(path !=null){try{InputStream in =Resources.getResourceAsStream(path);
source =newInputSource(in);
source.setPublicId(publicId);
source.setSystemId(systemId);}catch(IOException e){// ignore, null is ok}}return source;}}
publicclassXMLConfigBuilderextendsBaseBuilder{publicConfigurationparse(){if(parsed){thrownewBuilderException("Each XMLConfigBuilder can only be used once.");}
parsed =true;parseConfiguration(parser.evalNode("/configuration"));return configuration;}}
3. 配置文件解析
这一部分是整个XML文件解析和装载的核心内容,其中包括;
属性解析propertiesElement
加载settings节点settingsAsProperties
载自定义VFS loadCustomVfs
解析类型别名typeAliasesElement
加载插件pluginElement
加载对象工厂objectFactoryElement
创建对象包装器工厂objectWrapperFactoryElement
加载反射工厂reflectorFactoryElement
元素设置settingsElement
加载环境配置environmentsElement
数据库厂商标识加载databaseIdProviderElement
加载类型处理器typeHandlerElement
(核心)加载mapper文件mapperElement
parseConfiguration(parser.evalNode("/configuration"));privatevoidparseConfiguration(XNode root){try{//issue #117 read properties first//属性解析propertiesElementpropertiesElement(root.evalNode("properties"));//加载settings节点settingsAsPropertiesProperties settings =settingsAsProperties(root.evalNode("settings"));//加载自定义VFS loadCustomVfsloadCustomVfs(settings);//解析类型别名typeAliasesElementtypeAliasesElement(root.evalNode("typeAliases"));//加载插件pluginElementpluginElement(root.evalNode("plugins"));//加载对象工厂objectFactoryElementobjectFactoryElement(root.evalNode("objectFactory"));//创建对象包装器工厂objectWrapperFactoryElementobjectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));//加载反射工厂reflectorFactoryElementreflectorFactoryElement(root.evalNode("reflectorFactory"));//元素设置settingsElement(settings);// read it after objectFactory and objectWrapperFactory issue #631//加载环境配置environmentsElementenvironmentsElement(root.evalNode("environments"));//数据库厂商标识加载databaseIdProviderElementdatabaseIdProviderElement(root.evalNode("databaseIdProvider"));//加载类型处理器typeHandlerElementtypeHandlerElement(root.evalNode("typeHandlers"));//加载mapper文件mapperElementmapperElement(root.evalNode("mappers"));}catch(Exception e){thrownewBuilderException("Error parsing SQL Mapper Configuration. Cause: "+ e, e);}}
public<T>voidaddMapper(Class<T> type){// 对于mybatis mapper接口文件,必须是interface,不能是classif(type.isInterface()){if(hasMapper(type)){thrownewBindingException("Type "+ type +" is already known to the MapperRegistry.");}boolean loadCompleted =false;try{// 为mapper接口创建一个MapperProxyFactory代理
knownMappers.put(type,newMapperProxyFactory<T>(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 =newMapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted =true;}finally{if(!loadCompleted){
knownMappers.remove(type);}}}}
@OverridepublicSet<BeanDefinitionHolder>doScan(String... basePackages){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;}
优先调用父类的super.doScan(basePackages);进行注册Bean信息
ClassPathBeanDefinitionScanner.java & 部分截取
protectedSet<BeanDefinitionHolder>doScan(String... basePackages){Assert.notEmpty(basePackages,"At least one base package must be specified");Set<BeanDefinitionHolder> beanDefinitions =newLinkedHashSet<BeanDefinitionHolder>();for(String basePackage : basePackages){Set<BeanDefinition> candidates =findCandidateComponents(basePackage);for(BeanDefinition candidate : candidates){ScopeMetadata scopeMetadata =this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());String beanName =this.beanNameGenerator.generateBeanName(candidate,this.registry);if(candidate instanceofAbstractBeanDefinition){postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);}if(candidate instanceofAnnotatedBeanDefinition){AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate)}if(checkCandidate(beanName, candidate)){BeanDefinitionHolder definitionHolder =newBeanDefinitionHolder(candidate, beanName);
definitionHolder =AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder,this.regi
beanDefinitions.add(definitionHolder);registerBeanDefinition(definitionHolder,this.registry);}}}return beanDefinitions;}
**processBeanDefinitions(beanDefinitions);**privatevoidprocessBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions){GenericBeanDefinition definition;for(BeanDefinitionHolder holder : beanDefinitions){
definition =(GenericBeanDefinition) holder.getBeanDefinition();if(logger.isDebugEnabled()){
logger.debug("Creating MapperFactoryBean with name '"+ holder.getBeanName()+"' and '"+ definition.getBeanClassName()+"' mapperInterface");}// the mapper interface is the original class of the bean// but, the actual class of the bean is MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());// issue #59
definition.setBeanClass(this.mapperFactoryBean.getClass());
definition.getPropertyValues().add("addToConfig",this.addToConfig);boolean explicitFactoryUsed =false;if(StringUtils.hasText(this.sqlSessionFactoryBeanName)){
definition.getPropertyValues().add("sqlSessionFactory",newRuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed =true;}elseif(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",newRuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed =true;}elseif(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){if(logger.isDebugEnabled()){
logger.debug("Enabling autowire by type for MapperFactoryBean with name '"+ holder.getBeanName()+"'.");}
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);}}}
public<T>TgetMapper(Class<T> type,SqlSession sqlSession){finalMapperProxyFactory<T> mapperProxyFactory =(MapperProxyFactory<T>) knownMappers.get(type);if(mapperProxyFactory ==null){thrownewBindingException("Type "+ type +" is not known to the MapperRegistry.");}try{return mapperProxyFactory.newInstance(sqlSession);}catch(Exception e){thrownewBindingException("Error getting mapper instance. Cause: "+ e, e);}}
publicObjectexecute(SqlSession sqlSession,Object[] args){Object result;switch(command.getType()){case INSERT:{Object param = method.convertArgsToSqlCommandParam(args);
result =rowCountResult(sqlSession.insert(command.getName(), param));break;}case UPDATE:{Object param = method.convertArgsToSqlCommandParam(args);
result =rowCountResult(sqlSession.update(command.getName(), param));break;}case DELETE:{Object param = method.convertArgsToSqlCommandParam(args);
result =rowCountResult(sqlSession.delete(command.getName(), param));break;}case SELECT:if(method.returnsVoid()&& method.hasResultHandler()){executeWithResultHandler(sqlSession, args);
result =null;}elseif(method.returnsMany()){
result =executeForMany(sqlSession, args);}elseif(method.returnsMap()){
result =executeForMap(sqlSession, args);}elseif(method.returnsCursor()){
result =executeForCursor(sqlSession, args);}else{Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);}break;case FLUSH:
result = sqlSession.flushStatements();break;default:thrownewBindingException("Unknown execution method for: "+ command.getName());}if(result ==null&& method.getReturnType().isPrimitive()&&!method.returnsVoid()){thrownewBindingException("Mapper method '"+ command.getName()+" attempted to return null from a method with a primitive return type ("+ method.getReturnType()+").");}return result;}
publicvoidafterPropertiesSet()throwsException{notNull(dataSource,"Property 'dataSource' is required");notNull(sqlSessionFactoryBuilder,"Property 'sqlSessionFactoryBuilder' is required");state((configuration ==null&& configLocation ==null)||!(configuration !=null&& configLocation !=null),"Property 'configuration' and 'configLocation' can not specified with together");this.sqlSessionFactory =buildSqlSessionFactory();}
519行:XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(…) 解析XMLMapperBuilder
521行:xmlMapperBuilder.parse() 执行解析,具体如下;
XMLMapperBuilder.java & 部分截取
publicclassXMLMapperBuilderextendsBaseBuilder{privatefinalXPathParser parser;privatefinalMapperBuilderAssistant builderAssistant;privatefinalMap<String,XNode> sqlFragments;privatefinalString resource;privatevoidbindMapperForNamespace(){String namespace = builderAssistant.getCurrentNamespace();if(namespace !=null){Class<?> boundType =null;try{
boundType =Resources.classForName(namespace);}catch(ClassNotFoundException e){//ignore, bound type is not required}if(boundType !=null){if(!configuration.hasMapper(boundType)){// Spring may not know the real resource name so we set a flag// to prevent loading again this resource from the mapper interface// look at MapperAnnotationBuilder#loadXmlResource
configuration.addLoadedResource("namespace:"+ namespace);
configuration.addMapper(boundType);}}}}}
publicclassMapperRegistry{public<T>voidaddMapper(Class<T> type){if(type.isInterface()){if(hasMapper(type)){thrownewBindingException("Type "+ type +" is already known to the MapperRegistry.");}boolean loadCompleted =false;try{
knownMappers.put(type,newMapperProxyFactory<T>(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 =newMapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted =true;}finally{if(!loadCompleted){
knownMappers.remove(type);}}}}}
67行:创建代理工程knownMappers.put(type, new MapperProxyFactory(type));