动态代理的分类
JDK动态代理和cglib动态代理
有接口: 使用JDK动态代理,生成接口实现类代理对象
代理对象和目标对象实现用样的接口
没有接口: 使用cglib动态代理,生成子类代理对象
继承被代理的目标类
具体操作:
第一步:引入aop依赖
<dependencies>
<!--spring aop依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>6.0.2</version>
</dependency>
<!--spring aspects依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.2</version>
</dependency>
<!--spring context依赖-->
<!--当你引入了此依赖之后,表示将spring的基础依赖引入了-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.2</version>
</dependency>
<!--junit-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.6.3</version>
</dependency>
<!-- log4j2的依赖-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.13.3</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.13.3</version>
</dependency>
</dependencies>
第二步:创建目标资源:1.接口 2.实现类
接口:
实现类:
第三步:创建切面类 :1.切入点 2.通知类型
接下来,创建一个切面类:
然后配置xml文件
1.引入context aop的相关约束
2.开启组件扫描,以及为了让spring认识我们的注解,我们还要开启aspectj代理
xml完整布局:
<?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:context="http://www.springframework.org/schema/context"
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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 开启组件扫描-->
<context:component-scan base-package="com.atguigu.spring.aop.anno"></context:component-scan>
<!-- 为了让spring认识我们的注解,所以我们要开启aspectj自助代理,为目标对象生成代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
回到切面类
接下来设置切入点和通知类型
通知类型包括:前置 返回 异常 后置 环绕
每种通知类型我们都需要建立一个方法,然后在方法上添加注解,用注解表示通知类型
//前置 @Before(value="切入点表达式") 通过切入点表达式来配置切入点
//返回 @AfterReturning()
//异常 @AfterThrowing()
//后置 @After()
//环绕 @Around()
切入点表达式
如图所示:
value="execution(访问修饰符 增强方法返回类型 增强方法所在类的全路径.方法名称(方法参数))"
创建测试类
运行一下...
报错了
原来是实现类没有加上注解@Component
给他加上!
再运行...
成功了
不同通知的属性和参数设置
如图
运行结果:
可以在每个方法里加入JoinPoint
通过加入JoinPoint来获取增强方法的方法名,输入的参数,返回值等信息,也可以加入ProceedingJoinPoint,
ProceedingJoinPoint 继承了 JoinPoint ,比 JoinPoint 方法更多,
返回和异常通知 可以在切面表达式中 配置额外的属性 来获取返回值和异常信息
returning = "res"
System.out.println("Logger-->返回通知,方法名称为:"+methodName+",返回值"+res);
//返回 @AfterReturning()
@AfterReturning(value = "execution(* com.atguigu.spring.aop.anno.CalculatorImpl.*(..)))",returning = "res")
public void AfterReturningMethod(JoinPoint joinPoint,Object res){
String methodName=joinPoint.getSignature().getName();
System.out.println("Logger-->返回通知,方法名称为:"+methodName+",返回值"+res);
}
throwing = "ex"
System.out.println("Logger-->异常通知,方法名称:"+methodName+",异常信息:"+ex);
//异常 @AfterThrowing() *目标方法出现异常,这个通知执行,获取目标方法的信息
@AfterThrowing(value ="execution(* com.atguigu.spring.aop.anno.CalculatorImpl.*(..)))",throwing = "ex")
public void AfterThrowingMethod(JoinPoint joinPoint,Throwable ex){
String methodName=joinPoint.getSignature().getName();
System.out.println("Logger-->异常通知,方法名称:"+methodName+",异常信息:"+ex);
}
完整代码
package com.atguigu.spring.aop.anno;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.lang.reflect.Array;
import java.util.Arrays;
//切面类
@Aspect //切面类
@Component //ioc容器
public class LogAspect {
//设置切入点和通知类型
//切入点表达式:execution(访问修饰符 增强方法返回类型 增强方法所在类的全路径.方法名称(方法参数))
//通知类型:
//前置 @Before()
@Before(value = "execution(public int com.atguigu.spring.aop.anno.CalculatorImpl.*(..))") //配置切入点
public void beforeMethod(JoinPoint joinPoint){
String methodName=joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println("Logger-->前置通知,方法名称:"+methodName+",方法参数:"+ Arrays.toString(args));
}
//返回 @AfterReturning()
@AfterReturning(value = "execution(* com.atguigu.spring.aop.anno.CalculatorImpl.*(..)))",returning = "res")
public void AfterReturningMethod(JoinPoint joinPoint,Object res){
String methodName=joinPoint.getSignature().getName();
System.out.println("Logger-->返回通知,方法名称为:"+methodName+",返回值"+res);
}
//异常 @AfterThrowing() *目标方法出现异常,这个通知执行,获取目标方法的信息
@AfterThrowing(value ="execution(* com.atguigu.spring.aop.anno.CalculatorImpl.*(..)))",throwing = "ex")
public void AfterThrowingMethod(JoinPoint joinPoint,Throwable ex){
String methodName=joinPoint.getSignature().getName();
System.out.println("Logger-->异常通知,方法名称:"+methodName+",异常信息:"+ex);
}
//后置 @After()
@After(value = "execution(* com.atguigu.spring.aop.anno.CalculatorImpl.*(..)))")
public void afterMethod(JoinPoint joinPoint){
String methodName=joinPoint.getSignature().getName();
System.out.println("Logger-->后置通知,方法名称为:"+methodName);
}
//环绕 @Around()
@Around(value = ("execution(* com.atguigu.spring.aop.anno.CalculatorImpl.*(..)))"))
public void AroundMethod(ProceedingJoinPoint joinPoint){
String methodName=joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
String string = Arrays.toString(args);
System.out.println("Logger-->环绕通知,方法名称为:"+methodName);
Object res=null;
try{
System.out.println("环绕方法==目标方法之前完成");
//调用目标方法
res = joinPoint.proceed();
System.out.println("环绕方法==目标方法返回值之后执行");
}catch (Throwable throwable){
throwable.printStackTrace();
System.out.println("环绕方法==目标方法出现异常执行");
}finally {
System.out.println("环绕方法==目标方法执行完毕执行");
}
}
}