前言

之前写了一篇文章Spring3:AOP,是当时学习如何使用Spring AOP的时候写的,比较基础。这篇文章最后的推荐以及回复认为我写的对大家有帮助的评论有很多,但是现在从我个人的角度来看,这篇文章写得并不好,甚至可以说是没有太多实质性的内容,因此这些推荐和评论让我觉得受之有愧。

基于以上原因,更新一篇文章,从最基础的原始代码-->使用设计模式(装饰器模式与代理)-->使用AOP三个层次来讲解一下为什么我们要使用AOP,希望这篇文章可以对网友朋友们有益。

原始代码的写法

既然要通过代码来演示,那必须要有例子,这里我的例子为:

有一个接口Dao有insert、delete、update三个方法,在insert与update被调用的前后,打印调用前的毫秒数与调用后的毫秒数。

首先定义一个Dao接口:

public interface Dao { public void insert();  public void delete();  public void update(); }

然后定义一个实现类DaoImpl:

最原始的写法,我要在调用insert()与update()方法前后分别打印时间,就只能定义一个新的类包一层,在调用insert()方法与update()方法前后分别处理一下,新的类我命名为ServiceImpl,其实现为:

这是最原始的写法,这种写法的缺点也是一目了然:

  1. 方法调用前后输出时间的逻辑无法复用,如果有别的地方要增加这段逻辑就得再写一遍
  2. 如果Dao有其它实现类,那么必须新增一个类去包装该实现类,这将导致类数量不断膨胀

使用装饰器模式

接着我们使用上设计模式,先用装饰器模式,看看能解决多少问题。装饰器模式的核心就是实现Dao接口并持有Dao接口的引用,我将新增的类命名为LogDao,其实现为:

在使用的时候,可以使用"Dao dao = new LogDao(new DaoImpl())"的方式,这种方式的优点为:

  • 透明,对调用方来说,它只知道Dao,而不知道加上了日志功能
  • 类不会无限膨胀,如果Dao的其它实现类需要输出日志,只需要向LogDao的构造函数中传入不同的Dao实现类即可。

不过这种方式同样有明显的缺点,缺点为:

  • 输出日志的逻辑还是无法复用。
  • 输出日志的逻辑与代码有耦合,如果我要对delete()方法前后同样输出时间,需要修改LogDao。

但是,这种做法相比最原始的代码写法,已经有了很大的改进。

使用代理模式

接着我们使用代理模式尝试去实现最原始的功能,使用代理模式,那么我们就要定义一个InvocationHandler,我将它命名为LogInvocationHandler,其实现为:

其调用方式很简单,我写一个main函数:

结果就不演示了,这种方式的优点为:

  • 输出日志的逻辑被复用起来,如果要针对其他接口用上输出日志的逻辑,只要在newProxyInstance的时候的第二个参数增加Class数组中的内容即可。

这种方式的缺点为:

  • JDK提供的动态代理只能针对接口做代理,不能针对类做代理。
  • 代码依然有耦合,如果要对delete方法调用前后打印时间,得在LogInvocationHandler中增加delete方法的判断。

使用CGLIB

接着看一下使用CGLIB的方式,使用CGLIB只需要实现MethodInterceptor接口即可:

代码调用方式为:

使用CGLIB解决了JDK的Proxy无法针对类做代理的问题,但是这里要专门说明一个问题:使用装饰器模式可以说是对使用原生代码的一种改进,使用Java代理可以说是对于使用装饰器模式的一种改进,但是使用CGLIB并不是对于使用Java代理的一种改进。

前面的可以说改进是因为使用装饰器模式比使用原生代码更好,使用Java代理又比使用装饰器模式更好,但是Java代理与CGLIb的对比并不能说改进,因为使用CGLIB并不一定比使用Java代理更好,这两种各有优缺点,像Spring框架就同时支持Java Proxy与CGLIB两种方式。

从目前看来代码又更好了一些,但是我认为还有两个缺点:

  • 无论使用Java代理还是使用CGLIB,编写这部分代码都稍显麻烦。
  • 代码之间的耦合还是没有解决,像要针对delete()方法加上这部分逻辑就必须修改代码。

使用AOP

最后来看一下使用AOP的方式,首先定义一个时间处理类,我将它命名为TimeHandler:

到第8行的代码与第12行的代码分别打印方法开始执行时间与方法结束执行时间。我这里写得稍微复杂点,使用了的写法,其实也可以拆分为与两种,这个看个人喜好。

