Spring&AOP入门笔记

上一篇-spring&代理

第五章 AOP

5.1 什么是AOP

AOP是Aspect Oriented Programming的缩写 ,译为面向切面编程。
举例:把编程的整个代码比喻作一个栋楼层,每一层楼房相当于一行代码,由于楼房才刚完成主体部分,还没有装修,把装修比作是AOP思想,现在开始一层层的装修楼房,就相当于使用AOP思想对代码进行增强。把窗户、门框、水、电等装修器材装修好,这些装修的器材就相当于AOP思想的增强代码,这些装修设备与楼房主体之间没有直接关系。装修在一起就形成了代码的增强,拆走装修设备也不影响主体结构,这就是SpringAOP思想。

5.2 AOP术语

1.连接点:英文名(Join point)代表类中的哪些方法可以被增强,能被增强的方法所处的位置称为连接点。

2.切入点:英文名(Pointcut)代表类中的连接点被增强的地方叫切入点,如果这个连接点被增强了就表示为切入点。

3.通知:英文名(Advice)代表对类中的方法何时执行增强操作,如:方法执行前进行增强;或者,方法执行后进行增强。

前置通知:Before Advice
环绕通知:Around Advice
后置通知:AfterReturning Advice
异常通知:AfterThrowing Advice
最终通知:After Advice

4.目标对象:英文名(Target object)被增强的对象称为目标对象,也称为被代理对象。

5.代理对象:英文名(Proxy Object)程序运行时动态生成的对象,称为代理对象。

6.织入:英文名(Weaving)把通知对象织入到目标对象的过程称为织入。

7.切面:英文名(Aspect) 整个通知与切入点的结合称为切面。

5.2.1 切面表达式

5.2.1.1切面表达式(expression)

通配符(wildcards)
* : 表示匹配任意字符,任意字符可以是权限修饰符,返回类型,包名,类名,方法名,参数等。
..: 表示匹配当前包以及子包。
+: 表示任意的子包和参数。

运算符(operators)
&&: 与 表示同时两个条件都满足。
||: 或 表示只要一个条件满足。
!: 非 表示取反。

指示器(designators)
表示通过什么样的方式去匹配需要操作的类的方法,比如,表示是通过注解方式,还是参数方式等。

execution:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)

问号(?)之前的参数可以选择不填。
modifiers-pattern:方法的可见性,如public,protected;可以选择不填。

ret-type-pattern:方法的返回值类型,如int,void等;

declaring-type-pattern:方法所在类的全路径名,如com.spring.Aspect;可以选择不填。

name-pattern:方法名类型,如save(),update();

param-pattern:方法的参数类型,如java.lang.String;

throws-pattern:方法抛出的异常类型,如java.lang.Exception;可以选择不填。

package com.lyf.spring5.aopdemo;

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

@Component
@Aspect
public class Expression {


    /**
     * 匹配所有的共有方法,返回任意类型  对add方法进行切入点连接
     */
    @Pointcut("execution(public * com.lyf.spring5.aopdemo.Calculator.add())")
    public void expr(){}

    /**
     * 匹配所有的共有方法,返回任意类型  对add方法进行切入点连接 (..)代表方法参数可变
     */
    @Pointcut("execution(public * com.lyf.spring5.aopdemo.Calculator.add(..))")
    public void expr1(){}

    /**
     * 匹配所有的共有方法,返回任意类型  (*)对任意方法进行切入点连接 (..)代表方法参数可变
     */
    @Pointcut("execution(public * com.lyf.spring5.aopdemo.Calculator.*(..))")
    public void expr2(){}

    /**
     * 匹配所有的共有方法,返回任意类型  (*add)对方法以add结尾进行切入点连接 (..)代表方法参数可变
     */
    @Pointcut("execution(public * com.lyf.spring5.aopdemo.Calculator.*add(..))")
    public void expr3(){}

    /**
     * 匹配所有的共有方法,返回任意类型 (右起:第二个*) 对aopdemo包下任意类的任意方法进行切入点连接 (..)代表方法参数可变
     */
    @Pointcut("execution(public * com.lyf.spring5.aopdemo.*.*(..))")
    public void expr4(){}

