目录
第五章 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类型。