传智播客-hibernate(3)-回调与拦截机制

本文介绍了Hibernate中的回调机制,如Lifecyle与Validatable接口,以及拦截器Interceptor如何解决代码高侵入性问题。通过示例展示了AOP的概念和在Struts2中的应用,解释了反射和代理在实现拦截器中的作用。讨论了在Hibernate中使用Interceptor避免了实体类对特定接口的依赖,增强了移植性。

我写的这一篇是《深入浅出Hibernate》里第五章第二节以及《struts2权威指南》第七章第一节的内容,这两部分内容主要是帮助我更加理顺了对于“侵入性”的理解和拦截器的基本实现原理,所以特意摘取了出来(详细内容请参看这两本书的相关章节)。从下面的内容也可以看出来,这部分的内容单从技术应用上而言,易懂好使,但是拦截后的处理,我个人以为,是和业务(包括数据处理方面)高度相关的,因为没有实践经验,这里没法给出具体示例,其妙处也许得等我实际工作遇到了以后才能真正体会。

 

(忏悔一下。。老师讲这部分内容的时候俺ms打盹了。。。所以写这篇用的是其他书上的内容。。。)


 

先说AOP
上课时问过老师一个问题,模版模式是先预设一定的流程步骤,然后自定义某些步骤的具体实现,AOP也是自定义某些步骤的具体实现,那这两种有什么区别呢?答:AOP不仅是自定义某些步骤的实现,还包括定义这些步骤自身。我是这么理解的,模版模式预设的流程是既定的,例如流程S-A-B-C-E,流程不可更改,但是具体的步骤ABC可以自行定义,而AOP的流程自身是可以更改的,例如原有流程S-A-B-C-E,可以添加一些实现步骤使之变为S-D-A-B-E-C-E。而且实现原理也不一样,模版模式是用抽象类来预设流程的框架,而AOP的底层实现则要用到反射(书里介绍AOP的实现时加了一句修饰语“大部分时候”,但是我并不清楚不使用反射的特例是什么样的情况)。而且,在框架中体现出的AOP设计是可插拔式的,通过配置文件即可以实现这一点。

 

“struts2的拦截体系体现了AOP的设计哲学”--虽然说的是struts2,但是我相信这句话对于其他框架的拦截体系也同样适用。

 

AOP编程方式中,有三个重要概念:
(1)目标对象。包含被拦截方法的原始对象。
(2)被插入的处理方法。不能独立存在,需要有一个存在的载体--拦截器,会在被拦截方法之前、之后自动执行的方法。
(3)代理对象。以目标对象为蓝本,由系统创建的新对象(当然,这个能自动创建新对象的“系统”也是要事先预设好这一功能实现的)。

 