    /**
     * 匹配所有的共有方法,返回任意类型 (右起:第二个*当前.) 对aopdemo包以及子包的任意类的任意方法进行切入点连接 (..)代表方法参数可变
     */
    @Pointcut("execution(public * com.lyf.spring5.aopdemo..*.*(..))")
    public void expr5(){}
    /**
     * 匹配所有的共有方法,返回任意类型  对以aop包名开头的,以lator结尾的类名的任意方法进行切入点连接 (..)代表方法参数可变
     */
    @Pointcut("execution(public * com.lyf.spring5.aop*.*lator.*(..))")
    public void expr6(){}

    /**
     * 匹配所有的共有方法,返回任意类型  对以aop开头的包名以及子包,以lator结尾的类名的任意方法进行切入点连接 (..)代表方法参数可变
     */
    @Pointcut("execution(public * com.lyf.spring5.aop*..*lator.*(..))")
    public void expr7(){}

    /**
     * 返回任意类型  对以aop开头的包名以及子包,以lator结尾的类名的任意方法进行切入点连接 (..)代表方法参数可变
     */
    @Pointcut("execution( * com.lyf.spring5.aop*..*lator.add(..))")
    public void expr8(){}
    
    
    /**
     * 返回任意类型  对以aop开头的包名以及子包,以lator结尾的类名的任意方法进行切入点连接 (Object,..)代表方法参数第一个参数类型为Object类型
     */
    @Pointcut("execution( * com.lyf.spring5.aop*..*lator.add(Object,..))")
    public void expr9(){}


    /**
     * 返回任意类型  对以aop开头的包名以及子包,以lator结尾的类名的任意方法进行切入点连接 (String,*)代表方法参数第一个是string,第二个可以是任意类型
     */
    @Pointcut("execution( * com.lyf.spring5.aop*..*lator.add(String,*))")
    public void expr10(){}
  
}

2 within:
within为execution的一种简化模式 使用起来更便捷 但是他没有execution功能强大;其只能指明具体的类或具体目录下类中的方法


    /**
     * 匹配Calculator类下的所有方法
     */
    @Pointcut("within(com.lyf.spring5.aopdemo.Calculator)")
    public void with(){}


    /**
     * 匹配aopdemo包以及子包下的类的所有方法
     */
    @Pointcut("within(com.lyf.spring5.aopdemo..Calculator))")
    public void with1(){}

3args
匹配指定的参数



/**
 * 指明包含特定入参的方法,可以通过..进行多个参数的统配
 * args(String,Integer) 只能是第一个参数为String 第二个为Integer的才能匹配
 * args(String,Integer,..) 只要是前两个参数为String和Integer的都可以匹配上
 * args(..,Integer) 最后一个参数为Integer的都可以匹配上
 */
@Pointcut("args(String,Integer)")
public void arg1() {
}

4this
用于匹配代理类的对象,对生成的代理类进行增强。


/**
 * 这里指明了com.lyf.spring5.aopdemo.UserDaoImpl用于匹配代理后的对象
 * <p>
 * 为了方便测试 这里定义了一个接口UserDao UserDaoImpl实现了UserDao
 * <p>
 * 第一次测试,
 * 使用 @EnableAspectJAutoProxy 表示默认使用jdk动态代理
 * 那么spring获取对象是 UserDao userDaoImpl = ac.getBean(UserDaoImpl.class)
 * 这样userDaoImpl对象调用方法的时候,不会被增强
 * 原因是因为jdk代理是基于接口,那么生成的代理对象如下
 * public $UserDaoImplByJDK implements UserDao {
 *     publis UserDaoImpl userDaoImpl;
 *     public void busi1(){
 *        userDaoImpl.save();
 *     }
 *
 *     // other....
 * }
 * 所以$UserDaoImplByJDK 无法匹配上UserDaoImpl ,增强失败
 * <p>
 * <p>
 * ===============================================================
 * <p>
 * 第二次测试
 *
 * @EnableAspectJAutoProxy(proxyTargetClass = true) 强制使用cglib
 * 那么就会得到一个基于cglib的代理实现
 * 最终的代理对象效果如下
 * public UserDaoImpl$$EbhabcerBySpringCGLIB&&xxxx extend UserDaoImpl implements UserDao
 * {
 * // do something
 * }
 * 因为cglib代理是基于继承 因此生成的代理对象是UserDaoImpl的子类,所以这里就可以匹配上
 * 第二次测试执行的UserDaoImpl中的所有方法都是可以增强的
 */
