使用完全注解的方式进行AOP功能实现(@Aspect+@Configuration+@EnableAspectJAutoProxy+@ComponentScan)

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

猜你喜欢

转载自blog.csdn.net/u010425839/article/details/115440816