原文出处:我叫刘半仙

壳叔搞笑时间

正文

为什么会有面向切面编程(AOP)?我们知道Java是一个面向对象(OOP)的语言,但它有一些弊端,比如当我们需要为多个不具有继承关系的对象引入一个公共行为,例如日志、权限验证、事务等功能时,只能在在每个对象里引用公共行为。这样做不便于维护,而且有大量重复代码。AOP的出现弥补了OOP的这点不足。

为了阐述清楚Spring AOP,我们从将以下方面进行讨论:

  1. 代理模式
  2. 静态代理原理及实践
  3. 动态代理原理及实践
  4. Spring AOP原理及实战

1. 代理模式

代理模式:为其他对象提供一种代理以控制对这个对象的访问。这段话比较官方,但我更倾向于用自己的语言理解:比如A对象要做一件事情,在没有代理前,自己来做;在对 A 代理后,由 A 的代理类 B 来做。代理其实是在原实例前后加了一层处理,这也是 AOP 的初级轮廓。

2. 静态代理原理及实践

静态代理模式:静态代理说白了,就是在程序运行前就已经存在代理类的字节码文件、代理类和原始类的关系在运行前就已经确定。废话不多说,我们看一下代码。为了方便阅读,博主把单独的 class 文件合并到接口中,读者可以直接复制代码运行:

package test.staticProxy;
// 接口
public interface IUserDao {void save();void find();
}
//目标对象
class UserDao implements IUserDao{@Overridepublic void save() {System.out.println("模拟:保存用户!");}@Overridepublic void find() {System.out.println("模拟:查询用户");}
}
/**静态代理特点:1. 目标对象必须要实现接口2. 代理对象,要实现与目标对象一样的接口*/
class UserDaoProxy implements IUserDao{// 代理对象,需要维护一个目标对象private IUserDao target = new UserDao();@Overridepublic void save() {System.out.println("代理操作: 开启事务...");target.save();   // 执行目标对象的方法System.out.println("代理操作:提交事务...");}@Overridepublic void find() {target.find();}
}

测试结果:

静态代理虽然保证了业务类只需关注逻辑本身,代理对象的一个接口只服务于一种类型的对象。如果要代理的方法很多,势必要为每一种方法都进行代理。再者,如果增加一个方法,除了实现类需要实现这个方法外,所有的代理类也要实现此方法。增加了代码的维护成本。那么要如何解决呢?答案是使用动态代理。

3. 动态代理原理及实践

动态代理模式:动态代理类的源码是在程序运行期间,通过 JVM 反射等机制动态生成。代理类和委托类的关系是运行时才确定的。实例如下:

package test.dynamicProxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;// 接口
public interface IUserDao {void save();void find();
}//目标对象
class UserDao implements IUserDao{@Overridepublic void save() {System.out.println("模拟: 保存用户!");}@Overridepublic void find() {System.out.println("查询");}
}/*** 动态代理:* 代理工厂,给多个目标对象生成代理对象!**/
class ProxyFactory {// 接收一个目标对象private Object target;public ProxyFactory(Object target) {this.target = target;}// 返回对目标对象(target)代理后的对象(proxy)public Object getProxyInstance() {Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(),  // 目标对象使用的类加载器target.getClass().getInterfaces(),   // 目标对象实现的所有接口new InvocationHandler() {            // 执行代理对象方法时候触发@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {// 获取当前执行的方法的方法名String methodName = method.getName();// 方法返回值Object result = null;if ("find".equals(methodName)) {// 直接调用目标对象方法result = method.invoke(target, args);} else {System.out.println("开启事务...");// 执行目标对象方法result = method.invoke(target, args);System.out.println("提交事务...");}return result;}});return proxy;}
}

测试结果如下:

IUserDao proxy = (IUserDao)new ProxyFactory(target).getProxyInstance();

其实是 JDK 动态生成了一个类去实现接口,隐藏了这个过程:

class $jdkProxy implements IUserDao{}

使用 JDK 生成的动态代理的前提是目标类必须有实现的接口。但这里又引入一个问题,如果某个类没有实现接口,就不能使用 JDK 动态代理。所以 CGLIB 代理就是解决这个问题的。

CGLIB 是以动态生成的子类继承目标的方式实现,在运行期动态的在内存中构建一个子类,如下:

public class UserDao{}//CGLIB 是以动态生成的子类继承目标的方式实现,程序执行时,隐藏了下面的过程
public class $Cglib_Proxy_class  extends UserDao{}

CGLIB 使用的前提是目标类不能为 final 修饰。因为 final 修饰的类不能被继承。

现在,我们可以看看 AOP 的定义:面向切面编程,核心原理是使用动态代理模式在方法执行前后或出现异常时加入相关逻辑