@Pointcut("this(com.lyf.spring5.iocdemo.UserDaoImpl)")
public void this1 () {
}

5target
匹配动态代理之前的目标对象


/**
 * 由于是匹配的目标对象 因此就不会区分使用的什么代理方式了
 * 因此jdk和cglib代理的对象都是可以增强的
 */
@Pointcut("target(com.lyf.spring5.aopdemo.Calculator)")
public void target1() {
}

@开头的指示器都表示匹配注解类型的对象。
6@within
匹配加了指定注解的类


/**
 * 类上面加了指定注解,那么该类下所有的方法执行都增强
 * <p>
 * 注意:该方式只作用于类上面 如果注解只写在方法上没加在类上面,那么增强是无法生效的
 */
@Pointcut("@within(com.lyf.spring5.aopdemo.)")
public void anno_within_pc() {
}

7@Target
匹配的目标对象是否加了指定的注解



/**
 * 代理对象的时候 目标对象添加了指定注解类的方法执行都会进行增强
 */
@Pointcut("@target(com.lyf.spring5.annontation.Log)")
public void annoTarget() {
}

8@args


/**
 * 方法参数带有指定注解的
 * <p>
 * 如下测试,定义一个测试对象
 *
 * @Log public class BusiBean {
 * }
 * <p>
 * 如下的方法是会进行增强的
 * public String busi1(BusiBean busiBean)
 * <p>
 * 下面这种方式是不会进行增强的
 * public void busi2(@Log String arg1)
 */
@Pointcut("@args(com.llyf.aopdemo.Log)")
public void anno_args_pc() {
}

9@annotation
匹配加了指定注解的方法


/**
 * 如下的方法是不会进行增强的
 * public String busi1(BusiBean busiBean)
 * <p>
 * 下面这种方式是不会进行增强的
 * public void busi2(@Log String arg1)
 */

 * 加了指定注解的方法调用的时候都会进行增强
 * <p>
 * 注意:这里只有方法,加在类上面是没有用的,和@within是对立的
 */
@Pointcut("@annotation(com.lyf.aopdemo.Log)")
public void anno_pc1() {
}

当有多个表达式时,可以使用逻辑运算符

@Pointcut("@annotation(com.lyf.spring5。aopdemo.Log ) && args(String)")

5.2.1.2 通知时机 (Advice)

1 @Before: 在目标方法执行前进行增强。


	@Before("allMonitor()")
    public void monitor(){
        System.out.println("监视器方法执行。。。");
    }

2@AfterReturning: 在目标方法成功执行之后,进行增强。


    @AfterReturning("allLog()")
    public void log(){
        System.out.println("记录日志");
    }

3@AfterThrowing: 目标方法执行异常之后进行增强。

/**
     * 定义一个异常通知
     * @param throwing
     */
    @AfterThrowing(value = "aller()",throwing = "throwing")
    public void checker(Throwable throwing){
        System.out.println("运算异常:"+throwing.getMessage());
    }

4.@After: 在目标方法执行完之后进行增强,不管方法执行成功还是失败。


    @After("allLog()")
    public void log(){
        System.out.println("记录日志");
    }

5@Around: 坏绕通知 囊括了上面4种通知时机


/**
 * 坏绕通知 囊括了上面4种通知时机
 *
 * @param joinPoint
 * @return 响应数据
 * @throws Throwable
 */
