Spring Framework 之AOP

问题

什么是AOP?

AOP的好处是什么?

AOP的实现方式有哪些?

Spring AOP 与 AspectJ AOP的区别?

AOP概述

​ AOP(Aspect-Oriented Programming)翻译为中文就是“面向切面编程”。从“面向过程”到“面向对象”编程,编程思想的发展永远是朝更自然、更优雅地描述世界的方向发展。“面向切面“的出现也是这一目的,它是对面向对象的补充,使编程语言能够更好地描述世界。

​ 现实编程中无法将重复出现的代码抽取至父类中。例如鉴权模块、监控模块、日志记录模块等,我们无法将公共代码块纵向抽取。那么如何横向抽取重复代码呢?AOP就能够通过横向抽取机制解决无法纵向抽取的问题,将分散在业务代码中的公共代码块抽取至一个独立的模块中。这也体现了设计模式中的“单一职责“的思想。

AOP知识

1、连接点(Joinpoint)

​ 程序执行的某个特定位置。例如类初始化前后、函数调用前后、函数抛异常后等。类或代码块具有边界性质的特定点就成为“连接点”。

2、切点(PointCut)

​ 每个类或函数都可以认为是连接点,我们如何定位我们关注的“连接点“?我们不需要为每个类或函数添加Advice,PointCut就是通过规则为我们关注的joinpoint添加Advice。

3、增强(Advice)

​ 由aspect添加到特定的Join point的代码块。

4、目标对象(Target)

​ 增强逻辑织入的目标类。

5、引介(Introduction)

​ 引介是一种特殊的增强,他为类添加一些属性和方法。

​ 例子:https://blog.csdn.net/u010599762/article/details/80182178

6、织入(Weaving)

​ 织入是将增强添加至目标类的具体连接点上的过程。

7、代理(Proxy)

​ 类被织入增强后就会产生新的结合了原类与增强的代理类。在Spring AOP中有两种代理,分别是JDK动态代理和CGLib动态代理。

8、切面(Aspect)

​ 切面由切点和增强组成,包括增强的横切逻辑和连接点。Spring AOP负责将切面中的增强逻辑织入指定的连接点中。

代理

静态代理

代理模式

​ 代理模式提供对目标对象进行访问方式,即通过代理对象访问目标对象。可以在目标对象的基础上增强,提供个性功能,达到扩招目标对象功能的作用。

接口

public interface Subject {
void request();
}

具体实现

public class RealSubject implements Subject {
public void request() {
//业务逻辑
}
}

代理类

public class Proxy implements Subject {

    //要代理的实现类
private Subject subject = null; public Proxy() {
this.subject = new Proxy();
} //通过构造函数传递代理者
public Proxy(Object... objects) { } //实现接口定义的方法
public void request() {
this.before();
this.subject.request();
this.after();
} //预处理
public void before() { } //后处理
public void after() { }
}

动态代理

JDK动态代理

​ JDK动态代理设计到java.lang.relect包中的两个类:Proxy和InvocationHandler,InvocationHandler可以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑与业务逻辑编织到一起。Proxy利用InvocationHandler动态创建某一符合该接口的实例,生成目标类的代理对象。

public class Monitor {

    public static void begin(){
System.out.println("before");
} public static void end(){
System.out.println("after");
}
}
public interface CouponService {

    void getCoupon();
}
public class CouponServiceImpl implements CouponService {

    public void getCoupon() {

        //Monitor.begin();
try {
System.out.println("业务代码");
} catch (Exception e) {
throw new RuntimeException();
}
//Monitor.end();
}
}
public class PerformanceHandler implements InvocationHandler {

    //被代理对象
private Object target; public PerformanceHandler(Object target) {
this.target = target;
} public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Monitor.begin();
Object object = method.invoke(target, args);
Monitor.end();
return object;
}
}
 public class Client {

    public static void main(String[] args) {

        //被代理对象
CouponService target = new CouponServiceImpl(); //让PerformanceHandler将监视横切逻辑编织到CouponService中
PerformanceHandler performanceHandler = new PerformanceHandler(target); //通过Proxy的newProxyInstace()方法,为编织了业务逻辑与监控逻辑的handler创建一个符合CouponService接口的代理实现
CouponService proxy = (CouponService) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),performanceHandler); proxy.getCoupon();
}
}
CGLIB动态代理

​ JDK创建代理只能为接口创建代理,实际开发中我们很难保证每个类都有其对应的接口,对于没有通过接口定义业务方法的类,JDK已经没法对其进行代理,这就出现了Cglib,通过字节码技术,为一个类创建子类,在子类中采用方法拦截的技术拦截所有父类方法的调用并织入横切逻辑。

public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer(); public Object getProxy(Class clazz) {
//设置需要创建子类的类
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
//通过字节码技术动态创建子类实例
return enhancer.create();
} @Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before");
//通过代理类调用父类中的方法
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("after");
return result;
}
}
public class Client {

