标签:
俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及的知识点总结如下:
开源框架的学习思路(个人经验,欢迎提出意见)
框架是为了解决开发中遇到的一个个问题而诞生的,程序员是为了解决问题而学习框架的,这才是正确的学习之道!一个框架的好与坏完全取决于其对问题解决程度和解决方式。个人的学习过程:
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),从而可以操作数据库
3. 因为核心配置Hibernate.cfg.xml里引入了实体关系映射配置文件,故该文件也会自动被解析——加载对象-关系映射文件:vo类.hbm.xml
4、然后是创建session对象,通过SessionFactory创建session。session可以操作数据库
// 通过 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语句会比较麻烦,而且在添加数据到列表中的时候,需要进行的处理也会特别的多。还有条件查询的情景……
Hibernate(2)——Hibernate的实现原理总结和对其模仿的demo
标签:
原文地址:http://www.cnblogs.com/kubixuesheng/p/5178851.html