@Around("anno_pc1()")
public Object advice4(ProceedingJoinPoint joinPoint) throws Throwable {
 //在这里相当于@Before

  System.out.println("aop around before.........");

 // 获取所有参数
  Object[] args = joinPoint.getArgs();

 try {
 // 执行目标方法并获取响应对象
    Object o = joinPoint.proceed(args);

 // 到这里相当于@AfterReturning
    System.out.println("aop around after returning.........");

 // 返回数据
 // 这里不同于@Before 这里如果不返回,调用方将拿不到响应数据
 return o;
  } catch (Throwable t) {

 // 这里相当于@AfterThrowing
    System.out.println("aop around after throwing.........");
 throw t;
  } finally {
 // 到这里相当于@After 异常和正常只会出现其中一种情况
    System.out.println("aop around after.........");
  }
}

5.3 AOP配置(xml)

创建一个新的maven项目。
导入spring与切面依赖

<!--        spring容器-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.10.RELEASE</version>
        </dependency>
<!--        aop切面-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!--        单元测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

定义一个目标对象

public class Calculator {

    public void add(){
        System.out.println("加法运算方法执行。。。");
    }

    public void subtract(){
        System.out.println("减法运算方法执行。。。");
    }

    public void multiply(){

        for (int i =0 ;i<1000;i++) {}
            System.out.println("乘法运算方法执行。。。");
        
    }

    public void divide(){

        System.out.println("除法运算方法执行。。。");
    }
}

定义代理对象

public class Check {

    public void check(){
        System.out.println("检查权限");
    }
}
public class Counter {

    private static int counter =0;
    public void counter(){
        System.out.println("统计方法调用次数:"+(++counter));
    }
}

public class Log {

    public void log(){
        System.out.println("记录日志");
    }
}
public class Monitor {

    public void monitor(){
        System.out.println("监视器方法执行。。。");
    }
}

配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<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.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

<!--    把所有的对象都创建到spring容器当中-->
    <bean id="calculator" class="com.lyf.spring5.aopdemo.Calculator"/>
    <bean id="check" class="com.lyf.spring5.aopdemo.Check"/>
    <bean id="counter" class="com.lyf.spring5.aopdemo.Counter"/>
    <bean id="log" class="com.lyf.spring5.aopdemo.Log"/>
    <bean id="monitor1" class="com.lyf.spring5.aopdemo.Monitor"/>

<!--    aop配置-->
    <aop:config>
<!--        定义切入点 expression表达式(权限修饰符 返回类型 包名+类名+方法名+(方法参数)抛出异常类型)-->
        <aop:pointcut id="add" expression="execution(public * com.lyf.spring5.aopdemo.Calculator.add(..) )"/>
        <aop:pointcut id="subtract" expression="execution(public * com.lyf.spring5.aopdemo.Calculator.subtract(..))"/>
        <aop:pointcut id="monitor" expression="execution(public * com.lyf.spring5.aopdemo.Calculator.*(..))"/>
        <aop:pointcut id="divide" expression="execution(public * com.lyf.spring5.aopdemo.Calculator.divide(..))"/>
        <aop:pointcut id="multiply" expression="execution(public * com.lyf.spring5.aopdemo.Calculator.multiply(..))"/>


<!--            定义切面 order 定义事务的优先级-->
        <aop:aspect ref="check" order="1">
<!--            定义通知  before表示前置通知方式  pointcut-ref 表示引用切入点-->
            <aop:before method="check" pointcut-ref="monitor" />

        </aop:aspect>
        <aop:aspect ref="log">
<!--            表示对切入点monitor执行后置通知 通知的代理对象是log,通知的目标对象是切入点expression匹配的方法-->
            <aop:after-returning method="log" pointcut-ref="monitor"/>
            <!--  表示对切入点multiply执行最终通知 通知的代理对象是log,通知的目标对象是切入点expression匹配的方法-->
            <aop:after method="log" pointcut-ref="multiply"/>
        </aop:aspect>

        <aop:aspect ref="counter">
            <aop:around method="counter" pointcut-ref="divide"/>
            <aop:after-returning method="counter" pointcut-ref="monitor"/>
        </aop:aspect>


        <aop:aspect ref="monitor1">
            <aop:after method="monitor" pointcut-ref="add"/>
            <aop:before method="monitor" pointcut-ref="add"/>
        </aop:aspect>
        
    </aop:config>
