AOP

引言

Spring 为解耦而生,其中AOP(面向切面编程)是很浓重的一笔。AOP(Aspect-Oriented-Programming)指一种程序设计范型,该范型以一种称为切面(aspect)的语言构造为基础,切面是一种新的模块化机制,用来描述分散在对象、类或方法中的横切关注点(crosscutting)。

AOP相关概念

  • 连接点(JoinPoint):程序执行到某个特定位置(如:某个方法调用前,调用后,方法抛出异常),一个类或一段程序代码拥有一些具有边界性质的特定点,这些代码中的特定点就是连接点。
  • 切点(PointCut):如果连接点相当于数据中的记录,那么切点就相当于查询条件,一个切点可以匹配多个连接点。SpringAop的规则解析引擎负责解析切点所设定的查询条件,找到对应的连接点。
  • 增强(Advice):增强是织入到目标类连接点上的一段程序代码,Spring提供的增强接口都是带方位的,如BeforeAdvice、AfterReturningAdvice、ThrowsAdvice等。,很多资料上将增强译为“通知”,这明显是词不达意的。

说明:Advice在国内很多的书面资料上都被翻译为“通知”,但这个翻译无法表达其本质,有少量的读物将这个词翻译为“增强”,这个翻译是比较为准确的诠释,我们通过AOP将横切关注功能加到原有的业务逻辑上,这就是对原有的业务逻辑进行的一种增强,这个增强可以是前置增强,后置增强,返回后增强,抛出异常时增强,和包围型增强。

  • 引介(Introduction):引介是一种特殊的增强,他为类添加一些属性和方法,这样,即使一个业务类原本没有实现某个接口,通过引介功能,可以动态的为该业务类添加接口的实现逻辑,让业务类称为这个接口的实现类
  • 织入(Weaving):织入是将增强添加到目标类具体连接点上的过程,AOP由三种织入方式:1 编译期织入:需要特殊的Java编译期(例如Aspect的ajc);2 装载期织入:要求使用特殊的类加载器,在装载类的时候对类进行增强,3 运行时织入:在运行时为目标类生成代理实现增强。Spring采用动态代理的方式事实现了运行时织入,而AspectJ采用了编译期织入和装载期织入的方式。
  • 切面(Aspect):切面是由切点和增强(引介)组成的,它包括了对横切关注功能的定义,也包括了对连接点的定义。

原理

AOP是运用了动态代理。代理模式是GoF提出的23种设计模式中最为经典的模式之一,代理是对象的结构模式,他给某一个对象提供一个对象代理,并由代理控制对原对象的引用,简单的说,代理对象可以完成比原对象更多的职责,当需要为原对象添加横切关注功能时,就可以使用原对象的代理对象。我们在打开Office系列的Word文档时,如果文档中有插图,当文档刚加载时,文档中的插图都只是一个虚框的占位符,等真正用户翻到某一页要查看该图片时,才会真正加载这张图片,这其实就是代理模式的使用,代替真正的虚框就是一个虚拟代理,Hibernate的load方法也是返回一个虚拟代理对象,等真正用户需要访问对象的属性时,才向数据库发出SQL语句。

按照代理创建初期,代理可以分为两种:

  • 静态代理:由程序员创建或特定工具自动生成源代码,再对其编译,在程序运行前,代理类的.class文件就已经存在了。
  • 动态代理:在程序运行时,运行反射机制动态创建而成 另外还有CGLib动态代理,它是针对类实现的代理。

实现

JDK代理

静态代理

下面用一个找枪手代考的例子演示代理模式的使用

package com.test.test2.interfaces;/*** @Author: Young* @QQ: 403353323* @Date: 2019/4/25 10:35*/
public interface Candidate {public void answerTheQuestion();
}复制代码
package com.test.test2;import com.test.test2.interfaces.Candidate;/*** @Author: Young* @QQ: 403353323* @Date: 2019/4/25 10:36*/
public class LazyStudent implements Candidate {private String name;public LazyStudent(String name) {this.name = name;}@Overridepublic void answerTheQuestion() {// 懒学生只能写出自己名字不会答题System.out.println("姓名" + name);}
}复制代码
package com.test.test2;import com.test.test2.interfaces.Candidate;/*** @Author: Young* @QQ: 403353323* @Date: 2019/4/25 10:40*/
public class Gunman implements Candidate {private Candidate target;  //被代理的对象public Gunman(Candidate target) {this.target = target;}@Overridepublic void answerTheQuestion() {// 抢手要写上代考学生的名字target.answerTheQuestion();// 抢手帮助懒学生答题并交卷System.out.println("奋笔疾书书写正确答案");System.out.println("交卷");}
}复制代码
@Test
public void testGunman(){Candidate c = new Gunman(new LazyStudent("王小二"));c.answerTheQuestion();
}
复制代码

观察代理可以发现每一个代理类只能为一个接口服务,这样一来程序开发中必然会产生很多的代理,而且,所有代理除了调用方法不一样之外,其他操作都一样,则此时肯定是重复代码,解决这一个问题最好的做法是通过一个代理类完成全部的代理功能,那么此时就必须使用动态代理了。

动态代理

从JDK 1.3 开始,Java提供了动态技术,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为反射机制可以生成任意类型的动态代理。

java.lang.reflect包中的Proxy类和InvocationHandler接口提供了生成动态代理的能力。

JDK动态代理中包含一个接口和一个类:

