文档章节

怎么才能让Spring AOP有最大的作用--乐字节java

o
 osc_sb30h1xb
发布于 07/15 07:12
字数 2708
阅读 32
收藏 0

行业解决方案、产品招募中!想赚钱就来传!>>>

Spring AOP

日志处理带来的问题

​ 我们有一个Pay(接口) 然后两个实现类DollarPay和RmbPay,都需要重写pay()方法, 这时我们需要对pay方法进行性能监控,日志的添加等等怎么做?

SpringAOP-02

最容易想到的方法

​ 对每个字符方法均做日志代码的编写处理,如下面方式

SpringAOP-03

​ 缺点: 代码重复太多, 添加的日志代码耦合度太高(如果需要更改日志记录代码功能需求,类中方法需要全部改动,工程量浩大)

使用装饰器模式 /代理模式改进解决方案

装饰器模式:动态地给一个对象添加一些额外的职责。

代理模式:以上刚讲过。于是得出以下结构:

SpringAOP-04

​ 仔细考虑过后发现虽然对原有内部代码没有进行改动,对于每个类做日志处理,并引用目标类,但是如果待添加日志的业务类的数量很多,此时手动为每个业务类实现一个装饰器或创建对应的代理类,同时代码的耦合度也加大,需求一旦改变,改动的工程量也是可想而知的。

有没有更好的解决方案,只要写一次代码,对想要添加日志记录的地方能够实现代码的复用,达到松耦合的同时,又能够完美完成功能?

​ 答案是肯定的,存在这样的技术,aop已经对其提供了完美的实现!

如有疑问,可加入群:10803-55292,输入暗号13,即可有大佬帮助

什么是AOP?

​ Aspect Oriented Programing 面向切面编程,相比较 oop 面向对象编程来说,Aop关注的不再是程序代码中某个类,某些方法,而aop考虑的更多的是一种面到面的切入,即层与层之间的一种切入,所以称之为切面。联想大家吃的汉堡(中间夹肉)。那么aop是怎么做到拦截整个面的功能呢?考虑前面学到的servlet filter /* 的配置 ,实际上也是aop 的实现。

AOP能做什么?

​ AOP主要应用于日志记录,性能统计,安全控制,事务处理等方面,实现公共功能性的重复使用。

AOP的特点

​ 1. 降低模块与模块之间的耦合度,提高业务代码的聚合度。(高内聚低耦合)

​ 2. 提高了代码的复用性。

​ 3. 提高系统的扩展性。(高版本兼容低版本)

​ 4. 可以在不影响原有的功能基础上添加新的功能

AOP的底层实现

​ 动态代理(JDK + CGLIB)

AOP基本概念

Joinpoint(连接点)

​ 被拦截到的每个点,spring中指被拦截到的每一个方法,spring aop一个连接点即代表一个方法的执行。

Pointcut(切入点)

​ 对连接点进行拦截的定义(匹配规则定义 规定拦截哪些方法,对哪些方法进行处理),spring 有专门的表达式语言定义。

Advice(通知)

​ 拦截到每一个连接点即(每一个方法)后所要做的操作

  1. 前置通知 (前置增强)— before() 执行方法前通知
  2. 返回通知(返回增强)— afterReturn 方法正常结束返回后的通知
  3. 异常抛出通知(异常抛出增强)— afetrThrow()
  4. 最终通知 — after 无论方法是否发生异常,均会执行该通知。
  5. 环绕通知 — around 包围一个连接点(join point)的通知,如方法调用。这是最强大的一种通知类型。 环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。

Aspect(切面)

​ 切入点与通知的结合,决定了切面的定义,切入点定义了要拦截哪些类的哪些方法,通知则定义了拦截过方法后要做什么,切面则是横切关注点的抽象,与类相似,类是对物体特征的抽象,切面则是横切关注点抽象。

Target(目标对象)

​ 被代理的目标对象

Weave(织入)

​ 将切面应用到目标对象并生成代理对象的这个过程即为织入

Introduction(引入)

​ 在不修改原有应用程序代码的情况下,在程序运行期为类动态添加方法或者字段的过程称为引入

Spring AOP的实现

Spring AOP环境搭建

坐标依赖引入

<!--Spring AOP-->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.9</version>
</dependency>

添加spring.xml的配置

添加命名空间

xmlns:aop="http://www.springframework.org/schema/aop"
 http://www.springframework.org/schema/aop
 http://www.springframework.org/schema/aop/spring-aop.xsd

注解实现

定义切面

/**
 * 切面
 *  切入点和通知的抽象   (与面向对象中的 类 相似)
 *  定义 切入点和通知 (切入点定义了要拦截哪些类的哪些方法,通知则定义了拦截过方法后要做什么)
 */
@Component // 将对象交给IOC容器去实例化
@Aspect // 声明当前类是一个切面
public class LogCut {