反射和代理
下文只是从《struts2权威指南》相关章节抽取的关于反射应用以实现AOP机制的简单示例,关于反射的具体知识请自行google百度或来传智播客聆听:)
示例代码:
public interface Dog {
 public void info();
 public void run();
}
public class DogImpl implements Dog {
 @Override
 public void info() {
  System.out.println("I am a dog.");
 }
 @Override
 public void run() {
  System.out.println("I run fast.");
 }
}
//定义系统拦截器类
public class DogInterceptor {
 //第一个拦截器方法
 public void method1(){
  System.out.println("=====method1=====");
 }
 //第二个拦截器方法
 public void method2(){
  System.out.println("=====method2=====");
 }
}
//代理的处理类
//可以看出这个类在代码上与普通java类没有区别,之所以称之为拦截器,只是就其行为而言。
//与被拦截对象没有丝毫耦合,从而提供了更好的解耦,以及更好的可扩展性。
//但是与其他两个地方耦合了:
//1、与拦截器耦合:固定使用了DogInterceptor拦截器
//2、与被拦截的方法耦合:指定了拦截名为info的方法
//通过配置文件解耦
//hibernate还可以通过将拦截器注册到session中以达到拦截器的复用
public class ProxyHandler implements InvocationHandler {
 //需被代理的目标对象
 private Object target;
 //创建拦截器实例
 DogInterceptor di = new DogInterceptor();
 //执行代理的目标方法时,该invoke方法会被自动调用
 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  Object result = null;
  //如果被调用方法的方法名为info
  if(method.getName().equals("info")){
   //调用拦截器方法1
   di.method1();
   result=method.invoke(target, args);
   //调用拦截器方法2
   di.method2();
  }else{
   result=method.invoke(target, args);
  }
  return result;
 }
 public void setTarget(Object object){
  this.target=object;
 }
}
//代理工厂,根据目标对象生产一个代理实例。
//工厂模式,简单说就是:调用工厂的方法产生产品的实例。
//个人感觉,最后一句return语句中,DogImpl.class通常情况下应该不会像这样硬编码植入
public class MyProxyFactory {
 /**
  * 实例Service对象
  * @param serviceName String
  * @return Object
  */
 public static Object getProxy(Object object){
  //代理的处理类实例
  ProxyHandler handler = new ProxyHandler();
  //把该Dog实例托付给代理操作
  handler.setTarget(object);
  //第一个参数是用来创建可以定义代理类的类加载器对象--DogImpl类
  //第二个参数是接口数组,即被代理类实现的接口--Dog接口
  //第三个参数是代理包含的处理实例--ProxyHandler,主要是调用该类的invoke()方法
  return  Proxy.newProxyInstance(DogImpl.class.getClassLoader(), object.getClass().getInterfaces(), handler);
 }
}
public class TestDog {
 //运行结果 :
 //=====method1=====
 //I am a dog.
 //=====method2=====
 //I run fast.
 public static void main(String[] args) {
  //创建一个Dog实例,该实例将被作为代理的目标对象
  Dog targetObject = new DogImpl();
  Dog dog = null;
  //以目标对象创建代理
  Object proxy = MyProxyFactory.getProxy(targetObject);
  if(proxy instanceof Dog){
   dog = (Dog)proxy;
  }
  //测试被代理的方法info
  dog.info();
  dog.run();
 }
}

 

回调与拦截
1、Lifecyle与Validatable(看一下hibernate包内的这两个接口你就知道该咋用了)
“在某些情况下,我们需要对实体对象的CRUD操作进行捕获并执行相应的处理。在数据库层,这通常通过触发器(Triger)实现。”而“Hibernate通过Lifecyle与Validatable接口制定了实体对象CRUD过程中的回调(CallBack)方式”来实现(provides callbacks from the session to the persistent object)。Lifecyle主要针对的是CRUD的业务逻辑,实体对象的生命周期内只会被调用一次;而Validatable在实体对象的生命周期内可能数次调用,仅应用于数据进行验证。

 

“Lifecyle/Validatable接口定义了一种自然的回调机制”,但是“这种机制要求实体类必须实现Lifecyle/Validatable接口,Hibernate原生接口的介入,使得我们的实体类移植性大大降低(实际上,此时的实体类已经不再是一个严格意义上的POJO)。”这句话中,“实体类”指@Entity注解的类;“Hibernate原生接口”指Lifecyle/Validatable接口是hibernate独有的,需要绑定某个产品系统的独有接口,意味着这种方式具有高侵入性,所以降低了移植性。

 

2、Interceptor(同上)
为了解决上面的代码高侵入性问题,hibernate引入了Interceptor(与这个原因相似的还有ejb2--ejb3)。hibernate的Interceptor接口定义了通用的拦截机制,session创建时即可加载--session=sessionFactory.openSession(myInterceptor),“之后,此session的持久化操作动作都将首先经由此拦截其捕获处理。”可以这么理解,Interceptor将对CRUD操作的监控与实体类解耦,而绑定在session上。

 

要注意的是,上述两种方式都不能调用当前session操作,因为“这些接口里面定义的方法都是由当前session负责调用的,如果在这些方法中又调用了当前session进行持久化操作,将会导致session内部状态的混乱”(这可不是递归)。

 

可以使用一个新的session实例,但是这意味着一个操作占用两个数据库连接,如果数据库并发量大的化这样的做法很不明智。较为合理的是重用当前session的数据库连接:sessionFactory.openSession(connection),依托当前session的JDBC Connection,创建一个临时session实例。此外,因为该临时session中的JDBC连接是与执行Lifecyle/Interceptor的session所共享的,所以无需再启动事务,也无需close。

 


 

ms拦截器(机制)现在是个很常见的东东,struts、hibernate、spring还有ejb都有。拦截器的基本实现原理--反射和代理,是传智播客基础班的必讲课程,还有java的annotation。就业班的javaEE加强课程也很有份量。这些课程对于理解很多开源框架的实现原理都很有帮助。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值