通过定义和前面代码我们可以发现3点:

  • AOP 是基于动态代理模式。
  • AOP 是方法级别的。
  • AOP 可以分离业务代码和关注点代码(重复代码),在执行业务代码时,动态的注入关注点代码。切面就是关注点代码形成的类。

4. Spring AOP

前文提到 JDK 代理和 CGLIB 代理两种动态代理。优秀的 Spring 框架把两种方式在底层都集成了进去,我们无需担心自己去实现动态生成代理。那么,Spring是如何生成代理对象的?

  1. 创建容器对象的时候,根据切入点表达式拦截的类,生成代理对象。
  2. 如果目标对象有实现接口,使用 JDK 代理。如果目标对象没有实现接口,则使用 CGLIB 代理。然后从容器获取代理后的对象,在运行期植入“切面”类的方法。通过查看 Spring 源码,我们在 DefaultAopProxyFactory 类中,找到这样一段话。

简单的从字面意思看出:如果有接口,则使用 JDK 代理,反之使用 CGLIB ,这刚好印证了前文所阐述的内容。Spring AOP 综合两种代理方式的使用前提有会如下结论:如果目标类没有实现接口,且 class 为 final 修饰的,则不能进行 Spring AOP 编程!

知道了原理,现在我们将自己手动实现 Spring 的 AOP:

package test.spring_aop_anno;import org.aspectj.lang.ProceedingJoinPoint;public interface IUserDao {void save();
}// 用于测试 CGLIB 动态代理
class OrderDao {public void save() {//int i =1/0; 用于测试异常通知System.out.println("保存订单...");}
}//用于测试 JDK 动态代理
class UserDao implements IUserDao {public void save() {//int i =1/0; 用于测试异常通知System.out.println("保存用户...");}
}//切面类
class TransactionAop {public void beginTransaction() {System.out.println("[前置通知]  开启事务..");}public void commit() {System.out.println("[后置通知] 提交事务..");}public void afterReturing() {System.out.println("[返回后通知]");}public void afterThrowing() {System.out.println("[异常通知]");}public void arroud(ProceedingJoinPoint pjp) throws Throwable {System.out.println("[环绕前:]");pjp.proceed(); // 执行目标方法System.out.println("[环绕后:]");}
}

Spring的xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><!-- dao实例加入容器 --><bean id="userDao" class="test.spring_aop_anno.UserDao"></bean><!-- dao实例加入容器 --><bean id="orderDao" class="test.spring_aop_anno.OrderDao"></bean><!-- 实例化切面类 --><bean id="transactionAop" class="test.spring_aop_anno.TransactionAop"></bean><!-- Aop相关配置 --><aop:config><!-- 切入点表达式定义 --><aop:pointcut expression="execution(* test.spring_aop_anno.*Dao.*(..))" id="transactionPointcut"/><!-- 切面配置 --><aop:aspect ref="transactionAop"><!-- 【环绕通知】 --><aop:around method="arroud" pointcut-ref="transactionPointcut"/><!-- 【前置通知】 在目标方法之前执行 --><aop:before method="beginTransaction" pointcut-ref="transactionPointcut" /><!-- 【后置通知】 --><aop:after method="commit" pointcut-ref="transactionPointcut"/><!-- 【返回后通知】 --><aop:after-returning method="afterReturing" pointcut-ref="transactionPointcut"/><!-- 异常通知 --><aop:after-throwing method="afterThrowing" pointcut-ref="transactionPointcut"/></aop:aspect></aop:config>
</beans>      

切入点表达式不在这里介绍。参考 Spring AOP 切入点表达式

代码的测试结果如下:

到这里,我们已经全部介绍完Spring AOP。回到开篇的问题,我们拿它做什么?

  1. Spring声明式事务管理配置:请参考博主的另一篇文章:分布式系统架构实战 demo:SSM+Dubbo
  2. Controller层的参数校验:参考Spring AOP拦截Controller做参数校验
  3. 使用 Spring AOP 实现 MySQL 数据库读写分离案例分析
  4. 在执行方法前,判断是否具有权限
  5. 对部分函数的调用进行日志记录:监控部分重要函数,若抛出指定的异常,可以以短信或邮件方式通知相关人员。
  6. 信息过滤,页面转发等等功能

博主一个人的力量有限,只能列举这么多,欢迎评论区对文章做补充。

Spring AOP还能做什么,实现什么魔幻功能,就在于我们每一个平凡而又睿智的程序猿!

About

程序员太辛苦了

请善待你们身边的每一位程序员~

欢迎在评论写下你的程序员趣事,程序员不是一个死板的职业~~

以上内容,均来自互联网~

欢迎扫描二维码加入我们的小组织 ,大家都叫壳叔,期待你的到来。

Group

黑壳家根据地 Q群:200408242

交流群 来者便是客

[转载]Spring AOP是什么?你都拿它做什么?相关推荐

