Spring(中) AOP

(一)代理模式

1.静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类。

举例:

package com.deserts.proxy.staticproxy;/*** @ClassName ProxyTest* @Description TODO* @Author deserts* @Date 2020/10/16 12:12*/interface IShoeFactory{void produce();
}class ShoeFactory implements IShoeFactory{@Overridepublic void produce() {System.out.println("生产鞋子");}
}class ShoeProxy implements IShoeFactory{private IShoeFactory shoeFactory;public ShoeProxy (IShoeFactory shoeFactory){this.shoeFactory = shoeFactory;}@Overridepublic void produce() {shoeFactory.produce();}
}public class ProxyTest {public static void main(String[] args) {ShoeProxy proxy = new ShoeProxy(new ShoeFactory());proxy.produce();}
}

优点:在不修改目标对象的功能前提下, 能通过代理对象对目标功能扩展

缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类;或者需要实现很多接口,代码混乱。

一旦接口增加方法,目标对象与代理对象都要维护。

2.JDK动态代理

基本介绍:

  1. 代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理

  2. 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象

  3. 动态代理也叫做:JDK代理、接口代理

举例:

package com.deserts.proxy.jdkproxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** @ClassName JDKProxyTest* @Description TODO* @Author deserts* @Date 2020/10/16 12:23*/interface IShoeFactory{void produce();
}
class ShoeFactory implements IShoeFactory{@Overridepublic void produce() {System.out.println("生产鞋子中");}
}class ShoeProxyFactory{private Object target;public ShoeProxyFactory(Object target) {this.target = target;}public Object getProxyInstance(){return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object returnResult = method.invoke(target, args);return returnResult;}});}
}public class JDKProxyTest {public static void main(String[] args) {IShoeFactory proxy = (IShoeFactory)new ShoeProxyFactory(new ShoeFactory()).getProxyInstance();proxy.produce();}}

(二)AOP概述

1.一个动态代理的例子

1.1 要求

①执行加减乘除运算

②日志:在程序执行期间追踪正在发生的活动

1.2 实现

创建计算器接口,让计算器类去实现;创建动态代理的类,利用构造器或者方法传入计算器对象,创建获取被代理类的方法。

1.ICalculator接口

public interface ICalculator {int add(int i, int j);int sub(int i, int j);int mul(int i, int j);int div(int i, int j);
}

2.Calculator类

package com.deserts.aop.proxy;/*** @ClassName Calculator* @Description TODO* @Author deserts* @Date 2020/10/15 19:23*/
public class Calculator implements ICalculator{@Overridepublic int add(int i, int j) {int result = i + j;return result;}@Overridepublic int sub(int i, int j) {int result = i - j;return result;}@Overridepublic int mul(int i, int j) {int result = i * j;return result;}@Overridepublic int div(int i, int j) {int result = i / j;return result;}
}

3.代理类

package com.deserts.aop.proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.function.Predicate;/*** @ClassName ProxyFactory* @Description TODO* @Author deserts* @Date 2020/10/15 19:26*/
public class ProxyFactory {Object target;public ProxyFactory(Object target){this.target = target;}public Object getProxy(){return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object returnResult = null;try {Log.before(method.getName(), args);returnResult = method.invoke(target, args);Log.after(method.getName(), returnResult);} catch (Exception e) {Log.exceptionInfo(method.getName(), e);} finally {Log.end();}return returnResult;}});}
}

4.封装日志功能,错误信息的类

package com.deserts.aop.proxy;import java.util.Arrays;/*** @ClassName Log* @Description TODO* @Author deserts* @Date 2020/10/15 19:54*/
public class Log {public static void before(String methodName, Object... args){System.out.println(methodName + "即将执行,参数为:" + Arrays.asList(args));}public static void after(String methodName, Object result){System.out.println(methodName + "执行结束,结果为:" + result);}public static void exceptionInfo(String methodName, Exception e){System.out.println(methodName + "执行过程出现了异常,异常信息为:" + e.getCause());}public static void end(){System.out.println("程序执行结束");}
}

5.测试类

package com.deserts.aop.proxy;/*** @ClassName Client* @Description TODO* @Author deserts* @Date 2020/10/15 19:34*/
public class Client {public static void main(String[] args) {ICalculator proxy = (ICalculator)new ProxyFactory(new Calculator()).getProxy();proxy.add(1,2);proxy.div(2, 0);}
}

