(7)Spring框架----面向切面编程(一个简单的约定游戏)

时间:2024-04-07 12:27:44

如果说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对我们的约定了。流程图:

(7)Spring框架----面向切面编程(一个简单的约定游戏)

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,通过这个标志能够判断反射原有对象方法的时候是否发生异常,这就是我们的程序能够按照约定规则打印的原因。