    /**
     *  切入点:
     *      匹配规则。规定什么方法被拦截、需要处理什么方法
     *  定义切入点
     *    @Pointcut("匹配规则")
     *
     *    Aop 切入点表达式简介
     *       1. 执行任意公共方法:
     *          execution(public *(..))
     *       2. 执行任意的set方法
     *          execution(* set*(..))
     *       3. 执行com.xxxx.service包下任意类的任意方法
     *          execution(* com.xxxx.service.*.*(..))
     *       4. 执行com.xxxx.service 包 以及子包下任意类的任意方法
     *          execution(* com.xxxx.service..*.*(..))
     *
     *       注:表达式中的第一个* 代表的是方法的修饰范围
     *          可选值:private、protected、public (* 表示所有范围)     
     */
    @Pointcut("execution (* com.xxxx.service..*.*(..) )")
    public void cut(){}

    /**
     * 声明前置通知   并将通知应用到定义的切入点上
     * 目标类方法执行前 执行该通知
     *
     */
   @Before(value = "cut()")
    public void before() {
        System.out.println("前置通知.....");
    }

    /**
     * 声明返回通知   并将通知应用到定义的切入点上
     * 目标类方法(无异常)执行后 执行该通知
     *
     */
   @AfterReturning(value = "cut()")
    public void afterReturn() {
        System.out.println("返回通知.....");
    }

    /**
     * 声明最终通知   并将通知应用到定义的切入点上
     * 目标类方法(无异常或有异常)执行后 执行该通知
     *
     */
    @After(value = "cut()")
    public void after() {
        System.out.println("最终通知.....");
    }

    /**
     * 声明异常通知   并将通知应用到定义的切入点上
     * 目标类方法出现异常时 执行该通知
     */
    @AfterThrowing(value="cut()",throwing = "e")
    public void afterThrow(Exception e) {
        System.out.println("异常通知....." + "  异常原因:" + e.getCause());
    }

    /**
     *  声明环绕通知  并将通知应用到切入点上
     *  方法执行前后  通过环绕通知定义相应处理
     *      需要通过显式调用对应的方法,否则无法访问指定方法 (pjp.proceed();)
     * @param pjp
     * @return
     */
    @Around(value = "cut()")
    public Object around(ProceedingJoinPoint pjp) {
        System.out.println("前置通知...");

        Object object = null;
        try {
            object = pjp.proceed();
            System.out.println(pjp.getTarget() + "======" + pjp.getSignature());
            // System.out.println("返回通知...");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("异常通知...");
        }
        System.out.println("最终通知...");

        return object;
    }

}

配置文件(spring.xml)

 <!--配置AOP代理-->
 <aop:aspectj-autoproxy/>

XML实现

定义切面

**
 * 切面
 *  切入点和通知的抽象   (与面向对象中的 类 相似)
 *  定义 切入点和通知 (切入点定义了要拦截哪些类的哪些方法,通知则定义了拦截过方法后要做什么)
 */
@Component // 将对象交给IOC容器去实例化
public class LogCut02 {

    public void cut(){}

    /**
     * 声明前置通知   并将通知应用到定义的切入点上
     * 目标类方法执行前 执行该通知
     *
     */

    public void before() {
        System.out.println("前置通知.....");
    }

    /**
     * 声明返回通知   并将通知应用到定义的切入点上
     * 目标类方法(无异常)执行后 执行该通知
     *
     */

    public void afterReturn() {
        System.out.println("返回通知.....");
    }

    /**
     * 声明最终通知   并将通知应用到定义的切入点上
     * 目标类方法(无异常或有异常)执行后 执行该通知
     *
     */

    public void after() {
        System.out.println("最终通知.....");
    }

    /**
     * 声明异常通知   并将通知应用到定义的切入点上
     * 目标类方法出现异常时 执行该通知
     */

    public void afterThrow(Exception e) {
        System.out.println("异常通知....." + "  异常原因:" + e.getCause());
    }

    /**
     *  声明环绕通知  并将通知应用到切入点上
     *  方法执行前后  通过环绕通知定义相应处理
     *      需要通过显式调用对应的方法,否则无法访问指定方法 (pjp.proceed();)
     * @param pjp
     * @return
     */

    public Object around(ProceedingJoinPoint pjp) {
        System.out.println("前置通知...");

        Object object = null;
        try {
            object = pjp.proceed();
            System.out.println(pjp.getTarget() + "======" + pjp.getSignature());
            // System.out.println("返回通知...");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("异常通知...");
        }
        System.out.println("最终通知...");

        return object;
    }

}

配置文件(spring.xml)

 <!--aop相关配置-->
<aop:config>
    <!--aop切面-->
    <aop:aspect ref="logCut02">
        <!-- 定义aop 切入点 -->
        <aop:pointcut id="cut" expression="execution(* com.xxxx.service..*.*(..))"/>
        <!-- 配置前置通知 指定前置通知方法名  并引用切入点定义 -->
        <aop:before method="before" pointcut-ref="cut"/>
        <!-- 配置返回通知 指定返回通知方法名  并引用切入点定义 -->
        <aop:after-returning method="afterReturn" pointcut-ref="cut"/>
        <!-- 配置异常通知  指定异常通知方法名  并引用切入点定义 -->
        <aop:after-throwing method="afterThrow" throwing="e" pointcut-ref="cut"/>
        <!-- 配置最终通知  指定最终通知方法名  并引用切入点定义 -->
        <aop:after method="after" pointcut-ref="cut"/>
        <!-- 配置环绕通知  指定环绕通知方法名  并引用切入点定义 -->
        <aop:around method="around" pointcut-ref="cut"/>
    </aop:aspect>
