Spring Aop使用

Spring Aop

基于AspectJ和基于schema的Aop命名的使用

在main方法中写上 System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”, “true”);可以在com.sun.proxy路径下看到生成的代理类class

基本概念

  • 连接点(Joinpoint) 所有可以执行额外代码的地方,比如方法前,方法后的时候
  • 切入点(Pointcut): 从众多的连接点根据条件找到实际执行代码的地方的点
  • 增强(Advice): 实际执行额外逻辑的代码
  • 目标对象(targer) :被增强目标类
  • 引介(Introduction):可以让目标类实现额外得方法,类似动态继承接口
  • 织入(weaving):将增强加入到切点得这个过程。有编译器织入(AspectJ原生),装载器织入(AspectJ原生),动态代理织入(spring原生aop,spring后面融入AspectJ,但是底层还是动态代理)
  • 代理(proxy):最后实际运行的类,功能和目标方法一样,只是加入了增强
  • 切面(Aspect):切入点+增强

简单使用

目标对象的接口以及实现

public interface Hello {
    void say();
    String tell(String mes);
}

public class HelloImpl implements Hello {
    @Override
    public void say() {
        System.out.println("hi");
    }

    @Override
    public String tell(String mes) {
        System.out.println(mes);
        return mes;
    }
}

实现一个切面

@Aspect
public class BeformAspect {


    @Before("execution(* say(..))")
    public void print()
    {
        System.out.println("before");
    }
}
  • @Aspect表示定位为一个切面
    在这里插入图片描述
    基于schema配置xml
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
       http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
	  <!--自动注入切面-->
      <aop:aspectj-autoproxy/>
      <!--切面-->
      <bean class="AspectJ.BeformAspect"/>
      <!--目标类-->
      <bean id="hello" class="AspectJ.HelloImpl"/>

</beans>

使用

   ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Hello hello = (Hello)applicationContext.getBean("hello");
hello.say();

AspectJ基本语法

切点表达式

在这里插入图片描述

在函数中使用通配符

符号作用
*匹配任意字符,但只能匹配上下文种的一个
…(两个点)匹配任意字符可以匹配上下文多个,但在表示类的时候,必须和*联合使用,表示入参单独使用
+必须跟在类名后,代表类的子类,包括自己

在这里插入图片描述

不同的增强类型

注解参数作用
@Before (前置)value定义切点
argNames可以获得目标对象的参数,参数名要和目标的参数名一致,多个已逗号隔开
@AfterReturning (后置)value定义切点
pointcut显示定义将覆盖value,和value同义
returning将目标的返回值绑定给增强方法
argNames同前面一样
@Around (环绕)value定义切点
argNames同前面一样
@AfterThrowing (异常)value定义切点
pointcut同前面一样
throwing将抛出的异常绑定到方法中
argNames同前面一样
@After (不管异常还是正常退出都会执行)value定义切点
argNames同前面一样
@DeclareParents (引介增强)value定义切点
defaultImpl默认的接口实现类

引介方法例子

比较特殊不同于其他增强类型的使用,
将HelloImpl融入SellerImpl类

public interface Hello {
    void say();
    String tell(String mes);
}

public class HelloImpl implements Hello {
    @Override
    public void say() {
        System.out.println("hi");
    }

    @Override
    public String tell(String mes) {
        System.out.println(mes);
        return mes;
    }
}

public interface Seller {
    String sell(String goods);
}

public class SellerImpl implements Seller{
    @Override
    public String sell(String goods) {
        System.out.println(goods);
        return null;
    }
}

介引切面

@Aspect
public class BeformAspect {

    @DeclareParents(value = "AspectJ.HelloImpl",defaultImpl = SellerImpl.class)
    public Seller seller;

}
  • value 切点,找到需要介引的目标
  • defaultImpl 找到需要增强的具体实现类
  • 下面的变量为defaultImpl的接口

配置文件

   <aop:aspectj-autoproxy />
      <bean class="AspectJ.BeformAspect"/>
      <bean id="hello" class="AspectJ.HelloImpl"/>

使用

        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        //可以将HelloImpl强转为Seller
        Seller hello = (Seller)applicationContext.getBean("hello");
        System.out.println(hello);

切点函数详解

@annotation

@Before("@annotation(AspectJ.Mytest)")

被注解标记的方法会执行切面注入

execution

通过方法签名定义

匹配 所有public的方法(其他修饰符不行),第一个*代表返回值 第二个代表方法名,(…)表示任意入参

@Before("execution(public * *(..))")

任意返回值,任意参数,方法名字以ll结尾的方法

@Before("execution(* *ll(..))")

通过类来定义

匹配Hello接口所有实现类,第一个*为返回值。(只包括接口定义的方法)

@Before("execution(* AspectJ.Hello.*(..))")

匹配Hello接口所有实现类,第一个*为返回值。(包括子类里不是接口的方法)

