Spring AOP

耐心读完相信你会有所收获 

目录

什么是AOP

初步认识Spring AOP

通过注解配置Spring AOP

通过注解 声明5种通知类型


什么是AOP

AOP直译过来就是 面向切面编程。AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。

将多个开发模块中某段重复的方法,抽出来。这

初步认识Spring AOP

Spring 中的 AOP 是通过动态代理实现的。不同的 AOP 框架支持的连接点也有所区别,例如,AspectJ 和 JBoss,除了支持方法切点,它们还支持字段和构造器的连接点。而 Spring AOP 不能拦截对对象字段的修改,也不支持构造器连接点,我们无法在 Bean 创建时应用通知。

package com.dome.test1;

public interface AB {
    String eat();
}
A实现AB接口

package com.dome.test1;

@Component
public class A implements AB {
    @Override
    public String aeat() {
        System.out.println("a吃饭");
        return "吃饭";
    }
}
B实现AB接口

package com.dome.test1;

@Component
public class B implements AB {
    @Override
    public String beat() {
        System.out.println("b不吃饭");
        return "不吃饭";
    }
}
配置文件
package com.demo;
@Configuration
@ComponentScan(basePackageClasses = {com.demo.test1.eat.class})
public class AppConfig {
}
测试类
package com.demo;

public class AppTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        A a = context.getBean("a",A.class);
        B b = (b) context.getBean("b");
        a.eat();
        b.eat();
    }
}

运行结果
a吃饭
b不吃饭

 如果我们想在a,b输出结果之前加入一个其他的方法是不是a,b两个方法都需要改呢。

这是就用到了Spring AOP很见简单的你就可以完成了。

package com.demo.test1;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class EatAspectJ {
    @Before("execution(* com.demo.test1.AB.eat(..))")
    public void ceshi(){
        System.out.println("AB喜欢的东西");
    }
}

 这个类,注解  @Component  表明它是一个Spring Bean 被装配,通过 @Aspect  表示它是一个切面。
类中方法  ceshi  前的@Before 注解,表示他将在方法执行之前执行。
参数 ("exection(* com.demo.test1.AB.eat(..))")声明了切点位置,表明在该位置切入切点。

package com.demo;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan(basePackageClasses = {com.demo.test1.AB.class})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {
}

 你能看到运行结果是:AB喜欢的东西

                                        a吃饭

                                     AB喜欢的东西

                                        b不吃饭

 

配置文件中的@EnableAspectJAutoProxy注解,是用于启动AOP功能的,参数proxyTargetClass的值设为True;如果我们不写参数,默认为 false。这个时候运行程序,程序抛出异常。

这是由于proxyTargetClass参数决定了代理的机制,当这个参数为false时,通过jdk的基于接口的方式进行织入,这时候代理生成的是一个接口对象,将这个接口对象强制转换为实现该接口的一个类,自然就抛出了上述类型转换异常。

当这个参数为true时,会使用cglib的动态代理方式这种方式的缺点是拓展类的方法被final修饰时,无法进行织入。

测试一下,把final加入aeat()方法中

A实现AB接口

package com.dome.test1;

@Component
public class A implements AB {
    @Override
    public final String aeat() {
        System.out.println("a吃饭");
        return "吃饭";
    }
}


运行结果:你可以看到切面组织唯有生效

通过注解配置Spring AOP

AspectJ指示器描述
arg()限制连接点匹配参数为指定类型的执行方法
@args()限制连接点匹配参数由指定注解标注的执行方法
execution()用于匹配是连接点的执行方法
this()限制连接点匹配AOP代理的Bean引用为指定类型的类
target()限制连接点匹配目标对象为指定类型的类
@target()限制连接点匹配特定的执行对象,这些对象对应的类要具备指定类型的注解
within()限制连接点匹配指定的类型
@within()限制连接点匹配指定注解所标注的类型(当前使用Spring AOP时,方法定义在有指定的注解所标注的类里)
@annotation限制连接点匹配带有指定注解连接点

 其他都是限制链接只有execution指示器是唯一的执行匹配,这说明execution在编写切点时非常的重要。

 execution(* com.demo.test1.AB.eat(...))

execution:在方法执行时触发
*:返回任意类型
com.demo.test1.AB :方法所属的类型
eat:此方法
... :使用任意参数


多个匹配之间我们可以使用链接符 &&、||、!来表示 “且”、“或”、“非”的关系。
但是在使用 XML 文件配置时,这些符号有特殊的含义,所以我们使用 “and”、“or”、“not”来表示。

