配置项解析 在mybatis中,一般我们会定义一个mapper-config.xml文件,来配置数据库连接的相关信息,以及我们的mapperxml文件存放目录。在本章,我们会读取这些文件,将其配置信息进行解析。 因为涉及到xml的解析,因此,我们先添加dom4j的依赖,以方便后续解析xml
1 2 3 4 5 <dependency> <groupId>org.dom4j</groupId> <artifactId>dom4j</artifactId> <version>2.1 .3 </version> </dependency>
mybatis-config.xml的内容如下,对于每一个环境,都有对应的数据源信息,此外,mappers标签存储的是mapper.xml文件的位置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?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> <environments default="development" > <environment id="development" > <transactionManager type="JDBC" /> <dataSource type="POOLED" > <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://192.168.102.128:3306/test" /> <property name="username" value="root" /> <property name="password" value="123456" /> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/UserMapper.xml" /> </mappers> </configuration>
首先,我们添加MybatisDataSource,用来记录数据源信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package com.yang.mybatis.config; import java.io.Serializable; public class MybatisDataSource implements Serializable { private String driver; private String url; private String username; private String password; public String getDriver() { return driver; } public String getUrl() { return url; } public String getUsername() { return username; } public String getPassword() { return password; } public void setDriver(String driver) { this.driver = driver; } public void setUrl(String url) { this.url = url; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } }
然后添加一个MybatisEnvironment,用来存储环境信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package com.yang.mybatis.config; import java.io.Serializable; public class MybatisEnvironment implements Serializable { private String id; private MybatisDataSource mybatisDataSource; public String getId() { return id; } public void setId(String id) { this.id = id; } public MybatisDataSource getMybatisDataSource() { return mybatisDataSource; } public void setMybatisDataSource(MybatisDataSource mybatisDataSource) { this.mybatisDataSource = mybatisDataSource; } }
然后是MybatisConfiguration,我们读取mybatis-config.xml配置文件后,配置信息就存储在这个类中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package com.yang.mybatis.config; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class MybatisConfiguration implements Serializable { private Map<String, MybatisEnvironment> id2EnvironmentMap = new HashMap<>(); private MybatisEnvironment defaultMybatisEnvironment; private List<String> mapperPaths = new ArrayList<>(); public void addEnvironment(String id, MybatisEnvironment mybatisEnvironment) { this.id2EnvironmentMap.put(id, mybatisEnvironment); } public MybatisEnvironment getEnvironment(String id) { return id2EnvironmentMap.get(id); } public MybatisEnvironment getDefaultMybatisEnvironment() { return defaultMybatisEnvironment; } public void setDefaultMybatisEnvironment(MybatisEnvironment defaultMybatisEnvironment) { this.defaultMybatisEnvironment = defaultMybatisEnvironment; } public void addMapperPath(String mapperPath) { this.mapperPaths.add(mapperPath); } public List<String> getMapperPaths() { return this.mapperPaths; } public List<MybatisEnvironment> getEnvironments() { return new ArrayList<>(id2EnvironmentMap.values()); } }
对于mybatis-config.xml的解析,我们定义一个IMybatisConfigParser接口
1 2 3 4 5 6 7 8 package com.yang.mybatis.config.parser; import com.yang.mybatis.config.MybatisConfiguration; public interface IMybatisConfigurationParser { MybatisConfiguration parser(String path) ;}
然后定义其实现类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 package com.yang.mybatis.config.parser; import com.yang.mybatis.config.MybatisConfiguration; import com.yang.mybatis.config.MybatisDataSource; import com.yang.mybatis.config.MybatisEnvironment; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; import java.io.InputStream; import java.util.HashMap; import java.util.List; import java.util.Map; public class XmlMybatisConfigurationParser implements IMybatisConfigurationParser { @Override public MybatisConfiguration parser(String path) { MybatisConfiguration mybatisConfiguration = new MybatisConfiguration(); try { InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(path); SAXReader saxReader = new SAXReader(); Document document = saxReader.read(inputStream); Element root = document.getRootElement(); parseEnvironments(mybatisConfiguration, root.element("environments" )); parseMappers(mybatisConfiguration, root.element("mappers" )); } catch (Exception e) { e.printStackTrace(); } return mybatisConfiguration; } private void parseMappers(MybatisConfiguration mybatisConfiguration, Element mappers) { List<Element> mapperList = mappers.elements("mapper" ); for (Element mapper : mapperList) { String resource = mapper.attributeValue("resource" ); mybatisConfiguration.addMapperPath(resource); } } private void parseEnvironments(MybatisConfiguration mybatisConfiguration, Element environments) { String defaultId = environments.attributeValue("default" ); for (Element element : environments.elements()) { String id = element.attributeValue("id" ); Element dataSource = element.element("dataSource" ); List<Element> properties = dataSource.elements("property" ); Map<String, String> propertyMap = new HashMap<>(); for (Element property : properties) { propertyMap.put(property.attributeValue("name" ), property.attributeValue("value" )); } String driver = propertyMap.get("driver" ); String url = propertyMap.get("url" ); String username = propertyMap.get("username" ); String password = propertyMap.get("password" ); MybatisDataSource mybatisDataSource = new MybatisDataSource(); mybatisDataSource.setDriver(driver); mybatisDataSource.setUrl(url); mybatisDataSource.setUsername(username); mybatisDataSource.setPassword(password); MybatisEnvironment mybatisEnvironment = new MybatisEnvironment(); mybatisEnvironment.setId(id); mybatisEnvironment.setMybatisDataSource(mybatisDataSource); mybatisConfiguration.addEnvironment(id, mybatisEnvironment); if (id.equals(defaultId)) { mybatisConfiguration.setDefaultMybatisEnvironment(mybatisEnvironment); } } } }
然后添加测试代码进行测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 IMybatisConfigurationParser mybatisConfigurationParser = new XmlMybatisConfigurationParser(); MybatisConfiguration mybatisConfiguration = mybatisConfigurationParser.parser("mybatis-config.xml" ); System.out.println("mapperPaths==========" ); for (String mapperPath : mybatisConfiguration.getMapperPaths()) { System.out.println(mapperPath); } System.out.println("environments========" ); for (MybatisEnvironment environment : mybatisConfiguration.getEnvironments()) { MybatisDataSource mybatisDataSource = environment.getMybatisDataSource(); System.out.println("id:" + environment.getId() + ",driver:" + mybatisDataSource.getDriver() + ",url:" + mybatisDataSource.getUrl() + ",username:" + mybatisDataSource.getUsername() + ",password:" + mybatisDataSource.getPassword()); } System.out.println("defaultEnvironment=======" ); MybatisEnvironment defaultMybatisEnvironment = mybatisConfiguration.getDefaultMybatisEnvironment(); MybatisDataSource mybatisDataSource = defaultMybatisEnvironment.getMybatisDataSource(); System.out.println("id:" + defaultMybatisEnvironment.getId() + ",driver:" + mybatisDataSource.getDriver() + ",url:" + mybatisDataSource.getUrl() + ",username:" + mybatisDataSource.getUsername() + ",password:" + mybatisDataSource.getPassword());
结果如下:
mapperXml的解析 首先添加一个MybatisSqlStatement
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package com.yang.mybatis.config; import java.io.Serializable; public class MybatisSqlStatement implements Serializable { private String namespace; private String id; private String sql; public String getNamespace() { return namespace; } public void setNamespace(String namespace) { this.namespace = namespace; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getSql() { return sql; } public void setSql(String sql) { this.sql = sql; } }
添加解析mapper文件的接口
1 2 3 4 5 6 7 8 9 10 package com.yang.mybatis.config.parser; import com.yang.mybatis.config.MybatisSqlStatement; import java.util.List; public interface IMybatisMapperParser { List<MybatisSqlStatement> parseMapper(String path);}
具体实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 package com.yang.mybatis.config.parser; import com.yang.mybatis.config.MybatisSqlStatement; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import java.io.InputStream; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class XmlMybatisMapperParser implements IMybatisMapperParser { private final static Set<String> tagSet = new HashSet<>(); static { tagSet.add("select" ); tagSet.add("insert" ); tagSet.add("update" ); tagSet.add("delete" ); tagSet.add("SELECT" ); tagSet.add("INSERT" ); tagSet.add("UPDATE" ); tagSet.add("DELETE" ); } @Override public List<MybatisSqlStatement> parseMapper(String path) { List<MybatisSqlStatement> mybatisSqlStatements = new ArrayList<>(); try { InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(path); SAXReader saxReader = new SAXReader(); Document document = saxReader.read(inputStream); Element root = document.getRootElement(); for (String tag : tagSet) { List<Element> elements = root.elements(tag); parseStatement(mybatisSqlStatements, elements, root); } } catch (DocumentException e) { e.printStackTrace(); } return mybatisSqlStatements; } private void parseStatement(List<MybatisSqlStatement> mybatisSqlStatements, List<Element> elements, Element root) { if (elements == null || elements.isEmpty()) { return; } String namespace = root.attributeValue("namespace" ); for (Element element : elements) { String id = element.attributeValue("id" ); String sql = element.getText().trim(); MybatisSqlStatement mybatisSqlStatement = new MybatisSqlStatement(); mybatisSqlStatement.setNamespace(namespace); mybatisSqlStatement.setId(id); mybatisSqlStatement.setSql(sql); mybatisSqlStatements.add(mybatisSqlStatement); } } }
然后,我们创建一个UserMapper.xml,用于测试,该文件内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.yang.mybatis.test.IUserMapper" > <select id="queryUserName" > select user_name from user where id = #{ id} </select> <select id="queryUserAge" > select age from user where id = #{ id} </select> </mapper>
添加测试代码,进行测试:
1 2 3 4 5 6 7 8 9 10 public static void main(String[ ] args) { IMybatisMapperParser mybatisMapperParser = new XmlMybatisMapperParser(); List<MybatisSqlStatement> mybatisSqlStatements = mybatisMapperParser.parseMapper("mapper/UserMapper.xml" ); for (MybatisSqlStatement mybatisSqlStatement : mybatisSqlStatements) { System.out.println("=================" ); System.out.println("namespace:" + mybatisSqlStatement.getNamespace()); System.out.println("id:" + mybatisSqlStatement.getId()); System.out.println("sql:" + mybatisSqlStatement.getSql()); } }
结果如下:
MapperProxyFactory注册Mapper 这里再修改一些MybatisConfiguration,因为mapperXML里的内容,其实我认为也属于配置项,因此收敛到MybatisConfiguration,方便后续统一管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 package com.yang.mybatis.config; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class MybatisConfiguration implements Serializable { private Map<String, MybatisEnvironment> id2EnvironmentMap = new HashMap<>(); private MybatisEnvironment defaultMybatisEnvironment; private List<String> mapperPaths = new ArrayList<>(); private Map<String, List<MybatisSqlStatement>> mapper2SqlStatementsMap = new HashMap<>(); public void putMybatisSqlStatementList(String mapperName, List<MybatisSqlStatement> mybatisSqlStatementList) { if (mapper2SqlStatementsMap.containsKey(mapperName)) { mapper2SqlStatementsMap.get(mapperName).addAll(mybatisSqlStatementList); } else { mapper2SqlStatementsMap.put(mapperName, mybatisSqlStatementList); } } public Map<String, List<MybatisSqlStatement>> getMapper2SqlStatementsMap() { return this.mapper2SqlStatementsMap; } public void addEnvironment(String id, MybatisEnvironment mybatisEnvironment) { this.id2EnvironmentMap.put(id, mybatisEnvironment); } public MybatisEnvironment getEnvironment(String id) { return id2EnvironmentMap.get(id); } public MybatisEnvironment getDefaultMybatisEnvironment() { return defaultMybatisEnvironment; } public void setDefaultMybatisEnvironment(MybatisEnvironment defaultMybatisEnvironment) { this.defaultMybatisEnvironment = defaultMybatisEnvironment; } public void addMapperPath(String mapperPath) { this.mapperPaths.add(mapperPath); } public List<String> getMapperPaths() { return this.mapperPaths; } public List<MybatisEnvironment> getEnvironments() { return new ArrayList<>(id2EnvironmentMap.values()); } }
然后修改MapperProxy,接收MybatisSqlStatement数组,在执行的时候,根据执行方法,找到对应的MybatisSqlStatement,获取mapper Xml对应的sql语句,并打印出来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com.yang.mybatis.mapper; import com.yang.mybatis.config.MybatisSqlStatement; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; import java.util.Map; public class MapperProxy<T> implements InvocationHandler { private Map<String, MybatisSqlStatement> sqlSessionMap = new HashMap<>(); private Class<T> mapperInterface; public MapperProxy(Class<T> mapperInterface, List<MybatisSqlStatement> mybatisSqlStatementList) { this.mapperInterface = mapperInterface; for (MybatisSqlStatement mybatisSqlStatement : mybatisSqlStatementList) { String mapperName = mybatisSqlStatement.getNamespace(); String id = mybatisSqlStatement.getId(); String key = mapperName + "." + id; sqlSessionMap.put(key, mybatisSqlStatement); } } @Override public Object invoke(Object proxy, Method method, Object[ ] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } String key = this.mapperInterface.getName() + "." + method.getName(); return "你的被代理了!" + sqlSessionMap.get(key).getSql(); } }
接着修改MybatisProxyFactory,通过配置信息,加载对应的mapper.xml文件并进行解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com.yang.mybatis.mapper; import com.yang.mybatis.config.MybatisConfiguration; import com.yang.mybatis.config.MybatisSqlStatement; import java.lang.reflect.Proxy; import java.util.HashMap; import java.util.List; import java.util.Map; public class MapperProxyFactory { private Map<Class, MapperProxy> mapperProxyMap = new HashMap<>(); public MapperProxyFactory(MybatisConfiguration mybatisConfiguration) { Map<String, List<MybatisSqlStatement>> mapperName2SqlStatementsMap = mybatisConfiguration.getMapper2SqlStatementsMap(); mapperName2SqlStatementsMap.forEach((mapperName, sqlStatementList) -> { try { Class<?> type = Class.forName(mapperName); mapperProxyMap.put(type, new MapperProxy(type, sqlStatementList)); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } ); } public Object newInstance(Class mapperType) { MapperProxy mapperProxy = mapperProxyMap.get(mapperType); return Proxy.newProxyInstance(mapperType.getClassLoader(), new Class[ ] { mapperType} , mapperProxy); } }
添加测试方法,进行测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public static void main(String[ ] args) { IMybatisConfigurationParser iMybatisConfigurationParser = new XmlMybatisConfigurationParser(); MybatisConfiguration mybatisConfiguration = iMybatisConfigurationParser.parser("mybatis-config.xml" ); IMybatisMapperParser iMybatisMapperParser = new XmlMybatisMapperParser(); List<String> mapperPaths = mybatisConfiguration.getMapperPaths(); for (String mapperPath : mapperPaths) { List<MybatisSqlStatement> mybatisSqlStatements = iMybatisMapperParser.parseMapper(mapperPath); Map<String, List<MybatisSqlStatement>> mapperNameGroupMap = mybatisSqlStatements.stream() .collect(Collectors.groupingBy(MybatisSqlStatement: : getNamespace)); for (Map.Entry<String, List<MybatisSqlStatement>> entry : mapperNameGroupMap.entrySet()) { String mapperName = entry.getKey(); List<MybatisSqlStatement> sqlSessionList = entry.getValue(); mybatisConfiguration.putMybatisSqlStatementList(mapperName, sqlSessionList); } } MapperProxyFactory mapperProxyFactory = new MapperProxyFactory(mybatisConfiguration); IUserMapper userMapper = (IUserMapper) mapperProxyFactory.newInstance(IUserMapper.class); System.out.println(userMapper.queryUserName(1 )); }
测试结果如下:
参考文章 https://zhuanlan.zhihu.com/p/338300626