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

Spring AOP

日志处理带来的问题

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

SpringAOP-02

最容易想到的方法

​ 对每一个字符方法均作日志代码的编写处理,以下面方式spring

SpringAOP-03

​ 缺点: 代码重复太多, 添加的日志代码耦合度过高(若是须要更改日志记录代码功能需求,类中方法须要所有改动,工程量浩大)express

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

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

代理模式:以上刚讲过。因而得出如下结构:安全

SpringAOP-04

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

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

​ 答案是确定的,存在这样的技术,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

相关文章
相关标签/搜索