package com.demo.test1;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class EatAspectJ {
    @Before("execution(* com.demo.test1.AB.eat(..)) 
    && within(* com.demo.*) && bean(girl)")
    public void ceshi(){
        System.out.println("AB喜欢的东西");
    }
}

此时运行时结果:  a吃饭

                                AB喜欢的东西

                                b不吃饭

通过注解 声明5种通知类型

注解通知
@Beforre通知方法会在目标方法调用之前执行
@After通知方法会在目标方法返回或异常后调用

@AfterReturning

通知方法会在目标方法返回调用
@AfterThrowing通知方法会在目标方法抛出异常后调用
@Around通知方法会目标方法封装起来

package com.sharpcj.aopdemo.test1;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class EatAspectJ {
    @Before("execution(* com.demo.test1.AB.eat(..))")
    public void hehe() {
        System.out.println("before ...");
    }

    @After("execution(* com.demo.test1.AB.eat(..))")
    public void haha() {
        System.out.println("After ...");
    }

    @AfterReturning("execution(* com.demo.test1.AB.eat(..))")
    public void xixi() {
        System.out.println("AfterReturning ...");
    }

    @Around("execution(* com.demo.test1.AB.eat(..))")
    public void xxx(ProceedingJoinPoint pj) {
        try {
            System.out.println("Around aaa ...");
            pj.proceed();
            System.out.println("Around bbb ...");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

}

测试类
package com.demo;

public class AppTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        A a = context.getBean("a",A.class);    
        a.eat();
    }
}

运行结果
Around aaa...
before...
a吃饭
Around bbb...
After ...
AfterReturning ...

 @Around修饰的环绕通知类型,将整个目标方法封装起来了,在使用时,我们传入了ProceedingJoinPoint  类型的参数,这个对象是必须要有的,并且需要调用ProceedingJoinPoint 的 proceed() 方法。 如果没有将不会环绕,

执行结果为
before...
a吃饭

Around aaa...
Around bbb...
After ...
AfterReturning ...

如果不调用对象proceed方法,表示原目标方法被堵塞了

多个通知使用相同的切点表达式,可以使用 @Pointcut 注解声明切点表达式

package com.sharpcj.aopdemo.test1;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class EatAspectJ {

    @Pointcut("execution(* com.demo.test1.AB.eat(..))")
    public void test(){}

    @Before("test()")
    public void hehe() {
        System.out.println("before ...");
    }

    @After("test()")
    public void haha() {
        System.out.println("After ...");
    }

    @AfterReturning("test())")
    public void xixi() {
        System.out.println("AfterReturning ...");
    }

    @Around("test()")
    public void xxx(ProceedingJoinPoint pj) {
        try {
            System.out.println("Around aaa ...");
            pj.proceed();
            System.out.println("Around bbb ...");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

}
    @Pointcut("execution(* com.demo.test1.AB.eat(..))")
    public void test(){}

这个方法相当于作为一个表示供通知使用。 

在说一下 *的用法,你如果不关心返回值是什么用 ,后边的 ... 表示任意参数。

如果需要传参数和需要返回值的时候,需要自己编写传参类型和返回值类型

编写返回类型和传入参数与未编写进行对比

package com.sharpcj.aopdemo.test1;

@Aspect
@Component
public class EatAspectJ {

    @Pointcut("execution(String com.demo.test1.AB.eat(String))")
    public void test(String a){}

    @Before("test(a)")
    public void hehe() {
        System.out.println("before ...");
    }


    @Around("test(a)")
    public void xxx(ProceedingJoinPoint pj,String a) {
        try {
            System.out.println("Around aaa ...");
            pj.proceed();
            System.out.println("Around bbb ...");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

@Pointcut("execution(* com.demo.test1.AB.eat(..))")
    public void test(){}

    @Before("test()")
    public void hehe() {
        System.out.println("before ...");
    }

    @After("test()")
    public void haha() {
        System.out.println("After ...");
    }

    @AfterReturning("test())")
    public void xixi() {
        System.out.println("AfterReturning ...");
    }

    @Around("test()")
    public void xxx(ProceedingJoinPoint pj) {
        try {
            System.out.println("Around aaa ...");
            pj.proceed();
            System.out.println("Around bbb ...");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

}

 

 参考来源Spring AOP——Spring 中面向切面编程 - SharpCJ - 博客园 (cnblogs.com)参考来源

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值