2.AOP概述

  1. AOP(Aspect-Oriented Programming,面向切面编程):是一种新的方法论,是对传统 OOP(Object-Oriented Programming,面向对象编程)的补充。

    • 面向对象 纵向继承机制
    • ​ 面向切面 横向抽取机制
  2. AOP编程操作的主要对象是切面(aspect),而切面用于模块化横切关注点(公共功能)

  3. 在应用AOP编程时,仍然需要定义公共功能,但可以明确的定义这个功能应用在哪里,以什么方式应用,并且不必修改受影响的类。这样一来横切关注点就被模块化到特殊的类里——这样的类我们通常称之为“切面”

  4. AOP的好处:

    ① 每个事物逻辑位于一个位置,代码不分散,便于维护和升级

    ② 业务模块更简洁,只包含核心业务代码

    ③ AOP图解

3.AOP术语

3.1 横切关注点

从每个方法中抽取出来的同一类非核心业务。

3.2 切面(Aspect)

封装横切关注点信息的类,每个关注点体现为一个通知方法。

3.3 通知(Advice)

切面必须要完成的各个具体工作

3.4 目标(Target)

被通知的对象

3.5 代理(Proxy)

向目标对象应用通知之后创建的代理对象

3.6 连接点(Joinpoint)

横切关注点在程序代码中的具体体现,对应程序执行的某个特定位置。例如:类某个方法调用前、调用后、方法捕获到异常后等。

3.7 切入点(pointcut)

定位连接点的方式。每个类的方法中都包含多个连接点,所以连接点是类中客观存在的事物。如果把连接点看作数据库中的记录,那么切入点就是查询条件——AOP可以通过切入点定位到特定的连接点。切点通过org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。

3.8 图解

4.AspectJ

4.1 简介

AspectJ:Java社区里最完整最流行的AOP框架。

在Spring2.0以上版本中,可以使用基于AspectJ注解或基于XML配置的AOP。

4.2 在Spring中启用AspectJ注解支持

1.导入jar包

  • com.springsource.net.sf.cglib-2.2.0.jar
  • com.springsource.org.aopalliance-1.0.0.jar
  • com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
  • spring-aop-4.0.0.RELEASE.jar
  • spring-aspects-4.0.0.RELEASE.jar

2.引入AOP命名空间

3.配置扫描包,配置被代理的类和日志类

日志类需要@Aspect注解,@Before注解将方法配置为前置通知,需要指定value值,值为切入点表达式。同时需要@Component注解让Spring组件扫描去管理。

被代理类也需要@Component注解,让Spring去管理:

测试类使用时需要创建Spring容器去管理被配置好的类的对象:

运行结果:

4.3 用AspectJ注解声明切面

① @Before:前置通知,在方法执行之前执行

② @After:后置通知,在方法执行之后执行

③ @AfterRunning:返回通知,在方法返回结果之后执行

④ @AfterThrowing:异常通知,在方法抛出异常之后执行

⑥ @Around:环绕通知,围绕着方法执行

(三) AOP细节

1.细节一:容器创建的对象

IOC容器中保存的是组件的代理对象,可以理解成这个代理对象也去实现了目标对象所实现的接口

如:

运行结果:

可以看到,该对象是class com.sun.proxy.$Proxy7代理对象,底层原理是jdk动态代理。

除了实现接口的方式去代理,spring还提供了cglib代理,为没有接口的类进行代理。

如:

测试类:

运行结果:

可以看到cglib为我们创建了代理对象

2.细节二:切入点表达式

2.1 *:

1)匹配一个或多个字符,如:execution(public int com.deserts.aop.proxy.Calculator.*(int,int))

  1. 匹配任意类型的参数,如:execution(public int com.deserts.aop.proxy.Calculator.add(*,*))

  2. 只能匹配一层路径,如:execution(public int com.deserts.aop.*.Calculator.add(int,int))

4)权限类型前不能写,可以不写权限类型。public(可选类型)。

2.2 … :
  1. 匹配任意多个参数,如:execution(public int com.deserts.aop.proxy.Calculator.add(…))

