如果说IoC是Spring的核心,那么面向切面编程就是Spring最为重要的功能之一了,在数据库事务中切面编程被广泛使用。一切要从Spring AOP的底层技术----动态代理技术说起。
一个简单的约定游戏
1、约定规则
先来一个Interceptor接口:
/**
* 拦截器
*/
public interface Interceptor{
public void before(Object obj);
public void after(Object obj);
public void afterReturning(Object obj);
public void afterThrowing(Object obj);
}
这里是一个拦截器接口,可以对它创建实现类。我们惊奇的发现这个自定义接口和Spring AOP定义的消息是惊人的相似,不要想的复杂了,这仅仅是一个简单的接口定义,理解它很简单。
对于生成的对象,我们要求使用这样一个类去生成对应的对象:
public class ProxyBeanFactory{
public static <T> T getBean(T obj,Interceptor interceptor){
return (T)ProxyBeanUtil.getBean(obj,interceptor);
}
}
当一个对象通过ProxyBeanFctory的getBean方法后,拥有这样的约定:
[1]Bean必须是一个实现类某个接口的对象
[2]最先会执行拦截器的before方法
[3]其次执行Bean的方法(反射形式)
[4]执行Bean方法时,无论是否产生异常,都会执行before方法
[5]执行Bean方法时,如果不产生异常,则执行afterReturning方法;异常,则执行afterThrowing方法。
这个约定实际上已经十分接近Spring AOP对我们的约定了。流程图:
2、测试
上面我们给出了接口和获取Bean的方式,同时也给出了具体的约定,这个时候我们可根据这个约定编写代码。由于约定服务对象必须实现接口,自定义RoleService接口:
/**
* 角色接口
*/
public interface SysRoleService {
public void insertRole(SysRole sysRole);
}
实现类:
/**
* 角色实现类
*/
public class SysRoleServiceImpl implements SysRoleService{
@Override
public void insertRole(SysRole sysRole) {
System.out.println("id=" + sysRole.getId()
+ ",name=" +sysRole.getName()
+ ",note=" + sysRole.getNote());
}
}
常规的接口和实现类,只是缺少一个角色拦截器。它只需要实现Interceptor接口即可。
/**
* 角色拦截器
*/
public class SysRoleInceptor implements Interceptor{
@Override
public void before(Object obj){
System.out.println("准备打印角色信息....")
}
@Override
public void after(Object obj){
System.out.println("已完成角色信息的打印处理....")
}
@Override
public void afterReturning(Object obj){
System.out.println("刚刚完成角色信息的打印处理,一切正常....")
}
@Override
public void afterThrowing(Object obj){
System.out.println("刚打印功能执行异常了,查看一下角色对象为空了吗....")
}
}
此时,我们可以清醒地直到代码按照流程图的流程执行,不需要关注如何实现,只要直到他们之间的约定即可。
测试约定流程:
/**
* 测试约定流程
*/
public class GameMain{
public static void mian(String[] args){
SysRoleService rolerService = new SysRoleServiceImpl();
Interceptor interceptor = new SysRoleInterceptor();
SysRoleService proxy = ProxyBeanFactory.getBean(roleService, interceptor);
SysRole role = new SysRole(1,"role_name","role_note");
proxy.insert(role);
System.out.println("============测试 afterThrowing 方法============")
role = null;
proxy.insert(role);
}
}
以上基于动态代理模式,它是理解Spring AOP的基础。下面将展示JDK动态代理实现上述流程的。
/**
* 使用动态代理实现流程
*/
public class ProxyBeanUtil implements InvocationHandler{
//被代理对象
private Object obj;
//拦截器
private Interceptor interceptor = null;
/**
* 获取动态代理对象
* @param obj 被代理对象
* @param interceptor 拦截器
* @return 动态代理对象
*/
public static Object getBean(Object obj, Interceptor interceptor){
//使用当前类,作为代理方法,此时被代理对象执行方法时,会进入当前类的invoke方法里
ProxyBeanUtil proxyBeanUtil = new ProxyBeanUtil();
//保存被代理对象
proxyBeanUtil.obj = obj;
//保存拦截器
proxyBeanUtil.interceptor = interceptor;
//生成代理对象,并绑定代理方法
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(), proxyBeanUtil);
}
/**
* 代理方法
* @param proxy 代理对象
* @param method 当前调度方法
* @param args 参数
* @return 方法返回
* @throws Throwable 异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object retObj = null;
//是否产生异常
boolean exceptionFlag = false;
//before方法
interceptor.before(obj);
try {
//反射原有方法
retObj = method.invoke(obj, args);
} catch (Exception e) {
exceptionFlag = true;
} finally {
//after方法
interceptor.after(obj);
}
if (exceptionFlag) {
//afterReturning方法
interceptor.afterReturning(obj);
} else {
//afterReturning方法
interceptor.afterReturning(obj);
}
return retObj;
}
}
分析:
[1] 通过getBean方法保存了被代理对象obj、拦截器interceptor和参数args
[2] 生成JDK动态代理对象proxy,绑定ProxyBeanUtil返回的对象作为其代理类,这样当代理对象调用方法的时候,就会进入到ProxyBeanUtil的invoke方法中,于是焦点又回到了invoke方法上
[3] 在invok方法中,按流程又实现了一遍拦截器的方法,其中设置了异常标志exceptionFlag,通过这个标志能够判断反射原有对象方法的时候是否发生异常,这就是我们的程序能够按照约定规则打印的原因。