Spring AOP潜入易懂的讲解

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

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

1.代理模式。

2.静态代理原理及实践。

3.动态代理原理及实践。

4.Spring AOP原理及实战。

1.代理模式

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

2.静态代理原理及实践

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

 1 package test.staticProxy;
 2 // 接口
 3 public interface IUserDao {
 4  void save();
 5  void find();
 6 }
 7 //目标对象
 8 class UserDao implements IUserDao{
 9  @Override
10  public void save() {
11    System.out.println("模拟:保存用户!");
12  }
13  @Override
14  public void find() {
15    System.out.println("模拟:查询用户");
16  }
17 }
18 /**
19    静态代理
20          特点:
21  1. 目标对象必须要实现接口
22  2. 代理对象,要实现与目标对象一样的接口
23 */
24 class UserDaoProxy implements IUserDao{
25  // 代理对象,需要维护一个目标对象
26  private IUserDao target = new UserDao();
27  @Override
28  public void save() {
29    System.out.println("代理操作: 开启事务...");
30    target.save();   // 执行目标对象的方法
31    System.out.println("代理操作:提交事务...");
32  }
33  @Override
34  public void find() {
35    target.find();
36  }
37 }

测试结果:

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

3.动态代理原理及实践

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

 1 package test.dynamicProxy;
 2
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 import java.lang.reflect.Proxy;
 6 // 接口
 7 public interface IUserDao {
 8  void save();
 9  void find();
10 }
11 //目标对象
12 class UserDao implements IUserDao{
13  @Override
14  public void save() {
15    System.out.println("模拟: 保存用户!");
16  }
17  @Override
18  public void find() {
19    System.out.println("查询");
20  }
21 }
22 /**
23 * 动态代理:
24 *    代理工厂,给多个目标对象生成代理对象!
25 *
26 */
27 class ProxyFactory {
28  // 接收一个目标对象
29  private Object target;
30  public ProxyFactory(Object target) {
31    this.target = target;
32  }
33  // 返回对目标对象(target)代理后的对象(proxy)
34  public Object getProxyInstance() {
35    Object proxy = Proxy.newProxyInstance(
36      target.getClass().getClassLoader(),  // 目标对象使用的类加载器
37      target.getClass().getInterfaces(),   // 目标对象实现的所有接口
38      new InvocationHandler() {      // 执行代理对象方法时候触发
39        @Override
40        public Object invoke(Object proxy, Method method, Object[] args)
41            throws Throwable {
42
43          // 获取当前执行的方法的方法名
44          String methodName = method.getName();
45          // 方法返回值
46          Object result = ;
47          if ("find".equals(methodName)) {
48            // 直接调用目标对象方法
49            result = method.invoke(target, args);
50          } else {
51            System.out.println("开启事务...");
52            // 执行目标对象方法
53            result = method.invoke(target, args);
54            System.out.println("提交事务...");
55          }
56          return result;
57        }
58      }
59    );
60    return proxy;
61  }
62 }

测试结果如下:

在运行测试类中,创建测试类对象代码中: 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点:

1.AOP是基于动态代理模式。

2.AOP是方法级别的(要测试的方法不能为static修饰,因为接口中不能存在静态方法,编译就会报错)。

3.AOP可以分离业务代码和关注点代码(重复代码),在执行业务代码时,动态的注入关注点代码。切面就是关注点代码形成的类。

4.spring AOP原理及实战

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

1.创建容器对象的时候,根据切入点表达式拦截的类,生成代理对象。