    public static void main(String[] args) {
CglibProxy proxy = new CglibProxy(); //通过冬天生成子类的方式创建代理类
CouponServiceImpl couponService = (CouponServiceImpl) proxy.getProxy(CouponServiceImpl.class); couponService.getCoupon();
}
}

静态代理与动态代理区别

(1)静态代理在编译时就已经实现,编译完成后代理类是一个实际的class文件

(2)动态代理是在运行时动态生成的,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中

JDK动态代理 与CGLIB代理区别

​ Cglib所创建的动态代理,性能要比jdk创建的动态代理高。但对用Cglib创建代理的时间,JDK动态代理显然要快很多。对于无需单例的代理对象或实例池可以使用CGLib来创建代理(无需频繁创建),反之使用JDK动态代理。

@AspectJ

​ AspectJ是语言级使用Java注解来AOP实现的一种方式,扩招Java语言,定义AOP语法,能够在编译期间提供横切代码织入。

切面定义

Java Configuration方式配置

@Configuration
@EnableAspectJAutoProxy
public class AOPConfig {
}

xml方式配置

<aop:aspectj-autoproxy/>

切面声明

@Component
@Aspect
public class ControllerIntercept {
}

通过@Aspect将ControllerIntercept标识成一个切面,通过@Component将ControllerIntercept标识为一个Bean,Spring框架会收集@Aspect标注的Bean,并将其添加至Spring AOP中。

切点声明

@Pointcut("execution(public * com.ljw.discern_spider.controller.DiscernController.discern(..))")
public void discernWeb() {
}

一个 pointcut 的声明由两部分组成:

  • 一个方法签名, 包括方法名和相关参数
  • 一个 pointcut 表达式, 用来指定哪些方法执行是我们感兴趣的(即因此可以织入 advice).

切点函数

​ AspectJ5.0的切点表达式由关键字和操作参数组成,如 "execution( greetTo(..))" 的切点表达式, execution 就是关键字, 而圆括号里的 greetTo(..) 就是操作参数。

函数 入参 说明
execution() 方法匹配模式串 满足方法匹配模式串所有目标类方法连接点。例如:@Pointcut("execution(public * com.ljw.controller.DiscernController.discern(..))") ,标识目标类中的discern方法
@annatation 方法注解类名 表示标注特定注解的目标类方法连接点。例如:@Pointcut("@annotation(com.ljw.ABC)"),表示标注@ABC注解的目标类连接点
within() 类名匹配串 表示特定路径下的所有连接点。例如:within(com.ljw.*),表示com.ljw路径下的所有连接点。within(com.ljw.*Service),表示com.ljw路径下以Service结尾的类中的所有连接点
args() 类名 匹配参数满足要求的方法。例如@Before(value = "aspectMethod() && args(name)"),只有一个参数且为name的方法;adgs(com.ljw.ABC),表示所有且仅有一个入参类型与ABC向匹配的方法。
target() 类名 目标类按类型匹配指定类。目标类的所有连接点匹配这一切点。例如:target(com.ljw.ABC)

常见的切点表达式

按方法签名匹配

@Pointcut("execution(public * com.ljw.controller.DiscernController.discern(..))")

按类匹配

@Pointcut("within(com.ljw.*)")
@Pointcut("within(com.ljw..*)")
//匹配实现接口的所有类中的实现的方法
@Pointcut("within(ABCService+)")

按Bean名称匹配

@Pointcut("bean(*Service)")

逻辑运算符运用

可以运用 || 、&& 、!等逻辑运算符

@Pointcut("bean(*Service || *ServiceImpl)")
@Pointcut("bean(*Service) && within(com.ljw.service.*)")

通配符

*匹配任意字符。只能匹配上下文中的一个元素。

..:匹配任意字符。可以匹配上下文中多个元素。标识类时需要与*结合使用,标识参数时可以单独使用。

+:表示按类型匹配指定类的所有类,必须跟在类名后面。继承或实现指定类的所有类。

增强声明

@Around("discernWeb()")
public Object around(ProceedingJoinPoint joinPoint) {
long start = System.currentTimeMillis();
try {
before(joinPoint);
return joinPoint.proceed(joinPoint.getArgs());
} catch (Throwable t) {
return Result.createFalseRet().withErrMsg("around exception");
}
}

可以从ProceedingJoinPoint中获取参数,对参数进行操作。

@Before:前置增强。

@After:后置增强。

@Around:环绕增强。可以在方法前后进行不同的操作。

参考

[1]《精通Spring4.x企业应用开发实战》

[2]https://segmentfault.com/a/1190000011291179

[3]https://segmentfault.com/a/1190000007469968