  • InvocationHandler接口
public interface InvocationHandler {public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
}
复制代码

参数说明:

参数 说明
Object proxy 指被代理的对象
Method method 要调用的方法
Object[] args 方法调用时的参数

可以将InvocationHandler接口的子类想象成一个代理的最终操作类,替换掉ProxySubject。

  • Proxy类:
  • Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态生成实现类,此类提供了如下的操作方法
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{/***************具体内容略去****************/}
复制代码

参数说明:

源码中的参数说明

* @param   loader the class loader to define the proxy class
* @param   interfaces the list of interfaces for the proxy class to implement
* @param   h the invocation handler to dispatch method invocations to
复制代码
参数 说明
ClassLoader 类加载器
Class<?>[] interfaces 得到全部的接口
InvocationHandler h 得到InvocationHandler接口的子类实例

Ps:在Proxy中类的newProxyInstance()方法中需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器。在Java中主要有三种类加载器:

  • Bootstrap ClassLoader:此口加载器采用C++编写,一般开发中是看不到的。
  • Extendsion ClassLoader:用来进行扩展类的加载器,一般对应的是jre\lib\ext目录中的类;
  • APPClassLoader:(默认)加载classpath制定的类,是最常使用的一种加载器。

BookFacade.java

public interface BookFacade {   public void addBook();
}
复制代码

BookFacadeImpl.java

import net.battier.dao.BookFacade;  public class BookFacadeImpl implements BookFacade {  @Override  public void addBook() {  System.out.println("增加图书方法。。。");  }
}
复制代码

BookFacadeProxy.java

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/** * JDK动态代理代理类 */
public class BookFacadeProxy implements InvocationHandler {  private Object target;  /** * 绑定委托对象并返回一个代理类 * @param target * @return */  public Object bind(Object target) {  this.target = target;  //取得代理对象  return Proxy.newProxyInstance(target.getClass().getClassLoader(),  target.getClass().getInterfaces(), this);   //要绑定接口(这是一个缺陷,cglib弥补了这一缺陷)  }  @Override  /** * 调用方法 */  public Object invoke(Object proxy, Method method, Object[] args)  throws Throwable {  Object result=null;  System.out.println("事物开始");  //执行方法  result=method.invoke(target, args);  System.out.println("事物结束");  return result;  }
}
复制代码

TestProxy.java

import net.battier.dao.BookFacade;
import net.battier.dao.impl.BookFacadeImpl;
import net.battier.proxy.BookFacadeProxy;  public class TestProxy {  public static void main(String[] args) {  BookFacadeProxy proxy = new BookFacadeProxy();  BookFacade bookProxy = (BookFacade) proxy.bind(new BookFacadeImpl());  bookProxy.addBook();  }
}
复制代码

说明:使用Java的动态代理有一个局限性就是被代理的类必须实现接口(也就是说只能对该类所实现接口中定义的方法进行代理),虽然面向接口编程是每个优秀的Java程序员都知道的规则,但现实往往不尽人意,对于没有实现接口的代理类如何为其生成代理类呢?继承继承是最经典的扩展已有代码能力的手段。CGLib代理就是这样实现代理。

CGLib动态代理

CGLib采用非常底层的字节码生成技术,通过为一个类创建子类来生成代理类,它弥补了Java动态代理的不足,因此,Spring中的动态代理和CGLib都是创建代理的重要手段,对于实现了接口的类就用动态代理为其生成代理类,而没有实现接口的类就用CGLib通过继承方式为其创建代理类。但因为采用的是继承,所以不能对final修饰的类进行代理。

intercept参数列表:

参数 说明
Object obj 代理的对象
Metho method 表示拦截的方法
Object[] args 数组表示参数列表,基本数据类型需要传入其包装类型,如int-->Integer、long-Long、double-->Double
MethodProxy methodProxy 表示对方法的代理,invokeSuper方法表示对被代理对象方法的调用
package com.test.test2.proxy;/*** @Author: Young* @QQ: 403353323* @Date: 2019/5/3 21:32*/
public class BookFacadeImpl1 {public void addBook(){System.out.println("增加图书的普通方法");}
}
复制代码
package com.test.test2.proxy;import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;/*** @Author: Young* @QQ: 403353323* @Date: 2019/5/3 21:34*//*** CGLib创建动态代理*/
public class BookFacadeProxy implements MethodInterceptor {private Object target;public Object getInstance(Object target){this.target = target;Enhancer enhancer = new Enhancer();enhancer.setSuperclass(this.target.getClass());// 回调方法enhancer.setCallback(this);//创建代理对象return enhancer.create();}@Override// 回调方法  public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("开始事务");methodProxy.invokeSuper(obj, args);System.out.println("事务结束");return null;}
}复制代码
public class TestCglib {  public static void main(String[] args) {  BookFacadeCglib cglib=new BookFacadeCglib();  BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(new BookFacadeImpl1());  bookCglib.addBook();  }
}
复制代码
参考

AOP实现原理
Java面试知识点总结及解析
java动态代理(JDK和cglib)
Cglib及其基本使用

AOP概述及实现原理相关推荐