2.如果目标对象有实现接口,使用jdk代理。如果目标对象没有实现接口,则使用Cglib代理。然后从容器获取代理后的对象,在运行期植入"切面"类的方法。通过查看Spring源码,我们在DefaultAopProxyFactory类中,找到这样一段话。

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

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

 1 package test.spring_aop_anno;
 2
 3 import org.aspectj.lang.ProceedingJoinPoint;
 4
 5 public interface IUserDao {
 6  void save();
 7 }
 8 //用于测试Cglib动态代理
 9 class OrderDao {
10  public void save() {
11    //int i =1/0;用于测试异常通知
12    System.out.println("保存订单...");
13  }
14 }
15 //用于测试jdk动态代理
16 class UserDao implements IUserDao {
17  public void save() {
18    //int i =1/0;用于测试异常通知
19    System.out.println("保存用户...");
20  }
21 }
22 //切面类
23 class TransactionAop {
24  public void beginTransaction() {
25    System.out.println("[前置通知]  开启事务..");
26  }
27  public void commit() {
28    System.out.println("[后置通知] 提交事务..");
29  }
30  public void afterReturing(){
31    System.out.println("[返回后通知]");
32  }
33  public void afterThrowing(){
34    System.out.println("[异常通知]");
35  }
36  public void arroud(ProceedingJoinPoint pjp) throws Throwable{
37    System.out.println("[环绕前:]");
38    pjp.proceed();             // 执行目标方法
39    System.out.println("[环绕后:]");
40  }
41 }

Spring的xml配置文件:

 1 xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4    xmlns:context="http://www.springframework.org/schema/context"
 5    xmlns:aop="http://www.springframework.org/schema/aop"
 6    xsi:schemaLocation="
 7        http://www.springframework.org/schema/beans
 8        http://www.springframework.org/schema/beans/spring-beans.xsd
 9        http://www.springframework.org/schema/context
10        http://www.springframework.org/schema/context/spring-context.xsd
11        http://www.springframework.org/schema/aop
12        http://www.springframework.org/schema/aop/spring-aop.xsd">
13
14  <bean id="userDao" class="test.spring_aop_anno.UserDao">bean>
15
16
17  <bean id="orderDao" class="test.spring_aop_anno.OrderDao">bean>
18
19
20  <bean id="transactionAop" class="test.spring_aop_anno.TransactionAop">bean>
21
22
23  <aop:config>
24
25    <aop:pointcut expression="execution(* test.spring_aop_anno.*Dao.*(..))" id="transactionPointcut"/>
26
27    <aop:aspect ref="transactionAop">
28
29      <aop:around method="arroud" pointcut-ref="transactionPointcut"/>
30
31      <aop:before method="beginTransaction" pointcut-ref="transactionPointcut" />
32
33      <aop:after method="commit" pointcut-ref="transactionPointcut"/>
34
35      <aop:after-returning method="afterReturing" pointcut-ref="transactionPointcut"/>
36
37      <aop:after-throwing method="afterThrowing" pointcut-ref="transactionPointcut"/>
38    aop:aspect>
39  aop:config>
40 beans>

切入点表达式不在这里介绍。ref:Spring AOP 切入点表达式:http://blog.csdn.net/keda8997110/article/details/50747923

代码的测试结果如下:

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

1.Spring声明式事务管理配置。

2.Controller层的参数校验。

3.使用Spring AOP实现MySQL数据库读写分离案例分析

4.在执行方法前,判断是否具有权限。

5.对部分函数的调用进行日志记录。监控部分重要函数,若抛出指定的异常,可以以短信或邮件方式通知相关人员。

6.信息过滤,页面转发等等功能,博主一个人的力量有限,只能列举这么多,欢迎评论区对文章做补充。

原文:my.oschina.net/liughDevelop/blog/1457097

原文地址:https://www.cnblogs.com/aoshicangqiong/p/8922408.html

时间: 2024-10-14 00:57:21

Spring AOP潜入易懂的讲解的相关文章

Spring AOP前置通知实例讲解与AOP详细解析

