hibernate代理实现懒加载+cglib和jdk动态代理

来源:互联网 发布:易宝软件深圳分公司 编辑:程序博客网 时间:2024/06/16 04:56

Hibernate中用proxy(代理)实现类的延迟加载

可以在.xml文件中指定lazy=”true”这个属性来实现:

这种方式等价于

这样Hibernate就会自动继承Student这个类,来生成一个代理类,这个代理类是实现延迟加载的关键,比如一个Student类有很多属性,所以就希望当用到Student的属性的时候(调用getXXX())才去数据库读取,这个时候时候就可以使用Student的代理类如StudentProxy来实现Student的延迟加载!

Student student = (Student)session.load(Student.class, id); //如果使用proxy的话,Hibernate在执行这句话的时候根本就不会去执行select操作,而这句话的作用仅仅是用cglib来初始化代理类(setSid(id)…)。

student.getSname(); //当执行这句化的时候,Hibernate才会去执行select操作,读取数据库,如下是Hibernate执行此句时候输出的sql
Hibernate: select student0_.sid as sid0_, student0_.sname as sname0_, student0_.sage as sage0_ from student student0_ where student0_.sid=?

实现原理:proxy其实和JDK1.3的动态代理没有什么区别,只不过JDK1.3的动态代理只能代理接口,而我们在应用Hibernate的时候要代理类,所以必须用cglib来实现动态代理类的功能。

下面用JDK的Dynamic Proxy大概说明一下实现过程,大致代码如下:

package com.test;import java.lang.reflect.Proxy;     import java.lang.reflect.InvocationHandler;     import java.lang.reflect.Method;   public class ProxyTest implements InvocationHandler    {        public Student student;    public ProxyTest(Student student)        {             this.student = student;        }    public static Student getStudent(Student student)        {            return (Student)(Proxy.newProxyInstance(Student.class.getClassLoader(),new Class[]{Student.class},new StudentProxy(student)));        }        public Object invoke(Object proxy, Method m, Object[] args) throws Throwable        {            Object obj = null;            if(“getSname”.equals(m.getSname()))    //当执行的方法为getSname()的时候,就从数据库加载Student类            {                loadById();   //执行select操作;             obj = m.invoke(student, args);              }            else                 obj = m.invoke(student, args);               return obj;        }}

java动态代理(JDK和cglib)
JAVA的动态代理
代理模式
代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
按照代理的创建时期,代理类可以分为两种。
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理:在程序运行时,运用反射机制动态创建而成。

首先看一下静态代理:
1、Count.java

    package net.battier.dao;      /**      * 定义一个账户接口      *       * @author Administrator      *       */      public interface Count {          // 查看账户方法          public void queryCount();          // 修改账户方法          public void updateCount();      }  

2、CountImpl.java

    package net.battier.dao.impl;      import net.battier.dao.Count;      /**      * 委托类(包含业务逻辑)      *       * @author Administrator      *       */      public class CountImpl implements Count {          @Override          public void queryCount() {              System.out.println("查看账户方法...");          }          @Override          public void updateCount() {              System.out.println("修改账户方法...");          }      }      、CountProxy.java      package net.battier.dao.impl;      import net.battier.dao.Count;      /**      * 这是一个代理类(增强CountImpl实现类)      *       * @author Administrator      *       */      public class CountProxy implements Count {          private CountImpl countImpl;          /**          * 覆盖默认构造器          *           * @param countImpl          */          public CountProxy(CountImpl countImpl) {              this.countImpl = countImpl;          }          @Override          public void queryCount() {              System.out.println("事务处理之前");              // 调用委托类的方法;              countImpl.queryCount();              System.out.println("事务处理之后");          }          @Override          public void updateCount() {              System.out.println("事务处理之前");              // 调用委托类的方法;              countImpl.updateCount();              System.out.println("事务处理之后");          }      }  

3、TestCount.java

    package net.battier.test;      import net.battier.dao.impl.CountImpl;      import net.battier.dao.impl.CountProxy;      /**      *测试Count类      *       * @author Administrator      *       */      public class TestCount {          public static void main(String[] args) {              CountImpl countImpl = new CountImpl();              CountProxy countProxy = new CountProxy(countImpl);              countProxy.updateCount();              countProxy.queryCount();          }      }  

观察代码可以发现每一个代理类只能为一个接口服务,这样一来程序开发中必然会产生过多的代理,而且,所有的代理操作除了调用的方法不一样之外,其他的操作都一样,则此时肯定是重复代码。解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能,那么此时就必须使用动态代理完成。
再来看一下动态代理:
JDK动态代理中包含一个类和一个接口:
InvocationHandler接口:
public interface InvocationHandler {
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
}
参数说明:
Object proxy:指被代理的对象。
Method method:要调用的方法
Object[] args:方法调用时所需要的参数

可以将InvocationHandler接口的子类想象成一个代理的最终操作类,替换掉ProxySubject。

Proxy类:
Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法:
public static Object newProxyInstance(ClassLoader loader, Class

    package net.battier.dao;      public interface BookFacade {          public void addBook();      }  

2、BookFacadeImpl.java

    package net.battier.dao.impl;      import net.battier.dao.BookFacade;      public class BookFacadeImpl implements BookFacade {          @Override          public void addBook() {              System.out.println("增加图书方法。。。");          }      }      、BookFacadeProxy.java      package net.battier.proxy;      import java.lang.reflect.InvocationHandler;      import java.lang.reflect.Method;      import java.lang.reflect.Proxy;      /**      * JDK动态代理代理类      *       * @author student      *       */      public class BookFacadeProxy implements InvocationHandler {          private Object target;          /**          * 绑定委托对象并返回一个代理类          * @param target          * @return          */          public Object bind(Object target) {              this.target = target;              //取得代理对象              return Proxy.newProxyInstance(target.getClass().getClassLoader(),                      target.getClass().getInterfaces(), this);   //要绑定接口(这是一个缺陷,cglib弥补了这一缺陷)          }          @Override          /**          * 调用方法          */          public Object invoke(Object proxy, Method method, Object[] args)                  throws Throwable {              Object result=null;              System.out.println("事物开始");              //执行方法              result=method.invoke(target, args);              System.out.println("事物结束");              return result;          }      }  

3、TestProxy.java

    package net.battier.test;      import net.battier.dao.BookFacade;      import net.battier.dao.impl.BookFacadeImpl;      import net.battier.proxy.BookFacadeProxy;      public class TestProxy {          public static void main(String[] args) {              BookFacadeProxy proxy = new BookFacadeProxy();              BookFacade bookProxy = (BookFacade) proxy.bind(new BookFacadeImpl());              bookProxy.addBook();          }      }  

但是,JDK的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用JDK代理,这就要使用cglib动态代理了。

Cglib动态代理
JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
示例
1、BookFacadeCglib.java

package net.battier.dao;  public interface BookFacade {      public void addBook();  } 

2、BookCadeImpl1.java

    package net.battier.dao.impl;      /**      * 这个是没有实现接口的实现类      *       * @author student      *       */      public class BookFacadeImpl1 {          public void addBook() {              System.out.println("增加图书的普通方法...");          }      }  

3、BookFacadeProxy.java

    package net.battier.proxy;      import java.lang.reflect.Method;      import net.sf.cglib.proxy.Enhancer;      import net.sf.cglib.proxy.MethodInterceptor;      import net.sf.cglib.proxy.MethodProxy;      /**      * 使用cglib动态代理      *       * @author student      *       */      public class BookFacadeCglib implements MethodInterceptor {          private Object target;          /**          * 创建代理对象          *           * @param target          * @return          */          public Object getInstance(Object target) {              this.target = target;              Enhancer enhancer = new Enhancer();              enhancer.setSuperclass(this.target.getClass());              // 回调方法              enhancer.setCallback(this);              // 创建代理对象              return enhancer.create();          }          @Override          // 回调方法          public Object intercept(Object obj, Method method, Object[] args,                  MethodProxy proxy) throws Throwable {              System.out.println("事物开始");              proxy.invokeSuper(obj, args);              System.out.println("事物结束");              return null;          }      }  

4、TestCglib.java

    package net.battier.test;      import net.battier.dao.impl.BookFacadeImpl1;      import net.battier.proxy.BookFacadeCglib;      public class TestCglib {          public static void main(String[] args) {              BookFacadeCglib cglib=new BookFacadeCglib();              BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(new BookFacadeImpl1());              bookCglib.addBook();          }      }  
原创粉丝点击