今天看Hibernate时发现延迟加载用到CGLib来返回代理对象,为了更深入理解CGLib特收集以下博文:
学习后总结
#1首先确定被代理类
package cn.java.cglibproxy;
public class TestCglib {
public void pringSomthing(){
System.out.println("printSomthing is now!!!");
}
}
#2写一个代理类,这个代理类要实现MethodIntercepter接口,它主要做两件事情,第一件是提供一个创建被代理类子类的方法,可以通过CGLib API 比如Enhancer来实现,第二件是重写intercept方法以便在子类调用父类的方法时进行拦截,从而实现代理概念===>其实第一件事情不必需在拦截器中作,可以在客户端程序中写,把实现MethodIntercepter的类单纯看成是一个拦截器就可以了,只不过要将拦截器接口显示赋给Enhancer的setCallback方法,也就是为其设置拦截器。实际在本例中enhancer.setCallback(this); 也是执行设置拦截器的操作,如果在客户端中写,需用具体的拦截器对象代替this.相当enhancer是被代理类和拦截器的桥梁
- package cn.java.cglibproxy;
- import java.lang.reflect.Method;
- import net.sf.cglib.proxy.Enhancer;
- import net.sf.cglib.proxy.MethodInterceptor;
- import net.sf.cglib.proxy.MethodProxy;
- public class CglibProxy implements MethodInterceptor {
- private Enhancer enhancer = new Enhancer();
- public Object getProxy(Class<TestCglib> clazz){
- enhancer.setSuperclass(clazz); //设置父类(被代理类)以创建子类
- enhancer.setCallback(this); //设置拦截器:在这里有可能就设置当子类调用父类方法时便会调用拦截器方法intercept.
- return enhancer.create(); //创建被代理类的子类
- }
- public Object intercept(Object obj, Method method, Object[] arg2,
- MethodProxy proxy) throws Throwable {
- System.out.println("打印前");
- Object result = proxy.invokeSuper(obj, arg2);
- System.out.println("打印后");
- return result;
- }
- }
#3客户端程序:
- package cn.java.cglibproxy;
- public class TestCglibMain {
- public static void main(String[] args) {
- //初始化代理类
- CglibProxy proxy = new CglibProxy();
- //通过代理类创建被代理类的子类
- TestCglib test = (TestCglib)proxy.getProxy(TestCglib.class);
- //调用父类方法 -----当子类的对象调用父类方法时会调用拦截器进行拦截
- test.pringSomthing();
- }
- }
http://elfasd.iteye.com/blog/1771226
java的动态代理只能对接口进行代理,如果一个类没有实现任何接口,则需要使用Cglib的动态代理技术,
cglib的一种实现简单地讲就是一句话:为代理类创建一个子类,并拦截这个子类对父类方法的调用。
这么说比较抽象,下面三个简单的类实现了cglib的动态代理
入口类:
TestCglibMain.java
- package cn.java.cglibproxy;
- public class TestCglibMain {
- public static void main(String[] args) {
- //初始化代理类
- CglibProxy proxy = new CglibProxy();
- //创建代理子类
- TestCglib test = (TestCglib)proxy.getProxy(TestCglib.class);
- //调用父类方法
- test.pringSomthing();
- }
- }
package cn.java.cglibproxy;
public class TestCglibMain {
public static void main(String[] args) {
//初始化代理类
CglibProxy proxy = new CglibProxy();
//创建代理子类
TestCglib test = (TestCglib)proxy.getProxy(TestCglib.class);
//调用父类方法
test.pringSomthing();
}
}
被代理类:
TestCglib.java
- package cn.java.cglibproxy;
- public class TestCglib {
- public void pringSomthing(){
- System.out.println("printSomthing is now!!!");
- }
- }
cglib代理实现类:
CglibProxy.java
- package cn.java.cglibproxy;
- import java.lang.reflect.Method;
- import net.sf.cglib.proxy.Enhancer;
- import net.sf.cglib.proxy.MethodInterceptor;
- import net.sf.cglib.proxy.MethodProxy;
- public class CglibProxy implements MethodInterceptor {
- private Enhancer enhancer = new Enhancer();
- public Object getProxy(Class<TestCglib> clazz){
- enhancer.setSuperclass(clazz); //设置父类(被代理类)以创建子类
- enhancer.setCallback(this); //设置拦截器
- return enhancer.create(); //创建被代理类的子类
- }
- public Object intercept(Object obj, Method method, Object[] arg2,
- MethodProxy proxy) throws Throwable {
- System.out.println("打印前");
- Object result = proxy.invokeSuper(obj, arg2);
- System.out.println("打印后");
- return result;
- }
- }
执行入口程序,打印结果:
打印前
printSomthing is now!!!
打印后
###对于Hibernate中使用CGLib来生成代理从而实现缓存的理解,它主要是在intercept方法中检查,如果是id就返回就调用父类的方法,并且此时父类的属性除了id外全部是空的,但如果不是id而是其它属性时就需要查数据库了(load)http://superleo.iteye.com/blog/243322
-
- public class LazyInitializerImpl<T, PK extends Serializable> implements
-
- LazyInitializer<T, PK>, MethodInterceptor {
- private Session<T, PK> session; // 绑定的session对象
- private boolean isAlreadyInit = false; // 是否已经查询过数据库
- private T targetObject; // 目标对象
- // 通CGLib生成的对象,如果设置了此拦截器,那么其方法每次调用时,都会触发此方法
- public Object intercept(Object obj, Method method, Object[] args,
- MethodProxy proxy) throws Throwable {
- // 继续利用反射得到代理对象的标有@Id的主键属性
- Class<?> clas = obj.getClass();
- Field field = getPrimaryKey(clas);
- assert (field != null);
- // 如果当前调用的方法是标注为@Id的话,那么就不从数据库里取,直接返回代理
- // 即如果是getId()的话,直接用代理调用;如果是getName()的话,那就必须查询数据库,取出实际对象,并进行相应的调用了
- if (method.getName().toLowerCase().indexOf(field.getName()) > -1) {
- return proxy.invokeSuper(obj, args);
- } else {
- if (!isAlreadyInit) {
- field.setAccessible(true);
- // session.get方法直接查询数据库,并将ResultSet结果组将成User对象
- targetObject = session.get((PK) field.get(obj));
- isAlreadyInit = true;
- }
- return method.invoke(targetObject, args);
- }
- }
- ..............省略其它辅助方法
- }