一.引出问题 有个接口TestServiceInter,有两个实现方法TestService和Test2Service.他们都有sayHello():我们的需求是在调用这两个方法之前,要先完成写日志的功能:二.菜鸟的想法 我在各个实现类的sayHello()方法里面写上写日志的功能就是了. 这样实现存在的问题:代码冗余.当所有实现类都要加上日志功能的时候,需要写很多重复代码三.利用AOP前置通知实现此功能3.1 第一步:我们需要定义一个接口 package com.jdc.aop; /** @a

Spring AOP的本质

不用再百科什么AOP了,我先推荐几篇文章或者系列文章:(感谢这些博文的原作者) 0.  Spring AOP 详解   http://pandonix.iteye.com/blog/336873/ 1.  AOP技术基础系列     http://wayfarer.cnblogs.com/articles/241024.html 2.  我对AOP的理解 http://jinnianshilongnian.iteye.com/blog/1474325 3.  Spring AOP本质系列  ht

Spring AOP概述

编程语言的终极目标就是能以更自然.更灵活的方式模拟世界,从原始机器语言到过程语言再到面向对象语言,编程语言一步步地用更自然.更灵活的方式编写软件.AOP 是软件开发思想发展到一定阶段的产物,但 AOP 的出现并不是要完全替代 OOP,而仅作为 OOP 的有益补充.虽然 AOP 作为一项编程技术已经有多年的历史,但长时间停留在学术领域,直到近几年,AOP 才作为一项真正的实用技术在应用领域开疆拓土.需要指出的是,AOP 是有特定的应用场合的,它只适合那些具有横切逻辑的应用场合,如性能监测.访问控制

Spring Aop实例之AspectJ注解配置

http://blog.csdn.net/xiaoxian8023/article/details/17285809 上篇博文<Spring Aop实例之xml配置>中,讲解了xml配置方式,今天来说说AspectJ注解方式去配置spring aop. 依旧采用的jdk代理,接口和实现类代码请参考上篇博文.主要是将Aspect类分享一下: [java] view plaincopy package com.tgb.aop; import org.aspectj.lang.JoinPoint;

Spring AOP四种实现方式Demo详解与相关知识探究

一.前言 在网络上看到一篇博客Spring实现AOP的4种方式,博主写的很通俗易懂,但排版实在抓狂,对于我这么一个对排版.代码格式有强迫症的人来说,实在是不能忍受~~~~(>_<)~~~~. 我亲手实现了一遍,重新整理,加上了一些不易关注到的细节.漏掉的知识,以及自己对AOP的一些理解,写成这篇博客. 二.AOP相关概念 (1)AOP是什么?AOP与拦截器的区别? 太抽象的不说,如果你知道Struts2的拦截器,拦截器就是应用的AOP的思想,它用于拦截Action以进行一些预处理或结果处理.而

死磕Spring AOP系列4:剖析AOP schema方式原理

这个是<死磕Spring AOP系列>第4个.已经讲过的内容 死磕Spring AOP系列3:剖析Bean处理器之DefaultAdvisorAutoProxyCreator 死磕Spring AOP系列2:剖析Bean处理器之BeanNameAutoProxyCreator 死磕Spring AOP系列1:编程式实现AOP 通过前3篇,大家应该可以清楚的知道:AOP代理原理有3元素 BeanPostProcessor,作为代理对象初始入口 Advisor&Pointcut&M

spring aop详解

AOP AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善.OOP引入封装.继承.多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合.不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能.日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性.异常处理和透明的持续性也都是如此,这种散布在各

死磕Spring AOP系列1:编程式实现AOP

作为[死磕Spring AOP]系列的第一篇, 这个系列是AOP源码分析级别的文章.由于现在AOP已经不是什么高深的技术,网上的例子也比比皆是,不论是xml schema,还是annotation声明式.相信用过Spring的朋友,都可以信手拈来. 本系列文章的原则 如何配置AOP不是重点 AOP相关概念讲解不是重点 AOP 底层代码设计才是重点 本篇的主要内容 认识ProxyFactory,并通过该工厂类,将"日志"和"安全校验"代码切入到业务逻辑中 分析代理对象

死磕Spring AOP系列2:剖析Bean处理器之BeanNameAutoProxyCreator

通过前一篇<死磕Spring AOP系列1:编程式实现AOP>,学习了Spring对代理的底层支持,认识了ProxyFactory对象,及从类设计层面认识了PointCut&Advisor&Advice&Interceptor,还认识了AdvisorChainFactory对象,知道了底层Advisor的底层链式结构.但是,上篇我们仅仅是通过Spring编程式实现的"AOP"效果,这种方式,实际开发时,如果这样用就太LOW了.今天,主要认识一个生成代