  1. Spring AOP概述及底层实现原理

    Spring AOP概述及底层实现原理 aop概述 AOP全称为Aspect Oriented Programming的缩写,意为:面向切面编程.将程序中公用代码进行抽离,通过动态代理实现程序功能的统 ...

  2. 底层实现_Java AOP的底层实现原理

    AOP用于处理系统中分布于各个模块的横切关注点,比如事务管理.日志.缓存等. AOP实现的关键,在于AOP框架自动创建的AOP代理,AOP代理主要分为静态代理和动态代理,静态代理的代表为AspectJ ...

  3. VLAN、Trunk概述和三层交换机原理

    VLAN.Trunk概述和三层交换机原理 一.VLAN概述 1.1VLAN的概念及优势 1.2静态VLAN 1.3静态VLAN的配置 二.VLAN Trunk 2.1Trunk 概述 2.2 Trun ...

  4. ISP(图像信号处理)算法概述、工作原理、架构、处理流程

    目录 ISP的主要内部构成:ISP内部包含 CPU.SUP IP(各种功能模块的通称).IF 等设备 ISP的控制结构:1.ISP逻辑    2.运行在其上的firmware ISP上的Firmwar ...

  5. 第30课时_电力MOSFET开关概述及工作原理

    第30课时_电力MOSFET开关概述及工作原理 MOSFET的工作原理 正向阻断 反向导电 形成反型层 导电沟道形成

  6. Animation动画概述和执行原理

    动画入门和进阶文章列表: Animation动画概述和执行原理 Android动画之补间动画TweenAnimation Android动画之逐帧动画FrameAnimation Android动画之 ...

  7. PHP之AOP面向切面编程原理(2)

    在传统的编写业务逻辑处理代码时,我们通常会习惯性地做几件事情:日志记录.事务控制及权限控制等,然后才是编写核心的业务逻辑处理代码.当代码编写完成回头再看时,不禁发现,扬扬洒洒上百行代码中,真正用于核心 ...

  8. ACL 概述及工作原理

    ACL 概述及工作原理 1.ACL访问控制列表 ACL的两种作用: 用来对数据包做访问控制(丢弃或者放行) 结合其他协议,用来匹配范围 访问控制列表 读取第三层,第四层包头信息 根据预先定义好的规则对 ...

  9. 《Spring设计思想》AOP设计思想与原理(图文并茂)

    Spring 提供了AOP(Aspect Oriented Programming) 的支持, 那么,什么是AOP呢?本文将通过一个另外一个角度来诠释AOP的概念,帮助你更好地理解和使用Spring ...

最新文章

  1. MYSQL二级表的管理_MySQL分区表的管理~2
  2. 拿下“[warn] (OS 64)指定的网络名不再可用”错误
  3. WPF MVVC 基础 - 父子窗体
  4. 漫步线性代数十六——投影和最小二乘
  5. c++ 写入文件_Python学习第52课-写入文件
  6. ambari下 hive metastore 启动失败
  7. rtklib 天线相位中心_全球5G天线产业竞争格局、技术趋势分析
  8. webrtc之SVC实现(十)
  9. matlab手眼标定,机器人手眼标定(eye to hand和eye in hand)
  10. Oracle | awr报告分析
  11. Transformer结构详解
  12. 路由守卫 AJAX,vue路由传参与路由守卫
  13. 天空里的云偶遇 彩虹做成衣裳也要用心呵护——潮港城的茶点,好芒果的冰饮...
  14. html js 做一个钟表,html,css,js实现的一个钟表
  15. 记录一次数据库被黑的过程
  16. 长沙小吃比较好吃and著名的地方
  17. iphone xr xs_如何关闭iPhone X,XS和XR
  18. python在浏览器运行一片空白_运行代码,浏览器显示一片空白是怎么回事?
  19. 【拓扑排序+dp】Hzy's Rabbit Candy
  20. MySQL 的 LIKE 语句

热门文章

  1. 多线程不重复读取数据_别再犯错了,多线程访问同一个资源一定要上锁?
  2. nodeJS的管道流和链式流
  3. linux设置登录次多过多锁定时间
  4. 十秒解决开发环境跨域问题——取消浏览器同源策略
  5. CPU+GPU异构计算完全解析
  6. 爬虫基本原理及Request和Response分析
  7. 31.CSS3变形效果【下】
  8. 一张图理解RACSignal的Subscription过程
  9. protobuf-2.5.0安装
  10. 【物联网智能网关-03】GPRS模块中文短信收发