2)匹配多层路径,如:execution(public int com.deserts…Calculator.*(int,int))

2.3 两种类型

最精确的:execution(public int com.deserts.aop.proxy.Calculator.add(int,int))

最模糊的:execution(*.*(…)) 不建议写,见方法就切

2.4 使用&&、||、!

在AspectJ中,切入点表达式可以通过 “&&”、“||”、“!”等操作符结合起来。

表达式 含义
execution (* *.add(int,…)) || excution( *.sub(int, …))
!execution (* *.add(int,…)) 匹配不是任意类中第一个参数为int类型的add方法

3.细节三:通知方法的执行顺序

正常执行:@Before(前置通知) ==== @After(后置通知) ==== @AfterReturning(正常返回)

异常执行: @Before(前置通知) ==== @After(后置通知) ==== @AfterThrowing(方法异常)

4.细节四:JoinPoint获取获取目标方法的信息

主要通过传入通知方法的JoinPoint joinPoint参数,在方法内调用参数相关方法进行获取,如:

5.细节五:使用returning、throwing来接收返回值、异常

接收返回值:

接收异常信息:

6.细节六:Spring对通知方法的约束

1)spring对通知方法的没有太大约束,唯一的约束是参数列表的参数不能乱写,如果有未知的参数,需要告诉spring这个参数是用来做什么的,因为这些通知方法是spring所管理的,主要是通过反射来调用的。

2)如returning、throwing指定接收返回值、异常的参数,参数类型要尽量的大,如Object、Exception

7.细节七:抽取可重用的切入点表达式

  • 抽取可重用的切入点表达式:

    • 1.创建一个返回值为void,参数为空的空方法
    • 2.给方法加上注解@PointCut,并在参数里写入要抽取的切入点表达式
    • 3.将方法名放入要使用该表达式的值里面
  • 示例:

8.细节八:环绕通知

  • 环绕通知是通知里面最强大的,可以当作其它四个通知的整合。
  • 对于环绕通知来说,连接点的参数类型必须是ProceedingJoinPoint。它是 JoinPoint的子接口,允许控制何时执行,是否执行连接点。
  • 在环绕通知中需要明确调用ProceedingJoinPoint的proceed()方法来执行被代理的方法。如果忘记这样做就会导致通知被执行了,但目标方法没有被执行
  • 注意:环绕通知的方法需要返回目标方法执行之后的结果,即调用 joinPoint.proceed();的返回值,否则会出现空指针异常。

示例:

9.细节九:环绕通知的执行顺序&抛出异常让其它通知感受到

测试:将四个普通通知和环绕通知都打开,运行查看结果:

通知顺序:

新的顺序:

让普通通知感受到异常:将该异常抛出去

10.细节十:多切面运行顺序

都是普通通知时,会根据切面类的名称的字母顺序,小的在外层,如LogUtil类在VaAspect类的外层,先进去后出来

若想改变切面运行顺序,可以加上@Order注解,如:

value值越小,优先级越高,也就是在最外层。

当有环绕通知时,环绕通知会先于该切面类的普通通知去执行,因为该环绕通知是加在这个切面类的。

如环绕通知加到LogUtils上:

AOP使用场景

1.日志记录

2.权限验证

3.安全检查

4.事务控制

(四)以xml方式配置切面

1.基于注解的AOP

2.基于配置的AOP

步骤

  1. 在IOC容器中创建管理目标类的bean和切面类的bean

  2. 配置切面类中的通知方法,配置切面时需要引用上面配置好的切面类

  3. 测试

3.注解和配置的选择

注解:快速方便

配置:功能完善

选择:重要的用配置,不重要的用注解