</beans>

测试代码

  @Test
    public void aopTest(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("aop/aopcontext.xml");
        Calculator calculator = applicationContext.getBean("calculator", Calculator.class);
        System.out.println("权限检查与日志记录功能开启");
        calculator.add();
        calculator.divide();
        calculator.multiply();
        calculator.subtract();
    }
  

运行结果
在这里插入图片描述

5.4 AOP配置(接口)

public class LogBefore implements MethodBeforeAdvice {

    /**
     *
     * @param method 要执行的目标对象的方法
     * @param objects 要执行目标对象方法的参数
     * @param o 目标对象
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {

        System.out.println(o.getClass().getName()+"的"+method.getName()+"方法被执行了");
    }
}
public class LogAfter implements AfterReturningAdvice {

    /**
     * 日志后置通知实现
     * @param returnValue 目标方法执行完的返回值
     * @param method 目标对象的方法
     * @param objects 目标对象方法的参数
     * @param object  目标对象
     * @throws Throwable
     */
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] objects, Object object) throws Throwable {
        System.out.println(object.getClass().getName()+"的"+method.getName()+"被执行了");
    }
}

    <bean id="after" class="com.lyf.spring5.aopdemo.LogAfter"/>
    <bean id="before" class="com.lyf.spring5.aopdemo.LogBefore"/>
   
    <aop:pointcut id="allLog" expression="execution(public * com.lyf.spring5.aopdemo.Calculator.*(..))"/>
    
  <aop:advisor advice-ref="after" pointcut-ref="allLog"/>
<aop:advisor advice-ref="before" pointcut-ref="allLog"/>

xml配置时 <aop:config></aop: config> 有顺序限制

aop:pointcut
aop:advisor
aop:aspect

5.5 AOP配置(半注解)

半注解方式就是在配置文件当中开启注解扫描。依赖与xml配置的依赖一致。

目标对象

package com.lyf.spring5.aopdemo;

import org.aopalliance.aop.Advice;
import org.springframework.stereotype.Repository;
@Repository
public class Calculator {

   public void add(){
       System.out.println("加法运算方法执行。。。");
   }

   public void subtract(){
       System.out.println("减法运算方法执行。。。");
   }

   public void multiply(){

       for (int i =0 ;i<1000;i++) {}
           System.out.println("乘法运算方法执行。。。");

   }

   public void divide(){

       System.out.println("除法运算方法执行。。。");
   }
}

@Component //定义当前类为组件,并加入到spring容器当中
@Aspect  // 定义当前类为一个切面
public class Check {

    //定义一个切入点
    @Pointcut(value = "execution(public * com.lyf.spring5.aopdemo.Calculator.*(..))")
    public void all(){}
    
  @Pointcut(value = "execution(public * com.lyf.spring5.aopdemo.Calculator.divide(..))")
    public void aller(){}
    /**
     * 定义一个通知 参数是一个切入点
     */
    @Before("all()")
    public void check(){
        System.out.println("检查权限");
    }
     /**
     * 定义一个异常通知
     * @param throwing
     */
    @AfterThrowing(value = "aller()",throwing = "throwing")
    public void checker(Throwable throwing){
        System.out.println("运算异常:"+throwing.getMessage());
    }
}

@Component
@Aspect
public class Counter {

    private static int counter =0;
    //定义一个切入点
    @Pointcut("execution(public * com.lyf.spring5.aopdemo.Calculator.multiply(..))")
    public void multiply(){};

    /**
     * 定义一个通知 参数是切入点
     */
    @AfterReturning(value = "multiply()")
    public void counter(){
        System.out.println("统计方法调用次数:"+(++counter));
    }
}

@Component
@Aspect
public class Log {