  1. Spring框架面试题:AOP是什么?都用它做什么?

    一:AOP:面向切面编程 核心原理:使用动态代理的设计模式在执行方法前后或出现异常做加入相关逻辑. 二:我们主要使用AOP来做: 1.事务处理:执行方法前:开始事务.执行完成后关闭事务.出现异常后回滚 ...

  2. Spring AOP源码分析(六)Spring AOP配置的背后

    本篇文章主要对Spring AOP配置背后进行了哪些事情做下说明.还是如上类似的工程,在xml中AOP拦截配置如下: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 < ...

  3. Spring AOP实现原理,从代理说起

    前言 为了理解Spring AOP,我们先来了解一下Java的代理模式 什么是代理? 举个例子来说明代理的作用: 假设我们想邀请一位明星,那么并不是直接联系明星,而是联系明星的经纪人,来达到同样的目的 ...

  4. 我用 Spring AOP 干掉了一摞简历

    如果说 Java 工程师,有什么一定要"死磕"拿下的东西,那一定是 Spring 无疑了. 众所周知,Spring 无论在 Java 生态系统,还是在就业市场,是绝对的王者.Spr ...

  5. 从诸葛亮博望坡火烧曹军谈Spring AOP 的五种使用方式

    曹操自剿黄巾,讨董作,擒吕布,灭袁绍后,队伍达到鼎盛期,拥有兵将100余万,为了统一全国,派手下将领夏候淳领兵十万攻打新野.时当秋月,秋风徐起,夏侯惇引兵至博望坡,新野危在旦夕. 刘备请军师诸葛亮对策 ...

  6. Spring AOP详解(转载)所需要的包

    上一篇文章中,<Spring Aop详解(转载)>里的代码都可以运行,只是包比较多,中间缺少了几个相应的包,根据报错,几经百度搜索,终于补全了所有包. 截图如下: 在主测试类里面,有人怀疑 ...

  7. 基于注解的Spring AOP的配置和使用--转载

    AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向切面编程.可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术. ...

  8. 转载:Spring AOP (下)

    昨天记录了Spring AOP学习的一部分(http://www.cnblogs.com/yanbincn/archive/2012/08/13/2635413.html),本来是想一口气梳理完的.但 ...

  9. [转载]使用SPRING AOP框架和EJB组件

    使用SPRING AOP框架和EJB组件 摘要 快速发展的开发人员社区.对各种后端技术(包括JMS.JTA.JDO. Hibernate.iBATIS等等)的支持,以及(更为重要的)非侵入性的轻量级I ...

最新文章

  1. Activity启动流程:Hook实现启动未注册Activity
  2. C#引用C++ Dll 所有類型轉換的方式(转)
  3. spring boot 之session的总结
  4. Redis作者摊上事了:多人要求修改Redis主从复制术语master/slave
  5. ubuntu 15.10下cmake 的安装
  6. 五大板块(5)——字符串
  7. linux shell脚本备份mysql数据库
  8. python设计模式8-组合模式
  9. Linux多线程服务端编程:使用muduo C++网络库
  10. linux 运行 ccs,ccs_linux
  11. 西门子PLC入门-PLC介绍
  12. MySQL安装及使用手册
  13. 实现单片机通过传感器获取信息,并且将信息通过wifi模块发送信息给PC端,并在pyqt5界面上显示(PC端部分)
  14. POI和Java Excel Api导入导出----详细到你不敢相信
  15. 服务化系统容量评估和性能保障
  16. 谷歌浏览器f12功能修改服务器代码,Chrome(谷歌)控制台,console实用教程
  17. 快速排序算法原理 Quicksort —— 图解(精讲) JAVA
  18. mac无线网连上没网络连接网络连接服务器,无线网络连接上但上不了网
  19. HTML目前最新版本,HTML5最新版本介绍
  20. 2020行业信息化竞争力百强发布!

热门文章

  1. 前端收藏夹 ,以及他们的github地址:GitHub - w3ctrain/w3ctrain.github.io: w3ctrian前端收藏夹
  2. canvas 水滴图、液体进度条、仿加速球、圆球水波图
  3. [声学测试学习1]声学相关基础知识
  4. C# winform中对gridcontrol查询的一些操作
  5. NEWS|药物发现公司正在定制ChatGPT:方法如下
  6. ECO-PASSPOET认证辅导,Eco-Passport证书有效期为多久,到期是否需要重新缴纳费用
  7. 网赚灰产不归人——雅贼归来(上)
  8. 饿了么ui elementui 浏览器日志报错的检查思路
  9. 用C/C++编程实现挖金子游戏「含项目源码」
  10. 【开源】电子围栏-测距离-测面积-拉框放大-实时路况-逆地理编码的实现