深入理解AOP (读SpringGuide随笔)<o:p></o:p>
前言:最近2年一阵春(Spring)风吹变了世界各地,当然也把俺吹的昏头转向(差点没吹趴下,这那里是春风啊,简直就是头骨的阴风,:( ),故写一篇小文来迎接一哈这阵阵春风,希望对学习Spring的人,有一些帮助。好那,不说废话那,进入正题。
<o:p> </o:p>
Spring 中提供的AOP支持,是基于动态的AOP机制实现的。
这里什么是动态的的AOP呢?
即通过动态的Proxy模式,在目标对象的方法调用的前后插入相应的处理代码。
那么是么是动态的Proxy模式呢?
在Spring中的动态Proxy模式,是基于Java Dynamic Proxy(面向Interface)和CGLIB(面向Class)的实现。
那什么是Dynamic Proxy是JDK1.3版本中引入的一种动态代理机制。它是Proxy模式的一种动态实现版本。
而Proxy模式又分为两种
A:静态Proxy模式
B:动态Proxy模式
首先我们来看看什么是静态Proxy模式
假设我们有一个UserDAO接口以及一个实现类UserDAOImp
UserDAO.Java
Public interface UserDAO{
Public void saveUser(User user);
}
UserDAOImp.java
Public class UserDAOImp implents UserDAO{
Public void saveUser(User user){
………………………
}
}
<o:p> </o:p>
UserDAOProxy.java
Public class UserDAOProxy implments UserDAO{
//可以看到代理UserDAOProxy也是实现那UserDAO
· //UserDAOProxy
instanceof
UserDAO 将返回TRUE
<o:p> </o:p>
Private UserDAO userDAO;
Public UserDAOProxy(UserDAO userDAO){
This.userDAO= userDAO;
}
Public void saveUser(User user){
Transaction tx =null;<o:p></o:p>
Try{<o:p></o:p>
Tx=new Transaction();<o:p></o:p>
UserDAO.saveUser(user);
}catch(Excption e){<o:p></o:p>
Tx.rollBack();<o:p></o:p>
}<o:p></o:p>
}
}
当外部调用UserDAO或UserDAOImp的saveUser方法的时候,实际上是调用了UserDAOProxy的saveUser方法
注意UserDAOProxy同样也实现了UserDAO接口,对于调用者来说,saveuser方法的使用完全相同,不同的是内部的实现机制发生了一些改变,UserDAOProxy中为UserDAO.saveUser方法加了一层JTA事务管理的外壳。
好那,现在我们看看如何创建一个动态代理类的实例<o:p></o:p>
java.lang.reflect.Proxy用于Proxy
提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。<o:p></o:p>
创建某一接口 Foo
的代理:
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
new Class[] { Foo.class },
handler);
<o:p> </o:p>
public static Object newProxyInstance(ClassLoader loader,<o:p></o:p>
Class[] interfaces,<o:p></o:p>
InvocationHandler h)<o:p></o:p>
throws IllegalArgumentException<o:p></o:p>
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。此方法相当于: <o:p></o:p>
参数: <o:p></o:p>
loader - 定义代理类的类加载器 <o:p></o:p>
interfaces - 代理类要实现的接口列表 <o:p></o:p>
h - 指派方法调用的调用处理程序 <o:p></o:p>
返回: <o:p></o:p>
一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口 <o:p></o:p>
动态代理类(以下简称为代理类)是一个实现在创建类时在运行时指定的接口列表的类。
代理接口 是代理类实现的一个接口。
代理实例 是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序 对象
每个代理实例都有一个关联的调用处理程序,它会被传递到其构造方法中。静态 Proxy.getInvocationHandler
方法将返回与作为其参数传递的代理实例相关的调用处理程序。
public interface InvocationHandler{
Object invoke(Object proxy,
Method method,
Object[] args)
throws Throwable}
参数: <o:p></o:p>
proxy - 在其上调用方法的代理实例 <o:p></o:p>
method - 对应于在代理实例上调用的接口方法的 Method 实例。Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。 <o:p></o:p>
args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。<o:p></o:p>
<o:p> </o:p>
<o:p> </o:p>
Spring的 HibernateDaoSupport 基类提供了访问与当前事务绑定的 Session 对象的函数,因而能保证在这种情况下异常的正确转化。类似的函数同样可以在 SessionFactoryUtils 类中找到,但他们以静态方法的形式出现。 值得注意的是,通常将一个false作为参数(表示是否允许创建)传递到 getSession(..) 方法中进行调用。 此时,整个调用将在同一个事务内完成(它的整个生命周期由事务控制,避免了关闭返回的 Session 的需要)。 <o:p></o:p>
public class ProductDaoImpl extends HibernateDaoSupport implements ProductDao {<o:p></o:p>
<o:p> </o:p>
public Collection loadProductsByCategory(String category)<o:p></o:p>
throws DataAccessException, MyException {<o:p></o:p>
<o:p> </o:p>
Session session = getSession(getSessionFactory(), false);<o:p></o:p>
try {<o:p></o:p>
List result = session.find(<o:p></o:p>
"from test.Product product where product.category=?",<o:p></o:p>
category, Hibernate.STRING);<o:p></o:p>
if (result == null) {<o:p></o:p>
throw new MyException("invalid search result");<o:p></o:p>
}<o:p></o:p>
return result;<o:p></o:p>
}<o:p></o:p>
catch (HibernateException ex) {<o:p></o:p>
throw convertHibernateAccessException(ex);<o:p></o:p>
}<o:p></o:p>
}<o:p></o:p>
}<o:p></o:p>
这种直接使用Hibernate访问代码的主要好处在于它允许你在数据访问代码中抛出checked exception,而 HibernateTemplate 却受限于回调中的unchecked exception。 注意,你通常可以将这些应用程序的异常处理推迟到回调函数之后,这样,你依然可以正常使用 HibernateTemplate。 一般来说,HibernateTemplate 所提供的许多方法在许多情况下看上去更简单和便捷。 <o:p></o:p>
声明式的事务划分<o:p></o:p>