</aop:config>

Spring AOP总结

代理模式实现三要素

  1. 接口定义
  2. 目标对象与代理对象必须实现统一接口
  3. 代理对象持有目标对象的引用 增强目标对象行为

代理模式实现分类以及对应区别

  1. 静态代理:手动为目标对象制作代理对象,即在程序编译阶段完成代理对象的创建

  2. 动态代理:在程序运行期动态创建目标对象对应代理对象。

  3. jdk动态代理:被代理目标对象必须实现某一或某一组接口 实现方式 通过回调创建代理对象。

  4. cglib 动态代理:被代理目标对象可以不必实现接口,继承的方式实现。

    动态代理相比较静态代理,提高开发效率,可以批量化创建代理,提高代码复用率。

Aop 理解

  1. 面向切面,相比oop 关注的是代码中的层 或面
  2. 解耦,提高系统扩展性
  3. 提高代码复用

Aop 关键词

  1. 连接点:每一个方法
  2. 切入点:匹配的方法集合
  3. 切面:连接点与切入点的集合决定了切面,横切关注点的抽象
  4. 通知:几种通知
  5. 目标对象:被代理对象
  6. 织入:程序运行期将切面应用到目标对象 并生成代理对象的过程
  7. 引入:在不修改原始代码情况下,在程序运行期为程序动态引入方法或字段的过程

image

o
粉丝 0
博文 69
码字总数 0
作品 0
私信 提问
加载中
请先登录后再评论。
Netty那点事(三)Channel与Pipeline

Channel是理解和使用Netty的核心。Channel的涉及内容较多,这里我使用由浅入深的介绍方法。在这篇文章中,我们主要介绍Channel部分中Pipeline实现机制。为了避免枯燥,借用一下《盗梦空间》的...

黄亿华
2013/11/24
2W
22
访问安全控制解决方案

本文是《轻量级 Java Web 框架架构设计》的系列博文。 今天想和大家简单的分享一下,在 Smart 中是如何做到访问安全控制的。也就是说,当没有登录或 Session 过期时所做的操作,会自动退回到...

黄勇
2013/11/03
3.4K
6
Flappy Bird(安卓版)逆向分析(一)

更改每过一关的增长分数 反编译的步骤就不介绍了,我们直接来看反编译得到的文件夹 方法1:在smali目录下,我们看到org/andengine/,可以知晓游戏是由andengine引擎开发的。打开/res/raw/at...

enimey
2014/03/04
5.8K
18
我的架构演化笔记 功能1: 基本的用户注册

“咚咚”,一阵急促的敲门声, 我从睡梦中惊醒,我靠,这才几点,谁这么早, 开门一看,原来我的小表弟放暑假了,来南京玩,顺便说跟我后面学习一个网站是怎么做出来的。 于是有了下面的一段...

强子哥哥
2014/05/31
976
3
Swift百万线程攻破单例(Singleton)模式

一、不安全的单例实现 在上一篇文章我们给出了单例的设计模式,直接给出了线程安全的实现方法。单例的实现有多种方法,如下面: class SwiftSingleton { } 这段代码的实现,在shared中进行条...

一叶博客
2014/06/20
3.3K
16

没有更多内容

加载失败,请刷新页面

加载更多

SPSSAU 付费数据研究报告服务

SPSSAU-付费数据分析报告服务(周老师提供) 本文分享自微信公众号 - SPSSAU(spssau)。 如有侵权,请联系 support@oschina.cn 删除。 本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起...

SPSSAU
2017/11/08
0
0
芋艿-springcloud gateway

http://www.iocoder.cn/Spring-Cloud/Spring-Cloud-Gateway/?github springcloud gateway 官方文档 https://cloud.spring.io/spring-cloud-gateway/reference/html/#gatewayfilter-factories......

Java搬砖工程师
32分钟前
5
0
新零售小程序制作流程

最近有很多小伙伴们都在观望新零售小程序,其实新零售小程序制作还是比较简单的,只要你能熟知以下的新零售小程序制作流程,你也可以制作出属于自己的小程序。下面木鱼小铺(www.muyu007.cn)...

木鱼小铺小程序1
33分钟前
5
0
bat增加自定义参数

#xxx.bat --tag=dev1010 --context=3 --cpu=3 --memory=3 --build=1 --update=1 --api-version=1 @echo off setlocal enabledelayedexpansion set COMMANSLINE="%" :STR_VISTOR for /f "toke......

_snake_
35分钟前
3
0
谷歌SEO推广团队,这样管理更高效!

如今不论是外贸企业还是专业的海外推广公司都会组建自己的Google SEO推广团队,可以更有效的做好网站SEO,但是要发挥谷歌SEO推广团队的最大效能,我们并不能随意的让团队成员听之任之,随波逐...

一尘SEO
37分钟前
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部