@Before("execution(* AspectJ.Hello+.*(..))")

通过类包来定义

这个包下的所有方法(如果子孙包中的类的父类或接口也在这个包下,也会触发)

@Before("execution(* AspectJ.*.*(..))")

这个包下的所有方法(包括子包孙包)

@Before("execution(* AspectJ..*.*(..))")

AspectJ包下的开头为x的包中类名以Son结尾,方法名为sa开头的所有方法

@Before("execution(* AspectJ.x*.*Son.sa*(..))")

通过方法入参来定义

匹配所有say方法,参数为两个,第一个类型为String

@Before("execution(* say(String,*))")

匹配所有say方法,参数为任意,第一个类型为String

@Before("execution(* say(String,..))")

匹配所有say方法,参数为一个,为Object以及其子类,(没有+号,代表只匹配object类型)

@Before("execution(* say(Object+))")

arg()和@arg()

arg()

根据入参的类型来匹配切点
args(Object) 等价于 execution(* (Object)) 也等价于 execution( *(Object+)) 也等价于args(Object+)

@arg()

该函数接受一个注解类的类名(接口无效)。当方法的运行时入参对象标注了指定的注解时,匹配切点。

mytest为自定义的注解

@Before("@args(Mytest)")
    public void test()
    {
        System.out.println("test");
    }

入参定义

public class helloSon {

    public void say(T1 t)
    {
        System.out.println("helloSon");
    }
}

参数类(T1 标注了自定义注解)

@Mytest
public class T1 {
}

public class T2 extends T1{
}

public class T3 extends T2{
}

public class T4 extends T3{
}

调用结果测试(注解没添加@Inherited)

  • 如果方法定义入参为T1,T1也标注了注解,那么在实际传参时T2,T3,T4也会被捕获。
  • 如果方法定义入参为T1,T2标注了注解,那么只有在实际传参为T2才会捕获。
  • 如果方法定义入参为T2,T1标注了注解,那么实际传参T2,T3,T4 都不会生效

(注解添加了@Inherited)

  • 只要实际传参的父类有注解都会生效

within()

execution 可以大到包,小到方法的定义,within等于简化版本,只能定义到类。

helloSon类所有方法被捕获

@Before("within(AspectJ.x.helloSon)")

AspectJ.x包下所有类被捕获(不包括子包,孙包。。)

@Before("within(AspectJ.x.*)")

AspectJ.x包下所有类被捕获(包括子包,孙包。。)

@Before("within(AspectJ.x..*)")

@within(),@target()

标注接口都不生效

被注解Mytest标注的类中所有方法被捕获

@Before("@target(Mytest)")

被注解Mytest标注的类(以及该类没有注解标记的子类)中所有方法被捕获

@Before("@within(Mytest)")

target(),this()

在这里插入图片描述

AspectJ进阶

切点的复合运算

在这里插入图片描述
例如

@Before("!@target(Mytest)")
@Before("@target(Mytest) || args(String,*)")
@Before("@target(Mytest) && args(String,*)")

切点命名,复用

切面1用@Pointcut定义了切点

@Aspect
public class BeformAspect {

    @Pointcut("@target(Mytest)")
    private void test()
    { }

    @Pointcut("args(String,*)")
    private void test1()
    { }

    @Pointcut("test() || test1()")
    public void cut()
    { }
}

切面2可以直接引用切面1定义好的切点

@Aspect
public class BeformAspect1 {


    @Before("BeformAspect.cut()")
    public void use()
    {
        System.out.println("hhhh");
    }
}

在切面1中,切点方法的访问控制决定是否可以在其他切面中发现,也可以继承切面进行扩展。

增强织入的顺序

在这里插入图片描述

访问连接点信息

使用JoinPoint接口表示目标类连接点对象,如果是环绕增强,则使用ProceedingJoinPoint表示连接点对象,该类是JoinPoint的子接口

JoinPoint

方法作用
Object[] getArgs()获取连接点方法运行时的入参列表
Signature getSignature()获取连接点的方法签名对象
Object getTarget()获取连接点所在的目标对象
Object getThis()获取代理对象本身

ProceedingJoinPoint 相对比 JoinPoint 多了两个方法

方法作用
Object proceed()通过反射执行目标连接点的方法
Object proceed(Object[] var1)通过反射执行目标连接点的方法,方法参数替换新的
@Aspect
public class TestAspect {

    @Around("within(com.ke.AspectJ.Around)")
    public void test(ProceedingJoinPoint pj) throws Throwable {

        Object[] args = pj.getArgs();
        System.out.println(Arrays.toString(args)); //获取调用目标方法的实际参数
        System.out.println(pj.getSignature().getName());//获取目标方法的名字
        System.out.println(pj.getTarget());//获取目标对象
        System.out.println(pj.getThis());//获取代理之后的对象

        System.out.println("before");
        pj.proceed();//执行目标方法
        System.out.println("after");

    }
}

