一)Spring AOP---schema-based approach
Spring AOP的配置方式,我强烈推荐这篇博客[url]http://pandonix.iteye.com/blog/336873[/url]。作者对AOP的概念和Spring AOP的xml配置实现都做了清晰的介绍。我想我是不可能做更好的介绍了...这里就再作几点补充说明吧~~
二)动态代理
要说动态代理,就得先说静态代理。动静都是比较出来的嘛~~
[b]静态代理:[/b]
UserDAOProxy和UserDAOImp一样,[b]同样是UserDAO接口的实现[/b],对于调用者而言,saveUser方法的使用完全相同,不同的是内部实现机制已经发生了一些变化――我们在UserDAOProxy中为UserDAO.saveUser方法套上了一个JTA事务管理的外壳。
很显然这样的结构设计,代理类只能代理某一个特定的接口(例子中是UserDAO接口)。现在假设系统中有20个类似的接口,针对每个接口实现一个Proxy,实在是个繁琐无味的苦力工程。所以我们需要一种设计能[b]将接口也作为参数动态的传入[/b],以实现对任意接口的动态代理!
[b]动态代理[/b]
AOPFactory就是Spring最后实现动态代理的一个精简示意。getClassInstance()首先通过反射机制将配置文件中的String转换成响应的Object。而后txHandler.bind()将根据obj生成响应的动态代理返回。显然,动态代理的核心逻辑就在AOPHandler类中。下面让我们看看,它到底是如何实现的:
1'首先说说public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
[b]参数说明:[/b]
ClassLoader loader:类加载器
Class<?>[] interfaces:得到全部的接口
InvocationHandler h:得到InvocationHandler接口的子类实例
注意:例子中InvocationHandler h传的是this。所以当获得动态代理后调用userDAO.saveUser时(假设我们的动态代理要代理的是userDAO接口),代理会调用AOPHandler.invoke来处理相应的逻辑。
此外newProxyInstance生产的动态代理类是无需我们手动编码参与的,程序会自动生成于内存中。这个自动生成的类就相当于上例静态代理中的UserDAOProxy,只是因为它在内存中我们看不见其源码罢了。
2'再说说,getIntercetors()。它严格来说和动态代理没什么关系。只是因为动态代理是接口名、接口内需要实现的方法名都不确定,所以在代理的前后处理时也不可避免的需要一些“动态”逻辑,需要采用反射机制。所以这里专门做了一些处理。和InvocationInfo 一样,具体的细节可以不关注。重点应该在AOPHandler本身。
3'介绍完了动态代理,我们可以发现一个结论:虽然动态代理的设计大大增加了代理的灵活型,但它仍然对被代理的类有一个限制:此类必须继承一个接口以供动态代理实现生成代理类!而CGLIB则进一步去除了这个限制
三)CGLIB
Spring中,引入了CGLib作为无接口情况下的动态代理实现。
CGLib与Dynamic Proxy的代理机制基本类似,只是其动态生成的代理对象并非某个接口的实现,而是针对目标类扩展的子类。
[img]http://dl.iteye.com/upload/attachment/0077/9124/3877ea0f-c7ef-3993-834b-2082fd154fcd.png[/img]
与Dynamic Proxy中的Proxy和InvocationHandler相对应,Enhancer和MethodInterceptor在CGLib中负责完成代理对象创建和方法截获处理。下面是通过CGLib进行动态代理的示例代码:
测试代码:
四)写好pointcut
对于pointcut的表达式,spring是参照AspectJ pointcut designators (PCD)的标准来做的。当然,由于AOP方面spring并没有AspectJ来得这么强大(spring只支持针对方法method的切面编程),所以在pointcut表达式上实际上也是取了PCD的一个子集而已。下面先说说PCD语法的几个关键字:
[b]execution[/b]:(对符合条件的方法)执行AOP
[b]within[/b]:在(满足条件的类型)内执行AOP (由于spring只支持方法层的AOP,所以spring中的within可以进一步解释为:对满足条件的类的方法执行AOP)
[b]this[/b]:指动态代理类本身
[b]target[/b]:指被动态代理类代理的对象(还是由于spring只支持方法层的AOP,而代理和被代理类的方法名肯定是一样的,所以spring中的this和target关键字几乎可以混用。)
[b]args[/b]:对满足此参数条件的方法执行AOP
execution是spring最常用的表达式,下面我们详加说明:
此表达式的格式如下:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
modifiers-pattern:修饰符类型(public protect private)
ret-type-pattern:返回值类型
declaring-type-pattern:对象类型
name-pattern:方法名
parm-pattern:参数名
throws-pattern:异常
其中,除ret-type-pattern,name-pattern和parameters-pattern之外,其他都是可选的。比如:
execution(public * *(..)) 表示所有public的方法
execution(* set*(..)) 表示所有方法名以set开头的方法
execution(* com.xyz.service.AccountService.*(..)) 表示接口AccountService内的所有方法
execution(* com.spring.service.*.*(..)) 表示com.spring.service包下,返回值为任意类型;方法名任意;参数不作限制的所有方法。
execution(* com.xyz.service..*.*(..)) 表示com.xyz.service包及其子包下,返回值为任意类型;方法名任意;参数不作限制的所有方法。
再来说说within:
within(com.xyz.service.*) 表示com.xyz.service包下所有的joint point(spring的话就是指com.xyz.service包下所有的方法了)
within(com.xyz.service..*) 表示com.xyz.service包及其子包下所有的joint point(spring的话就是指com.xyz.service包及其子包下所有的方法了)
下面是this:
this(com.xyz.service.AccountService) 表示代理了接口com.xyz.service.AccountService的动态代理下的所有joint point
target:
target(com.xyz.service.AccountService) 表示接口com.xyz.service.AccountService下的所有joint point
args:
args(java.io.Serializable)表示对所有传入参数是java.io.Serializable的方法执行AOP
注:1'最后说一种spring特有的,唯一不在PCD范围内的表达式:bean(idOrNameOfBean)
bean(*Service) 对以Service结尾的bean执行AOP
2'Spring AOP所有的pointcut都是针对public方法的。对应proctect private的方法无法使用AOP. 如果需要对protect和private的方法进行AOP,可以使用Spring-driven native AspectJ weaving。
Spring AOP的配置方式,我强烈推荐这篇博客[url]http://pandonix.iteye.com/blog/336873[/url]。作者对AOP的概念和Spring AOP的xml配置实现都做了清晰的介绍。我想我是不可能做更好的介绍了...这里就再作几点补充说明吧~~
二)动态代理
要说动态代理,就得先说静态代理。动静都是比较出来的嘛~~
[b]静态代理:[/b]
public interface UserDAO {
public void saveUser(User user);
}
public class UserDAOImp implements UserDAO{
public void saveUser(User user) {
……
}
}
//静态代理。代理了接口UserDAO下的所有实现类,例如UserDAOImp
public class UserDAOProxy implements UserDAO {
private UserDAO userDAO;
public UserDAOProxy(UserDAO userDAO) {
this.userDAO = userDAO;
}
public void saveUser(User user) {
UserTransaction tx = null;
try {
tx = (UserTransaction) (new InitialContext().lookup("java/tx"));
userDAO.saveUser(user);
tx.commit();
} catch (Exception ex) {
if (null!=tx){
try {
tx.rollback();
}catch(Exception e) {
}
}
}
}
}
UserDAOProxy和UserDAOImp一样,[b]同样是UserDAO接口的实现[/b],对于调用者而言,saveUser方法的使用完全相同,不同的是内部实现机制已经发生了一些变化――我们在UserDAOProxy中为UserDAO.saveUser方法套上了一个JTA事务管理的外壳。
很显然这样的结构设计,代理类只能代理某一个特定的接口(例子中是UserDAO接口)。现在假设系统中有20个类似的接口,针对每个接口实现一个Proxy,实在是个繁琐无味的苦力工程。所以我们需要一种设计能[b]将接口也作为参数动态的传入[/b],以实现对任意接口的动态代理!
[b]动态代理[/b]
public class AOPFactory {
private static Log logger = LogFactory.getLog(AOPFactory.class);
public static Object getClassInstance(String clzName){
Class cls;
try {
cls = Class.forName(clzName);
return (Object)cls.newInstance();
} catch (Exception e) {
logger.debug(e);
throw new AOPRuntimeException(e);
}
}
public static Object getAOPProxyedObject(String clzName){
AOPHandler txHandler = new AOPHandler();
Object obj = getClassInstance(clzName);
return txHandler.bind(obj);//返回动态代理类!!
}
}
AOPFactory就是Spring最后实现动态代理的一个精简示意。getClassInstance()首先通过反射机制将配置文件中的String转换成响应的Object。而后txHandler.bind()将根据obj生成响应的动态代理返回。显然,动态代理的核心逻辑就在AOPHandler类中。下面让我们看看,它到底是如何实现的:
public class AOPHandler implements InvocationHandler {
private static Log logger = LogFactory.getLog(AOPHandler.class);
private List interceptors = null;
private Object originalObject;
public Object bind(Object obj) {
this.originalObject = obj;
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(), this);
}
/**
* 在Invoke方法中,加载对应的Interceptor,并进行
* 预处理(before)、后处理(after)以及异常处理(exceptionThrow)过程
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
Throwable ex = null;
InvocationInfo invInfo = new InvocationInfo(proxy, method, args,result, ex);
logger.debug("Invoking Before Intercetpors!");
invokeInterceptorsBefore(invInfo);
try {
logger.debug("Invoking Proxy Method!");
result = method.invoke(originalObject, args);
invInfo.setResult(result);
logger.debug("Invoking After Method!");
invokeInterceptorsAfter(invInfo);
} catch (Throwable tr) {
invInfo.setException(tr);
logger.debug("Invoking exceptionThrow Method!");
invokeInterceptorsExceptionThrow(invInfo);
throw new AOPRuntimeException(tr);
}
return result;
}
private synchronized List getIntercetors() {
if (null == interceptors) {
interceptors = new ArrayList();
//Todo:读取配置,加载Interceptor实例
//interceptors.add(new MyInterceptor());
}
return interceptors;
}
private void invokeInterceptorsBefore(InvocationInfo invInfo) {
List interceptors = getIntercetors();
int len = interceptors.size();
for (int i = 0; i < len; i++) {
((Interceptor) interceptors.get(i)).before(invInfo);
}
}
private void invokeInterceptorsAfter(InvocationInfo invInfo) {
List interceptors = getIntercetors();
int len = interceptors.size();
for (int i = len - 1; i >= 0; i--) {
((Interceptor) interceptors.get(i)).after(invInfo);
}
}
private void invokeInterceptorsExceptionThrow(InvocationInfo invInfo) {
List interceptors = getIntercetors();
int len = interceptors.size();
for (int i = len - 1; i >= 0; i--) {
((Interceptor)interceptors.get(i)).exceptionThrow(invInfo);
}
}
}
1'首先说说public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
[b]参数说明:[/b]
ClassLoader loader:类加载器
Class<?>[] interfaces:得到全部的接口
InvocationHandler h:得到InvocationHandler接口的子类实例
注意:例子中InvocationHandler h传的是this。所以当获得动态代理后调用userDAO.saveUser时(假设我们的动态代理要代理的是userDAO接口),代理会调用AOPHandler.invoke来处理相应的逻辑。
此外newProxyInstance生产的动态代理类是无需我们手动编码参与的,程序会自动生成于内存中。这个自动生成的类就相当于上例静态代理中的UserDAOProxy,只是因为它在内存中我们看不见其源码罢了。
2'再说说,getIntercetors()。它严格来说和动态代理没什么关系。只是因为动态代理是接口名、接口内需要实现的方法名都不确定,所以在代理的前后处理时也不可避免的需要一些“动态”逻辑,需要采用反射机制。所以这里专门做了一些处理。和InvocationInfo 一样,具体的细节可以不关注。重点应该在AOPHandler本身。
3'介绍完了动态代理,我们可以发现一个结论:虽然动态代理的设计大大增加了代理的灵活型,但它仍然对被代理的类有一个限制:此类必须继承一个接口以供动态代理实现生成代理类!而CGLIB则进一步去除了这个限制
三)CGLIB
Spring中,引入了CGLib作为无接口情况下的动态代理实现。
CGLib与Dynamic Proxy的代理机制基本类似,只是其动态生成的代理对象并非某个接口的实现,而是针对目标类扩展的子类。
[img]http://dl.iteye.com/upload/attachment/0077/9124/3877ea0f-c7ef-3993-834b-2082fd154fcd.png[/img]
与Dynamic Proxy中的Proxy和InvocationHandler相对应,Enhancer和MethodInterceptor在CGLib中负责完成代理对象创建和方法截获处理。下面是通过CGLib进行动态代理的示例代码:
public class AOPInstrumenter implements MethodInterceptor {
private static Log logger = LogFactory.getLog(AOPInstrumenter.class);
private Enhancer enhancer = new Enhancer();
public Object getInstrumentedClass(Class clz) {
enhancer.setSuperclass(clz);
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object o, Method method,Object[] methodParameters,MethodProxy methodProxy)throws Throwable {
logger.debug("Before Method =>"+method.getName());
Object result = methodProxy.invokeSuper(o, methodParameters);
logger.debug("After Method =>"+method.getName());
return result;
}
}
测试代码:
AOPInstrumenter aopInst = new AOPInstrumenter();
UserDAOImp userDAO = (UserDAOImp) aopInst.getInstrumentedClass(UserDAOImp.class);
User user = new User();
user.setName("Erica");
userDAO.saveUser(user);
四)写好pointcut
对于pointcut的表达式,spring是参照AspectJ pointcut designators (PCD)的标准来做的。当然,由于AOP方面spring并没有AspectJ来得这么强大(spring只支持针对方法method的切面编程),所以在pointcut表达式上实际上也是取了PCD的一个子集而已。下面先说说PCD语法的几个关键字:
[b]execution[/b]:(对符合条件的方法)执行AOP
[b]within[/b]:在(满足条件的类型)内执行AOP (由于spring只支持方法层的AOP,所以spring中的within可以进一步解释为:对满足条件的类的方法执行AOP)
[b]this[/b]:指动态代理类本身
[b]target[/b]:指被动态代理类代理的对象(还是由于spring只支持方法层的AOP,而代理和被代理类的方法名肯定是一样的,所以spring中的this和target关键字几乎可以混用。)
[b]args[/b]:对满足此参数条件的方法执行AOP
execution是spring最常用的表达式,下面我们详加说明:
此表达式的格式如下:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
modifiers-pattern:修饰符类型(public protect private)
ret-type-pattern:返回值类型
declaring-type-pattern:对象类型
name-pattern:方法名
parm-pattern:参数名
throws-pattern:异常
其中,除ret-type-pattern,name-pattern和parameters-pattern之外,其他都是可选的。比如:
execution(public * *(..)) 表示所有public的方法
execution(* set*(..)) 表示所有方法名以set开头的方法
execution(* com.xyz.service.AccountService.*(..)) 表示接口AccountService内的所有方法
execution(* com.spring.service.*.*(..)) 表示com.spring.service包下,返回值为任意类型;方法名任意;参数不作限制的所有方法。
execution(* com.xyz.service..*.*(..)) 表示com.xyz.service包及其子包下,返回值为任意类型;方法名任意;参数不作限制的所有方法。
再来说说within:
within(com.xyz.service.*) 表示com.xyz.service包下所有的joint point(spring的话就是指com.xyz.service包下所有的方法了)
within(com.xyz.service..*) 表示com.xyz.service包及其子包下所有的joint point(spring的话就是指com.xyz.service包及其子包下所有的方法了)
下面是this:
this(com.xyz.service.AccountService) 表示代理了接口com.xyz.service.AccountService的动态代理下的所有joint point
target:
target(com.xyz.service.AccountService) 表示接口com.xyz.service.AccountService下的所有joint point
args:
args(java.io.Serializable)表示对所有传入参数是java.io.Serializable的方法执行AOP
注:1'最后说一种spring特有的,唯一不在PCD范围内的表达式:bean(idOrNameOfBean)
bean(*Service) 对以Service结尾的bean执行AOP
2'Spring AOP所有的pointcut都是针对public方法的。对应proctect private的方法无法使用AOP. 如果需要对protect和private的方法进行AOP,可以使用Spring-driven native AspectJ weaving。