Spring Framework 之AOP的更多相关文章

  1. 框架应用:Spring framework (二) - AOP技术

    基础概念 线程中的方法栈 java程序虚拟机启动时会载入程序码,虚拟机会为每一条正在运行的线程生成一个方法调用栈,线程以方法运行为执行单位. AOP概念以及目标 AOP是面向切面编程,其实就是在不修改 ...

  2. Hello Spring Framework——面向切面编程(AOP)

    本文主要参考了Spring官方文档第10章以及第11章和第40章的部分内容.如果要我总结Spring AOP的作用,不妨借鉴文档里的一段话:One of the key components of S ...

  3. 转-Spring Framework中的AOP之around通知

    Spring Framework中的AOP之around通知 http://blog.csdn.net/xiaoliang_xie/article/details/7049183 标签: spring ...

  4. 【Spring Framework】Spring入门教程(六)Spring AOP使用

    Spring的AOP 动态代理模式的缺陷是: 实现类必须要实现接口 -JDK动态代理 无法通过规则制定拦截无需功能增强的方法. Spring-AOP主要弥补了第二个不足,通过规则设置来拦截方法,并对方 ...

  5. 【Spring Framework】Spring入门教程(五)AOP思想和动态代理

    本文主要讲解内容如下: Spring的核心之一 - AOP思想 (1) 代理模式- 动态代理 ① JDK的动态代理 (Java官方) ② CGLIB 第三方代理 AOP概述 什么是AOP(面向切面编程 ...

  6. 浅谈对Spring Framework的认识

    Spring Framework,作为一个应用框架,官方的介绍如下: The Spring Framework provides a comprehensive programming and con ...

  7. 手动创建Spring项目 Spring framework

    之前学习框架一直是看的视频教程,并且在都配套有项目源码,跟着视频敲代码总是很简单,现在想深入了解,自己从官网下载文件手动搭建,就遇到了很多问题记载如下. 首先熟悉一下spring的官方网站:http: ...

  8. Spring Framework------>version4.3.5.RELAESE----->Reference Documentation学习心得----->关于spring framework中的beans

    Spring framework中的beans 1.概述 bean其实就是各个类实例化后的对象,即objects spring framework的IOC容器所管理的基本单元就是bean spring ...

  9. Spring Framework------>version4.3.5.RELAESE----->Reference Documentation学习心得----->使用spring framework的IoC容器功能----->方法一:使用XML文件定义beans之间的依赖注入关系

    XML-based configuration metadata(使用XML文件定义beans之间的依赖注入关系) 第一部分 编程思路概述 step1,在XML文件中定义各个bean之间的依赖关系. ...

随机推荐

  1. 微信小程序页面通信

    目录 微信小程序页面通信 方式一:通过URL 方式二:通过全局变量 方式三:通过本地存储 方式四:通过路由栈 微信小程序页面通信 方式一:通过URL // A 页面 wx.navigateTo({ u ...

  2. 【Selenium07篇】python+selenium实现Web自动化:PO模型,PageObject模式!

    一.前言 最近问我自动化的人确实有点多,个人突发奇想:想从0开始讲解python+selenium实现Web自动化测试,请关注博客持续更新! 这是python+selenium实现Web自动化第七篇博 ...

  3. 使用rem配置PC端自适应大屏

    效果如下 使得大屏不论在什么宽高比例依然能展示全部数据 安装 npm install -S postcss-pxtorem rem配置思路 原先的rem函数是能解决大部分的问题的,如果展示不全,也可以 ...

  4. java 方法 在jvm中的调用

    java 某个类的几个对象,这些对象调用类中一个函数,是各自拥有自己的函数代码还是使用同一段代码?30 1.java 某个类的几个对象,这些对象调用类中一个函数(普通的函数),是各自拥有自己的函数代码 ...

  5. qt creator源码全方面分析(4-1)

    目录 d指针和q指针 简单示例 q指针 QObject和QObjectPrivate qtcreator中的变体1 qtcreator中的变体2 小结 d指针和q指针 我们在类成员名称和使用d指针中, ...

  6. Davor COCI 2018

    当题目中有多组解,但要某值最大,该怎么办? 本文为博客园ShyButHandsome的原创作品,转载请注明出处 题目描述 After successfully conquering the South ...

  7. 21-Java-Hibernate框架(一)

    一.Hibernate了解 Hibernate框架是Java持久层的框架,是Gavin King发明的,2001年发布的,JBoss公司的产品,2003年进入市场. Hibernate是基于对象来操作 ...

  8. Python基础:按位异或 ^ ,按位或 | ,按位与 &

    前言文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. PS:如有需要Python学习资料的小伙伴可以加点击下方链接自行获取http: ...

  9. react useCallback notice

    多个不同输入框共用一个方法时使用useCallback: params = initParams = {code: "code_test", name: "name_te ...

  10. 【vue】nextTick源码解析

    1.整体入手 阅读代码和画画是一样的,忌讳一开始就从细节下手(比如一行一行读),我们先将细节代码折叠起来,整体观察nextTick源码的几大块. 折叠后代码如下图 整体观察代码结构 上图中,可以看到: ...