public class TestMyBatis { public static void main(String[] args) { try { // 基本mybatis環境 // 1.定義mybatis_config文件地址 String resources = "mybatis_config.xml"; // 2.獲取InputStreamReaderIo流 Reader reader = Resources.getResourceAsReader(resources); // 3.獲取SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); // 4.獲取Session SqlSession sqlSession = sqlSessionFactory.openSession(); // 5.操做Mapper接口 UserMapper mapper = sqlSession.getMapper(UserMapper.class); UserEntity user = mapper.getUser(2); System.out.println(user.getName()); } catch (Exception e) { e.printStackTrace(); } } }
1.mybatis SqlSessionFactoryBuilder源碼分析 (建造者模式)java
2.MybatisMapper接口綁定原理(代理設計模式)sql
1.讀取resources獲取對應的Reader對象,進入getResourceAsReader(resources)源碼片斷
Reader reader = Resources.getResourceAsReader(resources);設計模式
/* *讀取resources獲取對應的Reader對象 */ public static Reader getResourceAsReader(String resource) throws IOException { Reader reader; //判斷編碼 if (charset == null) { //調用javaioAPI 讀取resources配置文件,獲取InputStreamReader reader = new InputStreamReader(getResourceAsStream(resource)); } else { reader = new InputStreamReader(getResourceAsStream(resource), charset); } return reader; }2.進入SqlSessionFactoryBuilder()去看看無參構造函數作了什麼事情,咱們發現無參構造函數沒有作什麼事情,那麼咱們就點到build(reader)去看這個方法具體如何實現的。
咱們發現SqlSessionFactoryBuilder , 經過XMLConfigBuilder解析mybatis配置文件內容。下面的代碼都是對配置文件的解析過程。
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);mybatis
/* *建立 SqlSessionFactory */ public class SqlSessionFactoryBuilder { //第一步進入這個方法,Reader讀取mybatis配置文件,傳入構造方法 public SqlSessionFactory build(Reader reader) { //調用重載的方法,咱們點進去 return build(reader, null, null); } ....省略部分源碼 //第二步進入這個重載方法 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { //經過XMLConfigBuilder解析mybatis配置文件,源碼分析 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); //源碼分析 return build(parser.parse()); ...省略部分源碼 }下面就接着看看 XMLConfigBuilder 源碼片斷:具體如何解析配置文件的,是經過xml解析器去解析配置文件app
/** * 使用xml解析器去解析mybatis 配置文件信息 */ public class XMLConfigBuilder extends BaseBuilder { private boolean parsed; //xml解析器 private XPathParser parser; .... //第一步進入到這個帶參數的構造方法中 public XMLConfigBuilder(Reader reader, String environment, Properties props) { //調用具體的執行邏輯方法,點進去,進入重載的方法 this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props); } .... //第二步執行具體邏輯 private XMLConfigBuilder(XPathParser parser, String environment, Properties props) { super(new Configuration()); ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); //在構造函數設置了parsed 爲fasle this.parsed = false; this.environment = environment; this.parser = parser; }xml解析器經過return build(parser.parse())這個方法去解析配置文件內容,咱們去看看parse()方法源碼ide
//外部調用此方法對mybatis配置文件進行解析 public Configuration parse() { //由於在構造函數設置了parsed 爲fasle,xml解析器只解析一次 if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } //只解析一次,Configuration配置文件是全局的,只能被解析一次 parsed = true; //源碼分析,從根節點configuration parseConfiguration(parser.evalNode("/configuration")); return configuration; }在上面這段代碼調用了:parseConfiguration(parser.evalNode("/configuration")),咱們點進源碼看看具體怎麼作的函數
//1.此方法就是解析configuration節點下的子節點 //2.由此也可看出,咱們在configuration下面能配置的節點爲如下10個節點 //3.首先要看的就是properties節點和environments節點 private void parseConfiguration(XNode root) { try { //1.解析properties元素,源碼分析 propertiesElement(root.evalNode("properties")); typeAliasesElement(root.evalNode("typeAliases")); pluginElement(root.evalNode("plugins")); objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); reflectionFactoryElement(root.evalNode("reflectionFactory")); settingsElement(root.evalNode("settings")); //2.解析environments元素,源碼分析 environmentsElement(root.evalNode("environments")); databaseIdProviderElement(root.evalNode("databaseIdProvider")); typeHandlerElement(root.evalNode("typeHandlers")); //3.這裏源碼分析 mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }下面我門來看看解析properties的具體方法, propertiesElement(root.evalNode("properties")),這裏點進去看源碼如何寫的源碼分析
/* * 解析properties具體方法 */ private void propertiesElement(XNode context) throws Exception { if (context != null) { //將子節點的 name 以及value屬性set進properties對象 //這兒能夠注意一下順序,xml配置優先, 外部指定properties配置其次 Properties defaults = context.getChildrenAsProperties(); //獲取properties節點上 resource屬性的值 String resource = context.getStringAttribute("resource"); //獲取properties節點上 url屬性的值, resource和url不能同時配置 String url = context.getStringAttribute("url"); if (resource != null && url != null) { throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other."); } //把解析出的properties文件set進Properties對象 if (resource != null) { defaults.putAll(Resources.getResourceAsProperties(resource)); } else if (url != null) { defaults.putAll(Resources.getUrlAsProperties(url)); } //將configuration對象中已配置的Properties屬性與剛剛解析的融合 //configuration這個對象會裝載所解析mybatis配置文件的全部節點元素,之後也會頻頻提到這個對象 //既然configuration對象用有一系列的get/set方法, 那是否就標誌着咱們可使用java代碼直接配置? //答案是確定的, 不過使用配置文件進行配置,優點不言而喻 Properties vars = configuration.getVariables(); if (vars != null) { defaults.putAll(vars); } //把裝有解析配置propertis對象set進解析器, 由於後面可能會用到 parser.setVariables(defaults); //set進configuration對象 configuration.setVariables(defaults); } }下面來看看解析envioments元素節點的方法,environmentsElement(root.evalNode("environments")),這裏點進去看源碼如何實現的測試
/* * 解析envioments元素節點的方法 */ private void environmentsElement(XNode context) throws Exception { if (context != null) { if (environment == null) { //解析environments節點的default屬性的值 //例如: <environments default="development"> environment = context.getStringAttribute("default"); } //遞歸解析environments子節點 for (XNode child : context.getChildren()) { //<environment id="development">, 只有enviroment節點有id屬性,那麼這個屬性有何做用? //environments 節點下能夠擁有多個 environment子節點 // //相似於這樣: <environments default="development"><environment id="development">... //</environment><environment id="test">...</environments> //意思就是咱們能夠對應多個環境,好比開發環境,測試環境等, 由environments的default屬性去選擇對應的 //enviroment String id = child.getStringAttribute("id"); //isSpecial就是根據由environments的default屬性去選擇對應的enviroment if (isSpecifiedEnvironment(id)) { //事務, mybatis有兩種:JDBC 和 MANAGED, 配置爲JDBC則直接使用JDBC的事務,配置爲MANAGED則是將事務託管 //給容器 TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); //enviroment節點下面就是dataSource節點了,解析dataSource節點(下面會貼出解析dataSource的具體方法) //這裏源碼分析 DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); DataSource dataSource = dsFactory.getDataSource(); Environment.Builder environmentBuilder = new Environment.Builder(id) .transactionFactory(txFactory) .dataSource(dataSource); //老規矩,會將dataSource設置進configuration對象 configuration.setEnvironment(environmentBuilder.build()); } } } }下面來看看解析datasource元素節點的方法,dataSourceElement(child.evalNode("dataSource")),這裏看源碼怎麼實現datasource解析ui
/* * 解析datasource元素節點的方法 */ private DataSourceFactory dataSourceElement(XNode context) throws Exception { if (context != null) { //dataSource的鏈接池 String type = context.getStringAttribute("type"); //子節點 name, value屬性set進一個properties對象 Properties props = context.getChildrenAsProperties(); //建立dataSourceFactory DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance(); factory.setProperties(props); return factory; } throw new BuilderException("Environment declaration requires a DataSourceFactory."); }3.SqlSessionFactoryBuilder源碼分析 (建造者模式)經過以上源碼,咱們就能看出,在mybatis的配置文件中:
1. configuration節點爲根節點。 2. 在configuration節點之下,咱們能夠配置10個子節點, 分別爲:properties、typeAliases、plugins、objectFactory、objectWrapperFactory、settings、environments、databaseIdProvider、typeHandlers、mappers。 3.解析配置文件完成了以後,都會裝配到configuration 4.Configuration做用:mybatis核心的配置文件內容 ,使用xml轉換bean