1、简单介绍
如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP,如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换.本文中由于使用的是没接口的,所以会采用cglib实现动态代理。
2、注解说明
@Aspect表示该类是一个用于设置AOP切面相关的切面类,具体设置如下:
@Pointcut 来声明切入点表达式
@Order 注解指定切面的优先级, 值越小优先级越高
@Before在方法开始之前执行一段代码
@After在方法执行之后执行的代码. 无论该方法是否出现异常
@AfterReturning在方法法正常结束受执行的代码,返回通知是可以访问到方法的返回值的!
@AfterThrowing在目标方法出现异常时会执行的代码.可以访问到异常对象; 且可以指定在出现特定异常时在执行通知代码
@Around环绕通知需要携带 ProceedingJoinPoint 类型的参数.环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.且环绕通知必须有返回值, 返回值即为目标方法的返回值
@Configuration//用于定义配置类,可替换xml配置文件
@EnableAspectJAutoProxy(proxyTargetClass=true) //开启AspectJ 自动代理模式,如果不填proxyTargetClass=true,默认为false(代表即便你配置了@EnableAspectJAutoProxy,也不会开启代理模式,调试的时候我就被卡在这里蛮久…)
@ComponentScan(basePackages = “com.atguigu.spring5.configuration2”)//扫描注入类
3、示例代码:
<1>简单的bean对象 User.java
package com.atguigu.spring5.configuration2;
import org.springframework.stereotype.Component;
@Component("user")
public class User {
public void add(){
System.out.println("add ...");
}
}
<2>用于替换以前bean的xml配置文件的配置类 SpringConfiguration.java
package com.atguigu.spring5.configuration2;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
//即使用jdk默认代理模式,AspectJ代理模式是CGLIB代理模式
//如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
//如果目标对象实现了接口,可以强制使用CGLIB实现AOP (此例子我们就是强制使用cglib实现aop)
//如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
@Configuration//用于定义配置类,可替换xml配置文件
@EnableAspectJAutoProxy(proxyTargetClass=true) //开启AspectJ 自动代理模式,如果不填proxyTargetClass=true,默认为false
@ComponentScan(basePackages = "com.atguigu.spring5.configuration2")//扫描注入类
public class SpringConfiguration {
@Bean
public User user() {
return new User();
}
}
<3>用于替换bean的xml配置文件里AOP相关的配置类LoggingAspect .java
package com.atguigu.spring5.configuration2;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* 可以使用 @Order 注解指定切面的优先级, 值越小优先级越高
*/
//@Order(2)
@Aspect
@Component
public class LoggingAspect {
/**
* 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码.
* 使用 @Pointcut 来声明切入点表达式.
* 后面的其他通知直接使用方法名来引用当前的切入点表达式.
*/
@Pointcut("execution(public void com.atguigu.spring5.configuration2.User.*(..))")
public void declareJointPointExpression(){
}
/**
* 在 com.atguigu.spring5.aop.User 接口的每一个实现类的每一个方法开始之前执行一段代码
*/
@Before("declareJointPointExpression()")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
Object [] args = joinPoint.getArgs();
System.out.println("The beforeMethod " + methodName + " begins with " + Arrays.asList(args));
}
/**
* 在方法执行之后执行的代码. 无论该方法是否出现异常
*/
@After("declareJointPointExpression()")
public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("The afterMethod " + methodName + " ends");
}
/**
* 在方法法正常结束受执行的代码
* 返回通知是可以访问到方法的返回值的!
*/
@AfterReturning(value="declareJointPointExpression()",returning="result")
public void afterReturning(JoinPoint joinPoint, Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("The afterReturning " + methodName + " ends with " + result);
}
/**
* 在目标方法出现异常时会执行的代码.
* 可以访问到异常对象; 且可以指定在出现特定异常时在执行通知代码
*/
@AfterThrowing(value="declareJointPointExpression()",throwing="e")
public void afterThrowing(JoinPoint joinPoint, Exception e){
String methodName = joinPoint.getSignature().getName();
System.out.println("The afterThrowing " + methodName + " occurs excetion:" + e);
}
/**
* 环绕通知需要携带 ProceedingJoinPoint 类型的参数.
* 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.
* 且环绕通知必须有返回值, 返回值即为目标方法的返回值
*/
/*
@Around("execution(public int com.atguigu.springspring5.aop.User.*(..))")
public Object aroundMethod(ProceedingJoinPoint pjd){
Object result = null;
String methodName = pjd.getSignature().getName();
try {
//前置通知
System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
//执行目标方法
result = pjd.proceed();
//返回通知
System.out.println("The method " + methodName + " ends with " + result);
} catch (Throwable e) {
//异常通知
System.out.println("The method " + methodName + " occurs exception:" + e);
throw new RuntimeException(e);
}
//后置通知
System.out.println("The method " + methodName + " ends");
return result;
}
*/
}
<4>测试类TestAnnotationConfig.java
package com.atguigu.spring5.configuration2;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
public class TestAnnotationConfig {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfiguration.class);
User user = ctx.getBean(User.class);
user.add();
}
}
<5>运行测试类TestAnnotationConfig.java结果如下:
The beforeMethod add begins with []
add ...
The afterMethod add ends
The afterReturning add ends with null
Process finished with exit code 0