    @Pointcut("execution(public * com.lyf.spring5.aopdemo.Calculator.*(..))")
    public void allLog(){}

    @After("allLog()")
    public void log(){
        System.out.println("记录日志");
    }
}

@Component
@Aspect
public class Monitor {

    @Pointcut("execution(public * com.lyf.spring5.aopdemo.Calculator.add())")
    public void allMonitor(){}

    @Pointcut("execution(public * com.lyf.spring5.aopdemo.Calculator.add())")
    public void allMonitores(){}

    @Before("allMonitor()")
    @Around("allMonitores()")
    public void monitor(){
        System.out.println("监视器方法执行。。。");
    }
}


<?xml version="1.0" encoding="UTF-8" ?>
<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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

<!--    开启注解扫描-->
    <context:component-scan base-package="com.lyf.spring5.aopdemo"/>
<!--    开启AOP代理-->
    <aop:aspectj-autoproxy/>
</beans>

测试代码

    @Test
    public void aopAnnotationTest(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("aop/aopannotation.xml");
        Calculator calculator = applicationContext.getBean("calculator", Calculator.class);
        System.out.println("权限检查与日志记录功能开启");
        calculator.add();
        calculator.divide();
        calculator.multiply();
        calculator.subtract();
    }

运行结果
在这里插入图片描述

5.6 AOP配置(全注解)

依赖保持不变,目标类与代理类也不变。
添加一个配置类,实现全注解配置。

@Configuration  //表示当前是一个配置类
@ComponentScan({"com.lyf.spring5.aopdemo"}) //扫描aop注解所在的包路径
@EnableAspectJAutoProxy // 开启aop自动代理
public class AspectConfig {
}

测试代码


    @Test
    public void annotationTest(){
        ApplicationContext context = new AnnotationConfigApplicationContext(AspectConfig.class);
        Calculator calculator = context.getBean("calculator", Calculator.class);
        calculator.add();
        calculator.divide();
        calculator.multiply();
        calculator.subtract();
    }

运行结果
在这里插入图片描述

5.7 AOP配置(自定义实现)

public class DiyLog {

    public void beforeLog(){
        System.out.println("方法执行前");
    }

    public void afterLog(){
        System.out.println("方法执行之后");
    }
}

    <bean  id="diyLog" class="com.lyf.spring5.aopdemo.DiyLog"/>

 <aop:config>
        <aop:aspect ref="diyLog">
            <aop:pointcut id="logDiy" expression="execution(* com.lyf.spring5.aopdemo.Calculator.*(..))"/>
            <aop:before method="beforeLog" pointcut-ref="logDiy"/>
            
            <aop:after method="afterLog" pointcut-ref="logDiy"/>
        </aop:aspect>
    </aop:config>

5.8 AOP获取连接点信息(JoinPoint)

创建目标对象

package com.lyf.spring5.joinpoint;

import org.springframework.stereotype.Component;

@Component
public class Target {
    public String target(String name,Integer age){
        System.out.println("name:"+name);
        System.out.println("age:"+age);
        return name;
    }
}

创建代理对象


package com.lyf.spring5.joinpoint;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;


@Component
@Aspect
public class ProxyTarget {


    @Pointcut("execution( * com.lyf.spring5.joinpoint.*.*(..))")
    public void proxy(){}

