spring 理解Spring AOP 一个简单的约定游戏

佐手、 提交于 2020-03-20 06:38:21
  应该说AOP原理是Spring技术中最难理解的一个部分,而这个约定游戏也许会给你很多的帮助,通过这个约定游戏,就可以理解Spring AOP的含义和实现方法,也能帮助读者更好地运用Spring AOP到实际的编程当中,这对于正确理解Spring AOP是十分重要的

约定规则

  代码清单:定义Interceptor接口
package com.ssm.chapter11.game;

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,你就会发现笔者的定义和Spring AOP定义的消息是如此相近。

  代码清单:ProxyBeanFactory的getBean方法
package com.ssm.chapter11.game;

public class ProxyBeanFactory {
    public static <T> T getBean(T obj, Interceptor interceptor) {
        return (T) ProxyBeanUtil.getBean(obj, interceptor);
    }
}

  具体类ProxyBeanUtil的getBean方法的逻辑不需要去理会,因为这是笔者需要去完成的内容。但是作为读者,你要知道当使用了这个方法后,存在如下约定

  当一个对象通过ProxyBeanFactory的getBean方法定义后,拥有这样的约定。
  (1)Bean必须是一个实现了某一个接口的对象。
  (2)最先会执行拦截器的before方法。
  (3)其次执行Bean的方法(通过反射的形式)。
  (4)执行Bean方法时,无论是否产生异常,都会执行after方法。
  (5)执行Bean方法时,如果不产生异常,则执行afterReturning方法;如果产生异常,则执行afterThrowing方法。
  这个约定实际已经十分接近Spring AOP对我们的约定,所以这个约定十分重要,其流程如图所示。

 



读者的代码

  上面笔者给出了接口和获取Bean的方式,同时也给出了具体的约定,这个时候读者可以根据约定编写代码,比如打印一个角色信息。
  代码清单:RoleService接口
package com.ssm.chapter11.game.service;

import com.ssm.chapter11.game.pojo.Role;

public interface RoleService {
    public void printRole(Role role);
}

 

  代码清单:RoleServiceImpl
package com.ssm.chapter11.game.service.impl;

import com.ssm.chapter11.game.pojo.Role;
import com.ssm.chapter11.game.service.RoleService;

public class RoleServiceImpl implements RoleService {

    // @Override
    public void printRole(Role role) {
        System.out.println("{id =" + role.getId() + ", roleName=" + role.getRoleName() + ", note=" + role.getNote() + "}");
    }

}

 

  代码清单:角色拦截器RoleInterceptor
package com.ssm.chapter11.game.interceptor;

import com.ssm.chapter11.game.Interceptor;

public class RoleInterceptor implements Interceptor {

    // @Override
    public void before(Object obj) {
        System.out.println("--before-准备打印角色信息");
    }

    // @Override
    public void after(Object obj) {
        System.out.println("-after-已经完成角色信息的打印处理");
    }

    // @Override
    public void afterReturning(Object obj) {
        System.out.println("-afterReturning-刚刚完成打印功能,一切正常。");
    }

    // @Override
    public void afterThrowing(Object obj) {
        System.out.println("-afterThrowing-打印功能执行异常了,查看一下角色对象为空了吗?");
    }

}

 


  它编写了图中描述流程的各个方法,这个时候你可以清楚地知道代码将按照流程图的流程执行。注意,你并不需要知道笔者如何实现,你只需要知道我们之间的约定即可
  代码清单:测试约定流程
package com.ssm.chapter11.game.main;

import com.ssm.chapter11.game.Interceptor;
import com.ssm.chapter11.game.ProxyBeanFactory;
import com.ssm.chapter11.game.interceptor.RoleInterceptor;

import com.ssm.chapter11.game.pojo.Role;
import com.ssm.chapter11.game.service.RoleService;
import com.ssm.chapter11.game.service.impl.RoleServiceImpl;

public class GameMain {

    public static void main(String[] args) {
        RoleService roleService = new RoleServiceImpl();
        Interceptor interceptor = new RoleInterceptor();
        RoleService proxy = ProxyBeanFactory.getBean(roleService, interceptor);
        Role role = new Role(1L, "role_name_1", "role_note_1");
        proxy.printRole(role);
        System.out.println("############## 测试 afterthrowing方法###############");
        role = null;
        proxy.printRole(role);
    }

}

 

笔者的代码

  上面的代码都基于动态代理模式。
  下面展示通过JDK动态代理实现上述流程的代码,如代码清单所示。
  代码清单:使用动态代理实现流程
package com.ssm.chapter11.game;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

class ProxyBeanUtil implements InvocationHandler {

    //被代理对象
    private Object obj;
    // 拦截器
    private Interceptor interceptor = null;

    /**
     * 获取动态代理对象.
     *
     * @param obj         被代理对象
     * @param interceptor 拦截器
     * @param aroundFlag  是否启用around方法
     * @return 动态代理对象
     */
    public static Object getBean(Object obj, Interceptor interceptor) {
        //使用当前类,作为代理方法,此时被代理对象执行方法的时候,会进入当前类的invoke方法里
        ProxyBeanUtil _this = new ProxyBeanUtil();
        //保存被代理对象
        _this.obj = obj;
        //保存拦截器
        _this.interceptor = interceptor;
        //生成代理对象,并绑定代理方法
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), _this);
    }

    /**
     * 代理方法
     *
     * @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 ex) {
            exceptionFlag = true;
        } finally {
            //after方法
            interceptor.after(obj);
        }
        if (exceptionFlag) {
            //afterThrowing方法
            interceptor.afterThrowing(obj);
        } else {
            //afterReturning方法
            interceptor.afterReturning(obj);
        }
        return retObj;
    }
}

 

  上面的代码使用了动态代理,由于这段代码的重要性,这里有必要讨论其实现过程。
  首先,通过getBean方法保存了被代理对象、拦截器(inter-ceptor)和参数(args),为之后的调用奠定了基础。然后,生成了JDK动态代理对象(proxy),同时绑定了ProxyBeanUtil的一个实例作为其代理类,这样当代理对象调用方法的时候,就会进入到ProxyBeanUtil实例的invoke方法中,于是焦点又到了invoke方法上。
  在invoke方法中,笔者将拦截器的方法按照流程图实现了一遍,其中设置了异常标志(exceptionFlag),通过这个标志就能判断反射原有对象方法的时候是否发生了异常,这就是读者的代码能够按照流程打印的原因。但是,由于动态代理和反射的代码会比较抽象,更多的时候大部分的框架只会告诉你流程图和具体的流程方法的配置,就像笔者之前只是给出约定而已,相信有心的读者已经明白这句话的意思了,这就是说Spring框架也是这样做的。 
 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!