日志文章

2007年10月12日 16:39:37

spring aop 动态 代理 反射

概念:
--AOP及其机制:代理。
--代理(proxy)及其机制:反射。
--Proxy模式又分为两种
----A:静态Proxy模式
----B:动态Proxy模式
--何为动态代理:静态:代理类中引用实在的被代理类;动态:代理类中不引用实在的被代理类,动态注入。


参考:
--夏昕的SpringGuide
--http://www.javaeye.com/topic/47088

前言:最近2年一阵春(Spring)风吹变了世界各地,当然也把俺吹的昏头转向(差点没吹趴下,这那里是春风啊,简直就是头骨的阴风,:( ),故写一篇小文来迎接一哈这阵阵春风,希望对学习Spring的人,有一些帮助。好那,不说废话那,进入正题。

Spring 中提供的AOP支持,是基于动态的AOP机制实现的。
这里什么是动态的的AOP呢?
即通过动态的Proxy模式,在目标对象的方法调用的前后插入相应的处理代码。
那么是么是动态的Proxy模式呢?
在Spring中的动态Proxy模式,是基于Java Dynamic Proxy(面向Interface)和CGLIB(面向Class)的实现。
那什么是Dynamic Proxy是JDK1.3版本中引入的一种动态代理机制。它是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){
………………………
}
}

UserDAOProxy.java
Public class UserDAOProxy implments UserDAO{
//可以看到代理UserDAOProxy也是实现那UserDAO
// UserDAOProxy instanceof UserDAO 将返回TRUE
Private UserDAO   userDAO;
Public UserDAOProxy(UserDAO   userDAO){
This.userDAO= userDAO;
}
Public void saveUser(User user){
Transaction tx =null;
Try{
Tx=new Transaction();
UserDAO.saveUser(user);
}catch(Excption e){
Tx.rollBack();
}
}
}
当外部调用UserDAO或UserDAOImp的saveUser方法的时候,实际上是调用了UserDAOProxy的saveUser方法
注意UserDAOProxy同样也实现了UserDAO接口,对于调用者来说,saveuser方法的使用完全相同,不同的是内部的实现机制发生了一些改变,UserDAOProxy中为UserDAO.saveUser方法加了一层JTA事务管理的外壳。


----------
再来介绍一下JDK中的动态代理:Dynamic Proxy。
Spring AOP就是利用动态代理实现,包括另一个有名的AOP框架Nanning。

java.lang.reflect.Proxy用于Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
创建某一接口 Foo 的代理:
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[] { Foo.class }, handler); public static Object newProxyInstance(ClassLoader loader,
  Class[] interfaces,
  InvocationHandler h)
throws IllegalArgumentException
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。此方法相当于:
参数:
loader - 定义代理类的类加载器
interfaces - 代理类要实现的接口列表
h - 指派方法调用的调用处理程序
返回:
一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口。
动态代理类(以下简称为代理类)是一个实现在创建类时在运行时指定的接口列表的类。
代理接口 是代理类实现的一个接口。
代理实例 是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序 对象由于代理类将实现所有在其创建时指定的接口,所以对其 Class 对象调用 getInterfaces 将返回一个包含相同接口列表的数组(按其创建时指定的顺序),对其 Class 对象调用 getMethods 将返回一个包括这些接口中所有方法的 Method 对象的数组,并且调用 getMethod 将会在代理接口中找到期望的一些方法。
每个代理实例都有一个关联的调用处理程序,它会被传递到其构造方法中。
静态 Proxy.getInvocationHandler 方法将返回与作为其参数传递的代理实例相关的调用处理程序。
public interface InvocationHandler{Object invoke(Object proxy, Method method, Object[] args) throws Throwable}参数:
proxy - 在其上调用方法的代理实例
method - 对应于在代理实例上调用的接口方法的 Method 实例。Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。


-------------
动态代理的例子:(参考http://blog.csdn.net/ezerg/archive/2004/10/21/145275.aspx

sample_dynamic_proxy.rar


首先,是我们的接口以及实现类:
public interface Speakable {
public void speak();
}
public class SpeakHello implements Speakable{
private Logger logger = Logger.getLogger(getClass());
public void speak() {
logger.info("hello");
}
}


接着是proxy类:
public class SpeakProxy implements InvocationHandler {
private Logger logger = Logger.getLogger(this.getClass());

private Object callee;

private SpeakProxy(Object callee){
this.callee = callee;
}

public static Speakable getProxy(Object callee){
return (Speakable) Proxy.newProxyInstance(callee.getClass().getClassLoader(),
callee.getClass().getInterfaces(),
new SpeakProxy(callee));
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
Object result = null;

logger.info("before method invoke. " + method.getName() + "()");

try {
result = method.invoke(callee, args);
} catch (IllegalArgumentException e) {
logger.info(method.getName() + " has illegal argument exception.");
e.printStackTrace();
} catch (IllegalAccessException e) {
logger.info(method.getName() + " has illegal access exception.");
e.printStackTrace();
} catch (InvocationTargetException e) {
logger.info(method.getName() + " has invocation target exception.");
e.printStackTrace();
}finally{
logger.info("after mthod invoke. " + method.getName() + "()");
}
return result;
}
}
注意在newProxy返回的时候,你可以只返回Object,然后再调用的时候把它cast需要的接口。
这里为了简单和清楚,只返回Speakable。
最后看如何使用这个proxy:
public class Test {
public static void main(String[] args) {
Speakable speak = SpeakProxy.getProxy(new SpeakHello());
speak.speak();
}
}
如果一切正常你就可以看到如下的信息:
2004-09-09 10:35:16,655 INFO - before method invoke. speak()
2004-09-09 10:35:16,665 INFO - hello
2004-09-09 10:35:16,665 INFO - after mthod invoke. speak()
(程序中使用了log4j.jar,可以直接改为System.out.println更简单些)


----------
(参考:Struts,Hibernate,Spring集成开发宝典)
--
sample_online_aop.rar


aop中的配置:
参考http://www.javaeye.com/topic/47879
参考http://wiki.springside.org.cn/display/springside/Spring+Aop
----和1.0的比较。

execution(* *..BookManager.save(..))的解读:
  • 第一颗* 代表ret-type-pattern 返回值可任意,
  • *..BookManager 代表任意Pacakge里的BookManager类。
    如果写成com.xyz.service.* 则代表com.xyz.service下的任意类
    com.xyz.service..* com.xyz.service则代表com.xyz.service及其子package下的任意类
  • save代表save方法,也可以写save* 代表saveBook()等方法
  • (..) 匹配0个参数或者多个参数的,任意类型
    (x,..) 第一个参数的类型必须是X
    (x,,,s,..) 匹配至少4个参数,第一个参数必须是x类型,第二个和第三个参数可以任意,第四个必须是s类型。







Tags: spring   aop   动态代理   反射  

类别: 框架/struts |  评论(0) |  浏览(1655) |  收藏
发表评论
看不清楚,换一张