绑定连接点方法入参

args(),this(),target(),@args(),@within(),@target(),@annotation()这几个可以参数绑定到增强方法上

  • agrs() 方法参数值
  • @annotation() 可以获取注解的内容
  • @args() 可以获取方法参数值上的注解
  • 其他几个获取对应的目标对象

agrs()例子:

@Around("args(cyz)")
    public void test(ProceedingJoinPoint pj,String cyz) throws Throwable {
        System.out.println(cyz);
        System.out.println("before");
        pj.proceed();//执行目标方法
        System.out.println("after");
    }
public class Around {

    public void say()
    {
        System.out.println("say");
    }
    public void say(String mes)
    {
        System.out.println(mes);
    }
}

该切点匹配到public void say(String mes)方法,@Around(“args(cyz)”)的cyz可以根据test方法对应的cyz参数名判断出类型为String。在执行增强方法时,cyz就是目标方法的实际入参

@annotation()例子

@Aspect
public class TestAspect {
    
    @Around("@annotation(cyz)")
    public void test(ProceedingJoinPoint pj,cyz cyz) throws Throwable {
        System.out.println(cyz.argNames());
        System.out.println("before");
        pj.proceed();//执行目标方法
        System.out.println("after");
    }
}
public class Around {

    public void say()
    {
        System.out.println("say");
    }

    @cyz(argNames = "s")
    public void say(String mes)
    {
        System.out.println(mes);
    }
}

可以获取注解的内容

@args()例子:

@cyz(argNames = "CYZ")
public class T {
}
///
public class Around {

    public void say()
    {
        System.out.println("say");
    }

    public void say(T mes)
    {
        System.out.println(mes);
    }
}
@Aspect
public class TestAspect {

    @Around("@args(cyz)")
    public void test(ProceedingJoinPoint pj,cyz cyz) throws Throwable {
        System.out.println(cyz.argNames());
        System.out.println("before");
        pj.proceed();//执行目标方法
        System.out.println("after");
    }
}

可以获取目标参数T类型上的注解内容

其余几个也类似,但是在增强方法中获取的对应的目标对象。

绑定返回值

	//经过测试 cyz的类型如果和目标方法不一致,不会被后置增强捕获
    @AfterReturning(value = "@args(Mytest)",returning = "cyz")
    public void us1e(String cyz)
    {
        System.out.println(cyz);
    }

    @Around(value = "@args(Mytest)")
    public void us1e(ProceedingJoinPoint j) throws Throwable {
		//o就是返回值
        Object o = j.proceed();
        System.out.println(o+"s");
    }

绑定抛出的异常

异常类型必须要和抛出的异常可以匹配上才会进这个增强,且在处理异常后依然会抛出异常

@AfterThrowing(value = "@args(Mytest)",throwing = "e")
    public void use(Throwable e)
    {
        System.out.println(e.getMessage());
    }

装载期织入切面

LWT织入切面是在装载class的时候就进行织入。前面的都是在运行期间生成代理对象织入

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring AOP 是一个用于实现面向切面编程的框架。它可以通过配置来实现横切关注点的模块化,并将其应用到程序的不同部分。Spring AOP 使用 AspectJ 切入点指示符来定义切入点表达式,用于匹配方法执行连接点。Spring AOP 支持以下 AspectJ 切入点指示符: 1. execution:用于匹配方法执行连接点。这是使用 Spring AOP 时要使用的主要切入点指示符。 2. within:限制匹配以连接某些类型中的点(使用 Spring AOP 时在匹配类型中声明的方法的执行)。 3. this:限制匹配到连接点(使用 Spring AOP 时方法的执行),其中 Bean 引用(Spring AOP 代理)是给定类型的实例。 4. target:限制匹配到连接点(使用 Spring AOP 时方法的执行),其中目标对象(正在代理的应用程序对象)是给定类型的实例。 5. args:限制匹配到连接点(使用 Spring AOP 时方法的执行),其中参数是给定类型的实例。 6. @target:限制匹配到连接点(使用 Spring AOP 时方法的执行),其中执行对象的类具有给定类型的注释。 7. @args:限制匹配到连接点(使用 Spring AOP 时方法的执行),其中传递的实际参数的运行时类型具有给定类型的注释。 8. @within:限制匹配以连接具有给定注释的类型中的点(使用 Spring AOP 时在具有给定注释的类型中声明的方法的执行)。 9. @annotation:限制匹配到连接点的主题(在 Spring AOP 中运行的方法)具有给定注释的连接点。 在使用 Spring AOP 时,需要引入 Spring AOPSpring Context 相关的包,并在配置文件中进行相应的配置。可以通过 Maven 或其他构建工具来引入相关依赖。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值