应该说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框架也是这样做的。 

 

spring 理解Spring AOP 一个简单的约定游戏的更多相关文章

  1. spring boot: @Entity @Repository一个简单的数据读存储读取

    spring boot: @Entity @Repository一个简单的数据读存储读取 创建了一个实体类. 如何持久化呢?1.使用@Entity进行实体类的持久化操作,当JPA检测到我们的实体类当中 ...

  2. 无聊的人用JS实现了一个简单的打地鼠游戏

    直入正题,用JS实现一个简单的打地鼠游戏 因为功能比较简单就直接裸奔JS了,先看看效果图,或者 在线玩玩 吧 如果点击颜色比较深的那个(俗称坏老鼠),将扣分50:如果点击颜色比较浅的那个(俗称好老鼠) ...

  3. Unity 2D游戏开发高速入门第1章创建一个简单的2D游戏

    Unity 2D游戏开发高速入门第1章创建一个简单的2D游戏 即使是如今,非常多初学游戏开发的同学.在谈到Unity的时候.依旧会觉得Unity仅仅能用于制作3D游戏的. 实际上.Unity在2013 ...

  4. 【模块化编程】理解requireJS-实现一个简单的模块加载器

    在前文中我们不止一次强调过模块化编程的重要性,以及其可以解决的问题: ① 解决单文件变量命名冲突问题 ② 解决前端多人协作问题 ③ 解决文件依赖问题 ④ 按需加载(这个说法其实很假了) ⑤ ..... ...

  5. 理解与模拟一个简单web服务器

    先简单说下几个概念,根据自己的理解,不正确请见谅. web服务器 首先要知道什么是web服务器,简单说web服务器就是可以使用HTTP传输协议与客户端进行通信的服务器.最初的web服务器只能用来处理静 ...

  6. 通过创建一个简单的骰子游戏来探究 Python

    在我的这系列的第一篇文章 中, 我已经讲解如何使用 Python 创建一个简单的.基于文本的骰子游戏.这次,我将展示如何使用 Python 模块 Pygame 来创建一个图形化游戏.它将需要几篇文章才 ...

  7. Spring IOC DI AOP 的简单理解及应用

    Spring两大特性:IOC 和AOP.IOC 控制反转,AOP 面向切面编程 spring 核心容器的主要组件时Bean工厂(BeanFactory) ,Bean 工厂使用控制反转模式来降低程序代码 ...

  8. spring boot:创建一个简单的web(maven web project)

    1.新建一个maven web project; 2.在pom.xml文件中添加相应的依赖包: 3.新建一个HelloController请求控制类: 4.编写index.jsp页面: 5.编写启动类 ...

  9. Spring学习笔记--声明一个简单的Bean

    spring依赖的maven dependencyhttp://mvnrepository.com/artifact/org.springframework 在pom.xml中添加如下依赖: < ...

随机推荐

  1. mysql跨表删除多条记录

    Mysql可以在一个sql语句中同时删除多表记录,也可以根据多个表之间的关系来删除某一个表中的记录. 假定我们有两张表:Product表和ProductPrice表.前者存在Product的基本信息, ...

  2. Java - 框架之 Hibernate

    一:hibernate.cfg.xml 配置 <!-- 1.配置数据库连接的4个参数 --> <property name="hibernate.connection.dr ...

  3. ElementUI入门和NodeJS环境搭建

    1. ElementUI简介 我们学习VUE,知道它的核心思想式组件和数据驱动,但是每一个组件都需要自己编写模板,样式,添加事件,数据等是非常麻烦的,   所以饿了吗推出了基于VUE2.0的组件库,它 ...

  4. C++问题--error LNK2019: 无法解析的外部符号 __imp__wsprintfW

    一.问题 当编译运行C++连接Redis时,出现错误Win32_Interop_d.lib(Win32_ANSI.obj) : error LNK2019: 无法解析的外部符号 __imp__wspr ...

  5. cgp的辣鸡比赛题解

    目录 cgp的gcd 题目链接 思路 代码 cgp调戏妹子 题目链接 思路 代码 cgp的序列 题目链接 思路 代码 cgp的背包 题目链接 思路 代码 cgp的gcd 题目链接 传送门 思路 首先看 ...

  6. 读取中文文件到CString

      CString strFileName = _T("D:\\ai\\100.json"); CFile file; file.Open(strFileName, CFile:: ...

  7. CF1217题解

    E 也不知道为啥这题咕了好久~ 有一个明显的结论:如果存在有一位有两个数该为不为0,则这两个数可以组成一个满足条件的解 每一位分别维护不为0的和最小的即可

  8. 【洛谷】P3518 [POI2011]SEJ-Strongbox

    题目描述 有一个密码箱,0到n-1中的某些整数是它的密码. 且满足,如果a和b都是它的密码,那么(a+b)%n也是它的密码(a,b可以相等) 某人试了k次密码,前k-1次都失败了,最后一次成功了. 问 ...

  9. Linux 的crond 任务调度

    一.原理示意图 二.概述 任务调度:是指系统在某个时间执行的特定的命令或程序 任务调度分类: 1.系统工作:有些重要的工作周而复始的执行.如病毒扫描等 2.个别用户工作:个别用户可能希望执行程序,比如 ...

  10. bugku web所有writeup_超详细讲解_持续更新

    首先说一下我的主用工具,在windows下,主要是用这些,用到其他特定的工具会在题里说. 0.浏览器:火狐,配合Max hackbar插件 (这个是免费的) 1.抓包改包:burpsuite.http ...