    @Before("proxy()")
    public void target(JoinPoint joinPoint){
        //获取目标对象的声明类型 如:是类 还是接口,还是抽象类
        System.out.println("getDeclaringType "+joinPoint.getSignature().getDeclaringType());
        //获取目标对象的声明类型名称
        System.out.println("getDeclaringTypeName "+joinPoint.getSignature().getDeclaringTypeName());
        //获取目标对象的名称
        System.out.println("getName "+joinPoint.getSignature().getName());
       //获取修饰符 返回类型是数字,1 代表public
        System.out.println("getModifiers "+joinPoint.getSignature().getModifiers());
        //获取切入点的全部声明信息
        System.out.println("toLongString "+joinPoint.getSignature().toLongString());
        //获取切入点的类名.方法(参数)
        System.out.println("toShortString"+joinPoint.getSignature().toShortString());
        //获取目标类对象
        System.out.println("getTarget "+joinPoint.getTarget());
       //获取代理对象
        System.out.println("getThis "+joinPoint.getThis());
       //获取切入点指示器的声明类型 如 execution within this target 等
        System.out.println("getKind "+joinPoint.getKind());
       //获取静态部分
        System.out.println("getStaticPart "+joinPoint.getStaticPart());
       //获取资源所在路径
        System.out.println("getSourceLocation "+joinPoint.getSourceLocation());
        //获取所有参数
        Object[] args = joinPoint.getArgs();
        // 遍历输出所有参数
        for (Object obj: args) {
            System.out.println(obj);
        }
        // 获取目标对象 调用目标对象方法
      Target target = (Target) joinPoint.getTarget();
        String name = target.target("王二明", 12);
    }

    /**
     * ProceedingJoinPoint是JoinPoint的子类
     * @param joinPoint
     * @return
     */
    @Around("proxy()")
    public String around(ProceedingJoinPoint joinPoint){

        Object result = null;

        try {
            //  前置通知,相当于@Before
            System.out.println("目标方法之前");

            //执行目标方法,并接受目标方法返回值。该方法不需要参数
          //  result  = joinPoint.proceed();
            //执行目标方法,并接受目标方法返回值,该方法需要传人参数
            result  = joinPoint.proceed(new Object[]{"lisi",15});

                //后置通知,相当于@AfterReturning
            System.out.println("目标方法执行完成,返回通知");
        } catch (Throwable throwable) {

            // 异常通知 相当于 @AfterThrowing
            System.out.println("异常通知");
            throwable.printStackTrace();
        }finally {
            //最终通知 相当于@After
            System.out.println("最终通知");
        }
       return (String) result;
    }
}

测试代码

 @Test
    public void joinPoint(){
        Target target = (Target) new AnnotationConfigApplicationContext(TargetConfig.class).getBean("target");
        String s = target.target("网", 13);
        System.out.println(s);
    }

运行结果
在这里插入图片描述

5.8.1 获取注解

创建一个注解

package com.lyf.spring5.joinpoint;

import java.lang.annotation.*;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {

     String name() default "";
     String desc() default "";
}

创建目标类

@Component
public class Target {

    @Log(name = "日志注解",desc = "日志")
    public String target( String name,Integer age){
        System.out.println("name:"+name);
        System.out.println("age:"+age);
        return name;
    }

}

代理对象

package com.lyf.spring5.joinpoint;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component
@Aspect
public class Annot {


    @Pointcut("execution( * com.lyf.spring5.joinpoint.Target.*(..))")
    public void an(){}


    @Around("an()")
    public void annot(ProceedingJoinPoint joinPoint) throws Throwable {
            Object result =null;
        try {
            //获取签名
            Signature signature = joinPoint.getSignature();
            //将签名转换为方法签名
            MethodSignature methodSignature = (MethodSignature) signature;
            //方法签名获取方法对象
            Method method = methodSignature.getMethod();
            //方法对象获取注解
            Log log = method.getAnnotation(Log.class);
            //如果注解不为空,输出注解信息
            if (log !=null){
                System.out.println("注解名称:"+log.name());
                System.out.println("注解数字:"+log.desc());
            }
           result=joinPoint.proceed();
            return;
        }catch (Throwable throwable){
            System.out.println(throwable.getMessage());
        }

    }

}


测试代码


    @Test
    public void joinPoint(){
        Target target = (Target) new AnnotationConfigApplicationContext(TargetConfig.class).getBean("target");
        String s = target.target("网", 13);
        System.out.println(s);
    }

运行结果
在这里插入图片描述

总结

通过JoinPoint能获取到目标对象(被代理对象)的所有信息(类名,方法名,注解)等。
ProceedingJoinPoint该接口是JoinPoint的子接口,提供了proceed()方法去调用目标对象并拿到返回值。如果代理对象的方法参数是该接口,则通知类型必须是@Around类型。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值