俗話說,本身寫的代碼,6個月後也是別人的代碼……複習!複習!複習!涉及的知識點總結以下:java
開源框架的學習思路(我的經驗,歡迎提出意見)git
框架是爲了解決開發中遇到的一個個問題而誕生的,程序員是爲了解決問題而學習框架的,這纔是正確的學習之道!一個框架的好與壞徹底取決於其對問題解決程度和解決方式。我的的學習過程:程序員
1 // 經過new一個Configuration實例,而後用該實例去調用configure返回一個配置實例 2 Configuration configuration = new Configuration().configure(); 3 // 經過 配置實例的buildSessionFactory方法 生成一個 sessionFactory 對象 4 // buildSessionFactory方法會默認的去尋找配置文件hibernate.cfg.xml並解析xml文件 5 // 解析完畢生成sessionFactory,負責鏈接數據庫 6 SessionFactory sessionFactory = configuration.buildSessionFactory();
2. 生成的SessionFactory,等因而能夠獲取數據庫的鏈接(能建立session),從而能夠操做數據庫github
3. 由於核心配置Hibernate.cfg.xml裏引入了實體關係映射配置文件,故該文件也會自動被解析——加載對象-關係映射文件:vo類.hbm.xmlweb
四、而後是建立session對象,經過SessionFactory建立session。session能夠操做數據庫sql
// 經過 sessionFactory 得到一個數據庫鏈接 session,能夠操做數據庫 Session session = sessionFactory.openSession();
5. 開啓事務,也是經過session開啓數據庫
// 把操做封裝到數據庫的事務,則須要開啓一個事務 Transaction transaction = session.beginTransaction();
6. 調用session API,CRUD 對象設計模式
// 通常把對實體類和數據庫的操做,放到try-catch-finally塊 try { User user = new User(); user.setUserId(22); user.setUsername("dashuai"); user.setPassword("123456"); // 把user對象插入到數據庫 session.save(user); // 提交操做事務 transaction.commit(); LOG.info("transaction.commit(); ok"); } catch (Exception e) { // 提交事務失敗,必需要回滾 transaction.rollback(); // 打印日誌 LOG.error("save user error......", e); } finally { // 不能丟這一步,要釋放資源 if (session != null) { session.close(); LOG.info("session.close(); ok"); } }
7. 根據Dialect(以前在覈心配置文件配置的數據庫方言)生成和底層數據庫平臺相關的sql代碼數組
Hibernate實現原理中使用的技術有什麼?緩存
針對主流的XML文件配置方式,Hibernate實現原理中使用的關鍵技術主要有兩個。一是對XML文檔的解析——使用DOM(文檔對象模型)/SAX解析,Hibernate使用了常見的開源解析工具——dom4j(使用Java編寫,很流行),二是Java的反射技術,好比我能夠經過一個Java類的對象,經過反射機制來獲取這個對象的類的屬性,方法……簡單說,就相似我本身照鏡子,經過鏡子,我能夠看清楚我本身身體的各個部位。
固然了,還有基於註解的方式,那麼就還要使用Java的註解技術,本質上大同小異,熟能生巧。
Java反射技術淺析
大白話就是:Java反射機制可讓程序員在程序的運行期(Runtime)檢查類,接口,變量以及方法的信息,而檢查Java類的信息每每是在使用Java反射機制的時候所作的第一件事情,經過獲取類的信息能夠獲取如下相關的內容:Class對象,類名,修飾符,包信息,父類,實現的接口,構造器,方法,變量,註解……除了這些內容,還有不少的信息能夠經過反射機制得到(查閱API便可)。進一步反射還可讓程序員在運行期實例化對象,調用類的方法,經過調用get/set方法獲取變量的值等,因此,Java的反射機制功能很是強大並且很是實用。舉個例子,我能夠用反射機制把Java對象映射到數據庫表(Hibernate的實現機制之一),或者把腳本中的一段語句在運行期映射到相應的對象調用方法上,就如解析配置腳本時所作的那樣。
Java反射機制的原理
這涉及到了Java的類加載機制和原理,稍後會專題總結。這裏簡單說下,在說原理以前,必須先知道Java中通常常常用Class.forName(classname)來反射類。在以前的幾篇學習JVM總結隨筆中也有部分說到:JVM裝載某類時,類裝載器會定位相應的class文件,而後將其讀入到虛擬機中,並提取class中的類型信息,而Java中類的信息通常咱們認爲是存儲到JVM的方法區中了。
Java反射機制中涉及的類:
看個demo,新建一個類:dashuai.generics.Dog。該類作爲咱們的實驗類,經過反射機制建立該類的對象,並經過反射機制調用該類中的speak方法。
1 public class Dog{ 2 public void speak(String str) { 3 System.out.println("Dog speak! 汪汪" + str); 4 } 5 }
在main方法裏經過反射機制建立Dog類的對象,並調用其方法
public class Main { public static void main(String[] args) { Object obj = null; try { Class clazz = Class.forName("dashuai.generics.Dog"); obj = clazz.newInstance(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } Dog dog = (Dog) obj; dog.speak(」Hi」); } }
上面代碼中,dog對象是經過Class類的forName方法建立的,再調用該對象的speak方法,在控制檯打印一行字符串。註釋上面代碼最後一行:dog.speak(),我使用反射來調用dog對象的speak()方法。該類完整代碼以下:
1 public class Main { 2 public static void main(String[] args) { 3 Object obj = null; 4 5 try { 6 Class clazz = Class.forName("dashuai.generics.Dog"); 7 obj = clazz.newInstance(); 8 } catch (ClassNotFoundException e) { 9 e.printStackTrace(); 10 } catch (InstantiationException e) { 11 e.printStackTrace(); 12 } catch (IllegalAccessException e) { 13 e.printStackTrace(); 14 } 15 16 Dog dog = (Dog) obj; 17 Class<?>[] parameterTypes = new Class<?>[1]; 18 parameterTypes[0] = String.class; 19 20 try { 21 Method method = dog.getClass().getDeclaredMethod("speak", parameterTypes); 22 method.invoke(dog, new Object[] { "Hi" }); 23 } catch (SecurityException e1) { 24 e1.printStackTrace(); 25 } catch (NoSuchMethodException e1) { 26 e1.printStackTrace(); 27 } catch (IllegalArgumentException e) { 28 e.printStackTrace(); 29 } catch (IllegalAccessException e) { 30 e.printStackTrace(); 31 } catch (InvocationTargetException e) { 32 e.printStackTrace(); 33 } 34 } 35 }
經過java.lang.reflect.Method類來構建方法,再經過invoke方法執行dog對象的speak方法。
簡單說說反射的執行過程
JVM裝載類的目的就是把Java
字節代碼轉換成JVM
中的java.lang.Class
類的對象。這樣Java就能夠對該對象進行一系列操做,而上面的例子:Class.forName(classname)方法,其實是調用了Class類中的 Class.forName(classname, true, currentLoader)方法。參數:name - 所需類的徹底限定名;initialize - 是否必須初始化類;loader - 用於加載類的類加載器。currentLoader則是經過調用ClassLoader.getCallerClassLoader()獲取當前類加載器的。類要想使用,必須用類加載器加載,因此須要加載器。
還有一點:反射機制不是每次都去從新反射,而是提供了緩存,每次都須要類加載器去本身的緩存中查找,若是能夠查到,則直接返回該類。Java類加載器大致分兩類:前三者是一類,分爲BootStrap Class Loader(引導類加載器),Extensions Class Loader (擴展類加載器),App ClassLoader(或System Class Loader系統類加載器),最後一個是另外一類叫用戶自定義類加載器。
類的加載過程有兩個比較重要的特徵:層次組織結構和代理模式。
層次組織結構指的是每一個類加載器都有一個父類加載器(除了引導類加載器以外),經過getParent()方法能夠獲取到。類加載器經過這種父親-後代的方式組織在一塊兒,造成樹狀層次結構。系統類加載器的父類加載器是擴展類加載器,而擴展類加載器的父類加載器是引導類加載器,對於開發人員編寫的類加載器來講,其父類加載器是加載此類加載器 Java 類的類加載器。由於類加載器 Java 類如同其它的 Java 類同樣,也是要由類加載器來加載的。通常來講,開發人員編寫的類加載器的父類加載器是系統類加載器。類加載器經過這種方式組織起來,造成樹狀結構。樹的根節點就是引導類加載器。如圖:
代理模式則指的是一個類加載器既能夠本身完成Java類的定義工做,也能夠代理給其它的類加載器來完成。因爲代理模式的存在,啓動一個類的加載過程的類加載器和最終定義這個類的類加載器可能並非一個。
Java類的加載過程:
1.經過類的全名產生對應類的二進制數據流。(若是沒找到對應類文件,只有在類實際使用時才拋出錯誤。)
2.分析並將這些二進制數據流轉換爲方法區特定的數據結構(這些數據結構是實現有關的,不一樣 JVM 有不一樣實現)。這裏處理了部分檢驗,好比類文件的魔數的驗證,檢查文件是否過長或者太短,肯定是否有父類(除了 Obecjt 類)。
3.建立對應類的 java.lang.Class 實例(注意,有了對應的 Class 實例,並不意味着這個類已經完成了加載!)。
而JVM在整個加載過程當中,會先檢查類是否被已加載,檢查順序是自底向上,從系統類加載器到引導類加載器逐層檢查,只要某個classloader已加載就視爲已加載此類,保證此類只被全部ClassLoader加載一次。
可是加載的順序是自頂向下(和檢測順序反着,屬於父類優先的順序),也就是由上層來逐層嘗試加載此類。類加載器的詳細介紹後續專題總結。
只說一點,ClassLoader的加載類過程主要使用loadClass方法,該方法中封裝了加載機制:雙親委派模式。在forName方法中,就是調用了ClassLoader.loadClass方法來完成類的反射的,正如前面說的,JVM先檢查本身是否已經加載過該類,若是加載過,則直接返回該類,若沒有則調用父類的loadClass方法,若是父類中沒有,則執行findClass方法去嘗試加載此類,也就是咱們一般所理解的片面的"反射"了。
這個過程主要經過ClassLoader.defineClass方法來完成。defineClass 方法將一個字節數組轉換爲 Class 類的實例(任何類都是Class類的對象,在Java中,每一個class都有一個相應的Class對象,也就是說,當咱們編寫一個類.java文件,編譯完成後,在生成的.class文件中,就會產生一個Class對象,用於表示這個類的類型信息,既一切皆是對象)。這種新定義的類的實例須要使用 Class.newInstance 來建立,而不能使用new來實例化。
大白話:運行期間,若是咱們要產生某個類的對象,JVM 會檢查該類型的Class對象是否已被加載。若是沒有被加載,JVM會根據類的名稱找到.class文件並加載它。一旦某個類型的Class對象已被加載到內存,就能夠用它來產生該類型的全部對象。
再ps點:類加載器的用途
類加載器除了加載類信息,獲取類信息以外,還有一個重要用途是在JVM
中爲相同名稱的Java類建立隔離空間。在JVM
中,判斷兩個類是否相同,不只是根據該類的二進制名稱,還須要根據兩個類的定義類加載器。只有二者徹底同樣,才認爲兩個類的是相同的。所以,即使是一樣的Java
字節代碼,被兩個不一樣的類加載器定義以後,所獲得的Java
類也是不一樣的。若是試圖在兩個類的對象之間進行賦值操做,會拋出java.lang.ClassCastException
。這個特性爲一樣名稱的Java
類在JVM
中共存創造了條件。在實際的應用中,可能會要求同一名稱的Java
類的不一樣版本在JVM
中能夠同時存在。經過類加載器就能夠知足這種需求。這種技術在OSGi
中獲得了普遍的應用。
反射的應用
反射的缺點
2016-03-08 22:28:51,424 | INFO | main | dao.Session.save(Session.java:188) | save
2016-03-08 22:28:51,428 | INFO | main | dao.Session.save(Session.java:190) | SQL: insert into students(sname,sid) values (?,?)
Process finished with exit code 0
打算一步步在總結框架的時候完善和重構一個能用的ORM框架。
設計思路:
第一點:鏈接數據庫。第一種是JDBC鏈接,第二種是採用數據源來鏈接(採用數據源鏈接的時候,能夠採用任何的數據源,c3p0,dbcp。)。
第二點:操做數據:添加數據,刪除數據,修改數據。
第三點:查詢數據。
開發思路:
問題:拼接SQL語句的時候,表名從哪裏來?字段從哪裏來?值從哪裏來?
<class name=」User」 table=」user」>,name就表明了user這張表。table標籤對應的值,就是表名。咱們在配置中,會對每個字段進行配置,那麼我固然能夠取到字段的名字。最重要的是,值是怎麼來的。前面說了,利用Java所提供的反射機制來獲取
問題:若是一個表中有4個字段,我只需修改1個字段,那麼在修改的時候,只是針對於這一個字段給實體對象賦值,這個對象的其餘的字段屬性,都是null,這個時候,怎麼樣纔可以只修改對應的字段?
拼接一條SQL語句,好比說:form User。可是,這條語句數據庫是不認識的。數據庫認識的是這樣的:select * from 表名。固然,我也能夠添加一些條件。把拼接好的SQL語句,放到關係數據庫中取執行,獲得的是結果集:ResultSet。這個方法,返回給用戶的是一個List,是一個直接可使用的列表,可是這個列表中會有不少不少的對象,每個對象,又都有對應的值。當拿到結果集之後,遍歷結果集,而後根據上下文(好比表),把查詢出來的值,利用反射的方法設置到對象中,再把對象添加到列表中,最後返回列表。
這個方法中有不少的細節須要處理:好比說,當數據庫中的表,不是單一的表,是有鏈接關係的時候,拼接SQL語句會比較麻煩,並且在添加數據到列表中的時候,須要進行的處理也會特別的多。還有條件查詢的情景……