这里多说一句,切面方法printTime本身可以不用定义任何的参数,但是有些场景下需要获取调用方法的类、方法签名等信息,此时可以在printTime方法中定义JointPoint,Spring会自动将参数注入,可以通过JoinPoint获取调用方法的类、方法签名等信息。

由于这里我用的,要保证方法的调用,这样才能在方法调用前后输出时间,因此不能直接使用JoinPoint,因为JoinPoint没法保证方法调用。此时可以使用ProceedingJoinPoint,ProceedingPointPoint的proceed()方法可以保证方法调用,但是要注意一点,ProceedingJoinPoint只能和搭配,换句话说,如果aop.xml中配置的是,然后printTime的方法参数又是ProceedingJoinPoint的话,Spring容器启动将报错。

接着看一下aop.xml的配置:

我不大会写expression,也懒得去百度了,因此这里就拦截Dao下的所有方法了。测试代码很简单:

结果就不演示了。到此我总结一下使用AOP的几个优点:

  1. 切面的内容可以复用,比如TimeHandler的printTime方法,任何地方需要打印方法执行前的时间与方法执行后的时间,都可以使用TimeHandler的printTime方法
  2. 避免使用Proxy、CGLIB生成代理,这方面的工作全部框架去实现,开发者可以专注于切面内容本身
  3. 代码与代码之间没有耦合,如果拦截的方法有变化修改配置文件即可

下面用一张图来表示一下AOP的作用:

我们传统的编程方式是垂直化的编程,即A-->B-->C-->D这么下去,一个逻辑完毕之后执行另外一段逻辑。但是AOP提供了另外一种思路,它的作用是在业务逻辑不知情(即业务逻辑不需要做任何的改动)的情况下对业务代码的功能进行增强,这种编程思想的使用场景有很多,例如事务提交、方法执行之前的权限检测、日志打印、方法调用事件等等。

AOP使用场景举例

上面的例子纯粹为了演示使用,为了让大家更加理解AOP的作用,这里以实际场景作为例子。

第一个例子,我们知道MyBatis的事务默认是不会自动提交的,因此在编程的时候我们必须在增删改完毕之后调用SqlSession的commit()方法进行事务提交,这非常麻烦,下面利用AOP简单写一段代码帮助我们自动提交事务(这段代码我个人测试过可用):

这种场景下我们要使用的aop标签为,即切在方法调用之后。

这里我做了一个SqlSessionThreadLocalUtil,每次打开会话的时候,都通过SqlSessionThreadLocalUtil把当前会话SqlSession放到ThreadLocal中,看到通过TransactionHandler,可以实现两个功能:

  1. insert、update、delete操作事务自动提交
  2. 对SqlSession进行close(),这样就不需要在业务代码里面关闭会话了,因为有些时候我们写业务代码的时候会忘记关闭SqlSession,这样可能会造成内存句柄的膨胀,因此这部分切面也一并做了

整个过程,业务代码是不知道的,而TransactionHandler的内容可以充分再多处场景下进行复用。

第二个例子是权限控制的例子,不管是从安全角度考虑还是从业务角度考虑,我们在开发一个Web系统的时候不可能所有请求都对所有用户开放,因此这里就需要做一层权限控制了,大家看AOP作用的时候想必也肯定会看到AOP可以做权限控制,这里我就演示一下如何使用AOP做权限控制。我们知道原生的Spring MVC,Java类是实现Controller接口的,基于此,利用AOP做权限控制的大致代码如下(这段代码纯粹就是一段示例,我构建的Maven工程是一个普通的Java工程,因此没有验证过):

毫无疑问这种场景下我们要使用的aop标签为。这里我写得很简单,获取当前用户id与请求路径,根据这两者,判断该用户是否有权限访问该请求,大家明白意思即可。

后记

文章演示了从原生代码到使用AOP的过程,一点一点地介绍了每次演化的优缺点,最后以实际例子分析了AOP可以做什么事情。

之前的那篇AOP入门的文章Spring3:AOP再结合上这篇文章,希望可以真正对网友朋友们有益。

