|
概念: --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类型。
|
一共有 0 条评论