Spring框架(中) AOP相关推荐

  1. Spring框架中的核心技术之AOP

    目录 1. 什么是AOP? 2. 实现AOP技术的框架有哪些? 2.1 Spring框架中的AOP技术 2.2 Aspectj框架 2.3 小结 3. Aspectj框架中的使用AOP的方式 4. A ...

  2. 详解Spring框架的AOP机制

    AOP是Spring框架面向切面的编程思想,AOP采用一种称为"横切"的技术,将涉及多业务流程的通用功能抽取并单独封装,形成独立的切面,在合适的时机将这些切面横向切入到业务流程指定 ...

  3. Spring 框架之 AOP 原理深度剖析!|CSDN 博文精选

    作者 | GitChat 责编 | 郭芮 出品 | CSDN 博客 AOP(Aspect Oriented Programming)面向切面编程是 Spring 框架最核心的组件之一,它通过对程序结构 ...

  4. Spring框架中常用的设计模式详解

    一.浅谈控制反转(IOC)与依赖注入(DI) IOC(Inversion of Control)是Spring中一个非常重要的概念,它不是什么技术,而是一种解耦的设计思想.它主要的额目的是借助于第三方 ...

  5. Spring框架中的设计模式(一)

    设计模式有助于遵循良好的编程实践.作为最流行的Web框架之一的Spring框架也使用其中的一些. 本文将介绍Spring Framework中使用的设计模式.这是5篇专题文章的第一部分.这次我们将发现 ...

  6. 在Spring 框架中如何更有效的使用JDBC?

    使用Spring JDBC 框架,资源管理以及错误处理的代价都会减轻.开发人员只需通过statements 和queries 语句从数据库中存取数据.Spring 框架中通过使用模板类能更有效的使用J ...

  7. 在Spring框架中使用SQL存储过程

    Spring框架也支持对SQL存储过程的调用,SQL存储过程是一组预先定义好的SQL语句,并存储到数据库管理系统中,外部程序可以直接调用执行.本课主要讨论在Spring框架中应用程序如何调用MySQL ...

  8. 理解Spring框架中Bean的作用域

    本篇介绍Spring Bean实例的作用范围,Spring Bean实例的作用范围由配置项scope限定.通过本篇的学习,可以达成如下目标. ● 应用scope配置项配置Bean的作用域 ● 应用单例 ...

  9. Spring框架中的控制反转和依赖注入

    控制反转: 控制反转是用来降低代码之间的耦合度的,基本思想就是借助"第三方"实现具有依赖对象的解耦. 为什么需要控制反转,因为项目中对象或多或少存在耦合.控制反转的关键在于Ioc容 ...

  10. Spring框架中提取list集合类型属性注入

    提取list集合类型属性注入 前言 引入名称空间 编写`xml`配置文件 运行结果 前言 对于某一个类型属性通用性较高的情况下,可以单独的提取出来,给需要的bean进行引用. 有关类的创建见<S ...

最新文章

  1. 将类别加入到别人的名称空间内
  2. JDK+TOMCAT在LINUX下简单的配置
  3. cvpr2019 目标检测算法_CVPR2019 | 0327日更新12篇论文及代码汇总(多目标跟踪、3D目标检测、分割等)...
  4. 大型网站技术架构:摘要与读书笔记
  5. MyBatis动态SQL,写SQL更爽
  6. python写前端代码_python学习之路前端-JavaScript
  7. u盘读写测试_aigo U395固态U盘评测,速度可能会吓到你,价格很良心
  8. Spark:相关错误总结
  9. SQL Server 分离
  10. 《C#图解教程》 总览
  11. MySQL的下载安装教程
  12. 谷歌浏览器启动后,图标变成空白解决办法
  13. Windows下打开.jar文件的方式
  14. 目前见到的最傻瓜全面的STRUTS入门教程^_^
  15. 1971旗舰cpu intel_这就是近年来Intel最良心CPU!我彻底服了
  16. AutoCAD Civil 3D中将CASS地形图中地形点转换成CAD点实体
  17. Designing Data-Intensive Applications翻译
  18. D. Masquerade strikes back(思维)
  19. 常见功能测试点的测试用例集合--51testing
  20. Pepper初级教程:第二章 Pepper的使用方法

热门文章

  1. recycleview可见位置_判断view是否在可见区域
  2. 计算机内存不足吃鸡怎么办,Win10玩吃鸡游戏提示虚拟内存不足怎么办?
  3. nginx ---- nginx服务器版本升级和新增模块
  4. nginx ---- 启停
  5. mac ---- 安装nginx
  6. oracle数据库存储管理总结,oracle数据库存储管理
  7. sql的join语法解析
  8. MYSQL主从同步(Windows到Windows)
  9. shell 批量生成随机文件
  10. 再谈table组件:固定表头和表列