dao接口有什么好处_Java后端精选技术:我们为什么要使用AOP?相关推荐

  1. java后端技术有哪些_Java后端精选技术:什么是JVM?

    说明:做java开发的几乎都知道jvm这个名词,但是由于jvm对实际的简单开发的来说关联的还是不多,一般工作个一两年(当然不包括爱学习的及专门做性能优化的什么的),很少有人能很好的去学习及理解什么是j ...

  2. java后端技术路线_Java后端精选技术:Java的反射机制

    反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制. ...

  3. double类型最大值_Java后端精选基础教程:Java 中的基本数据类型「连载 6」

    数据类型定义了变量可以采用的值,例如,定义变量为 int 类型,则只能取整数值. 在 Java 中有两类数据类型: 1)原始数据类型 2)非原始数据类型 - 数组和字符串是非原始数据类型,将在以后的教 ...

  4. beaninfo详解源码解析 java_Java后端精选技术:源码解析Spring Cloud Zuul

    Zuul 架构图 在zuul中, 整个请求的过程是这样的,首先将请求给zuulservlet处理,zuulservlet中有一个zuulRunner对象,该对象中初始化了RequestContext: ...

  5. java sdi接口是什么意思_JAVA中Action层, Service层 ,modle层 和 Dao层的功能区分

    首先这是现在最基本的分层方式,结合了SSH架构.modle层就是对应的数据库表的实体类.Dao层是使用了Hibernate连接数据库.操作数据库(增删改查).Service层:引用对应的Dao数据库操 ...

  6. 阿里面试题:Mybatis中的Dao接口和XML文件里的SQL是如何建立关系的?

    一.解析XML 首先,Mybatis在初始化SqlSessionFactoryBean的时候,找到mapperLocations路径去解析里面所有的XML文件,这里我们重点关注两部分. 1.创建Sql ...

  7. 实体类dao接口mysql_利用MyBatis生成器自动生成实体类、DAO接口和Mapping映射文件...

    解决问题: 可利用MyBatis生成器自动生成实体类.DAO接口和Mapping映射文件. 测试环境准备: 新建一个mysql数据库,例如mungerzTest. 生成一张主键为自增ID的学生表: C ...

  8. mybatis中getMapper是怎么通过动态代理得到dao接口的实现类并执行mapper文件sql语句的

    提示1:本文需在掌握动态代理基础后浏览,如果动态代理需要回顾可以看我的另一篇博客 提示2:本文以我之前写的工程为模板进行讲解,工程结构及代码可以看我的另一篇博客 1. 说在前头 2. sqlSessi ...

  9. Mybatis工作流程,附带mybatis的mapper文件和config配置文件模板。mapper文件和dao接口的关系——xml中的namespace和sql标签id命名要求。

    1. Mybatis工作流程 1.1 使用MySQL创建数据库girls并生成一个表boys,如下图. 1.2 创建该表对应的简单实体类Boys,如下图. 1.3 创建Dao接口以及和接口同名的map ...

最新文章

  1. java虚拟机内存监控_java虚拟机内存监控工具
  2. Java文件流应用:剪切文件
  3. 实现一个bind函数
  4. labview圆环里实物动画图形_有趣的图形教案
  5. mysql explain字段含义_史上最全的explain常见结果含义分析,值得收藏
  6. Python面向对象编程 __init__方法
  7. 【Python系列】之python2.7.6离线安装Matplotlib
  8. ambari下 hive metastore 启动失败
  9. sublime配置python开发环境_【教程】把Sublime Text 2用作Python的IDE去实现Python的开发...
  10. 博商零售业网上商店系统解决方案
  11. zutuanxue.com-DNS服务器
  12. javashop源码百度云,java电商系统源码分享,Javashop多用户商城源码
  13. fterm 控制台乱码解决
  14. Windows MSDOS的批处理文件命令
  15. RX580 显卡gpu总是乱跳 求解,求解答
  16. Springboot定时任务配置及遇到的问题
  17. java数据类型_Java数据类型
  18. M91A人脸识别布控球机人脸AI视频分析功能
  19. df和du显示的磁盘空间使用情况不一致的原因及处理
  20. 怎么利用LinkedIn快速搜索国外客户资源?

热门文章

  1. RabbitMQ Topic exchange
  2. Cobbler全自动安装CentOS(整理)
  3. 理解HTTP session原理及应用
  4. 读取SBT项目resources目录中的文件
  5. 函数指针数组在ARM异常中断处理中的应用
  6. win7‘上帝模式’
  7. CSP认证201409-1 相邻数对[C++题解]:排序
  8. 通用工具之Pair和Tuple-《C++标准库(第二版)》读书笔记
  9. 李永乐线性代数手写笔记-向量
  10. c++runtime error单调栈