Spring_动态代理_通知的执行顺序_动态代理多个代理如何顺序-程序员宅基地

技术标签: Spring  


 

 

 

 


一、单切面-普通通知

 目标对象

@Service
public class MyMathCalculator{
	/**
	 * 加法
	 */
	public int add(int i, int j) {
		int result = i + j;
		System.out.println("方法内部执行");
		return result;
	}
	/**
	 * 减法
	 */
	public int sub(int i, int j) {
		int result = i - j;
		System.out.println("方法内部执行");
		return result;
	}
	/**
	 * 乘法
	 */
	public int mul(int i, int j) {
		//方法的兼容性;
		int result = i * j;
		System.out.println("方法内部执行");
		return result;
	}
	/**
	 * 除法
	 */
	public int div(int i, int j) {
		int result = i / j;
		System.out.println("方法内部执行");
		return result;
	}
}
@Aspect
@Component
public class LogUtils {
	/**
	 * @Before:在目标方法之前运行;  					 前置通知
	 * @After:在目标方法结束之后						后置通知
	 * @AfterReturning:在目标方法正常返回之后			返回通知
	 * @AfterThrowing:在目标方法抛出异常之后运行			异常通知
	 * @Around:环绕								环绕通知
	 */
	@Pointcut("execution(public int com.atguigu.impl.MyMathCalculator.*(..))")
	public void hahaMyPoint(){};

    //想在执行目标方法之前运行;写切入点表达式
	@Before("hahaMyPoint()")
	public static void logStart(JoinPoint joinPoint){
		//获取到目标方法运行是使用的参数
		Object[] args = joinPoint.getArgs();
		//获取到方法签名
		Signature signature = joinPoint.getSignature();
		String name = signature.getName();
		System.out.println("[LogUtils-前置]【"+name+"】方法开始执行,用的参数列表【"+Arrays.asList(args)+"】");
	}
    //想在目标方法正常执行完成之后执行
	@AfterReturning(value="hahaMyPoint()",returning="result")
	public static void logReturn(JoinPoint joinPoint,Object result){
		Signature signature = joinPoint.getSignature();
		String name = signature.getName();
		System.out.println("[LogUtils-返回]【"+name+"】方法正常执行完成,计算结果是:"+result);
	}
    //想在目标方法出现异常的时候执行
    @AfterThrowing(value="hahaMyPoint()",throwing="exception")
    public static void logException(JoinPoint joinPoint,Exception exception) {
        System.out.println("[LogUtils-异常]【"+joinPoint.getSignature().getName()+"】方法执行出现异常了,异常信息是【"+exception+"】:;这个异常已经通知测试小组进行排查");
    }

    //想在目标方法结束的时候执行
    @After("hahaMyPoint()")
	private int logEnd(JoinPoint joinPoint) {
		System.out.println("[LogUtils-后置]【"+joinPoint.getSignature().getName()+"】方法最终结束了");
		return 0;
	}
}
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");

@Test
public void test02(){
	MyMathCalculator bean = ioc.getBean(MyMathCalculator.class);
	bean.div(1, 1);
}

没有异常的输出: 

[LogUtils-前置]【div】方法开始执行,用的参数列表【[1, 1]】
方法内部执行
[LogUtils-后置]【div】方法最终结束了
[LogUtils-返回]【div】方法正常执行完成,计算结果是:1

异常输出: 

[LogUtils-前置]【div】方法开始执行,用的参数列表【[1, 0]】
[LogUtils-后置]【div】方法最终结束了
[LogUtils-异常]【div】方法执行出现异常了,异常信息是【java.lang.ArithmeticException: / by zero】:;这个异常已经通知测试小组进行排查

java.lang.ArithmeticException: / by zero

    at com.atguigu.impl.MyMathCalculator.div(MyMathCalculator.java:39)

 注意:这里的 后置通知(不管方法是否发生异常,都会执行) 都是在,返回通知(目标方法正常完成后被织入)和异常通知(目标方法发生异常后被织入)之前和我们的理解可能有点不同。


二、 单切面-环绕通知

@Aspect
@Component
public class LogUtils {

	@Pointcut("execution(public int com.atguigu.impl.MyMathCalculator.*(..))")
	public void hahaMyPoint(){};

    /**
     * @throws Throwable
     * @Around:环绕	:是Spring中强大的通知;
     * @Around:环绕:动态代理;
     * 	try{
     * 			//前置通知
     * 			method.invoke(obj,args);
     * 			//返回通知
     * 	}catch(e){
     * 			//异常通知
     *  }finally{
     * 			//后置通知
     * 	}
     *
     * 	四合一通知就是环绕通知;
     * 	环绕通知中有一个参数:	ProceedingJoinPoint pjp
     */

    @Around("hahaMyPoint()")
    public Object myAround(ProceedingJoinPoint pjp) throws Throwable{

        Object[] args = pjp.getArgs();
        String name = pjp.getSignature().getName();
        //args[0] = 100;
        Object proceed = null;
        try {
            //@Before
            System.out.println("【环绕前置通知】【"+name+"方法开始】");
            //就是利用反射调用目标方法即可,就是method.invoke(obj,args)
            proceed = pjp.proceed(args);
            //@AfterReturing
            System.out.println("【环绕返回通知】【"+name+"方法返回,返回值"+proceed+"】");
        } catch (Exception e) {
            //@AfterThrowing
            System.out.println("【环绕异常通知】【"+name+"】方法出现异常,异常信息:"+e);
            //为了让外界能知道这个异常,这个异常一定抛出去
            throw new RuntimeException(e);
        } finally{
            //@After
            System.out.println("【环绕后置通知】【"+name+"】方法结束");
        }

        //反射调用后的返回值也一定返回出去
        return proceed;
    }
    
}

没有异常的输出:  

【环绕前置通知】【div方法开始】
方法内部执行
【环绕返回通知】【div方法返回,返回值1】
【环绕后置通知】【div】方法结束

异常输出: 

【环绕前置通知】【div方法开始】
【环绕异常通知】【div】方法出现异常,异常信息:java.lang.ArithmeticException: / by zero
【环绕后置通知】【div】方法结束

java.lang.RuntimeException: java.lang.ArithmeticException: / by zero

    at com.atguigu.utils.LogUtils.myAround(LogUtils.java:62)

注:环绕通知是不存在前面的顺序问题的 


三、 单切面-环绕通知-普通通知

@Aspect
@Component
public class LogUtils {

/**
 * 告诉Spring每个方法都什么时候运行;
 * try{
 * 		@Before
 * 		method.invoke(obj,args);
 * 		@AfterReturning
 * }catch(e){
 * 		@AfterThrowing
 * }finally{
 * 		@After
 * }
 *
 * 5个通知注解
 * @Before:在目标方法之前运行;  					 前置通知
 * @After:在目标方法结束之后						后置通知
 * @AfterReturning:在目标方法正常返回之后			返回通知
 * @AfterThrowing:在目标方法抛出异常之后运行			异常通知
 * @Around:环绕								环绕通知
 *
 *
 * 抽取可重用的切入点表达式;
 * 1、随便声明一个没有实现的返回void的空方法
 * 2、给方法上标注@Pointcut注解
 */
@Pointcut("execution(public int com.atguigu.impl.MyMathCalculator.*(..))")
public void hahaMyPoint(){};


//想在执行目标方法之前运行;写切入点表达式
//execution(访问权限符  返回值类型  方法签名)
@Before("hahaMyPoint()")
public static void logStart(JoinPoint joinPoint){
    //获取到目标方法运行是使用的参数
    Object[] args = joinPoint.getArgs();
    //获取到方法签名
    Signature signature = joinPoint.getSignature();
    String name = signature.getName();
    System.out.println("[LogUtils-前置]【"+name+"】方法开始执行,用的参数列表【"+Arrays.asList(args)+"】");
}

//想在目标方法正常执行完成之后执行
@AfterReturning(value="hahaMyPoint()",returning="result")
public static void logReturn(JoinPoint joinPoint,Object result){
    Signature signature = joinPoint.getSignature();
    String name = signature.getName();
    System.out.println("[LogUtils-返回]【"+name+"】方法正常执行完成,计算结果是:"+result);
}


//想在目标方法出现异常的时候执行
@AfterThrowing(value="hahaMyPoint()",throwing="exception")
public static void logException(JoinPoint joinPoint,Exception exception) {
    System.out.println("[LogUtils-异常]【"+joinPoint.getSignature().getName()+"】方法执行出现异常了,异常信息是【"+exception+"】:;这个异常已经通知测试小组进行排查");
}

//想在目标方法结束的时候执行
@After("hahaMyPoint()")
private int logEnd(JoinPoint joinPoint) {
    System.out.println("[LogUtils-后置]【"+joinPoint.getSignature().getName()+"】方法最终结束了");
    return 0;
}

/**
 * @throws Throwable
 * @Around:环绕	:是Spring中强大的通知;
 * @Around:环绕:动态代理;
 * 	try{
 * 			//前置通知
 * 			method.invoke(obj,args);
 * 			//返回通知
 * 	}catch(e){
 * 			//异常通知
 *  }finally{
 * 			//后置通知
 * 	}
 *
 * 	四合一通知就是环绕通知;
 * 	环绕通知中有一个参数:	ProceedingJoinPoint pjp
 *
 *环绕通知:是优先于普通通知执行,执行顺序;
 *
 *[普通前置]
 *{
 *	try{
 *		环绕前置
 *		环绕执行:目标方法执行
 *		环绕返回
 *	}catch(){
 *		环绕出现异常
 *	}finally{
 *		环绕后置
 *	}
 *}
 *
 *
 *[普通后置]
 *[普通方法返回/方法异常]
 *新的顺序:
 *		(环绕前置---普通前置)----目标方法执行----环绕正常返回/出现异常-----环绕后置----普通后置---普通返回或者异常
 *注意:
 */

@Around("hahaMyPoint()")
public Object myAround(ProceedingJoinPoint pjp) throws Throwable{

    Object[] args = pjp.getArgs();
    String name = pjp.getSignature().getName();
    //args[0] = 100;
    Object proceed = null;
    try {
        //@Before
        System.out.println("【环绕前置通知】【"+name+"方法开始】");
        //就是利用反射调用目标方法即可,就是method.invoke(obj,args)
        proceed = pjp.proceed(args);
        //@AfterReturing
        System.out.println("【环绕返回通知】【"+name+"方法返回,返回值"+proceed+"】");
    } catch (Exception e) {
        //@AfterThrowing
        System.out.println("【环绕异常通知】【"+name+"】方法出现异常,异常信息:"+e);
        //为了让外界能知道这个异常,这个异常一定抛出去
        throw new RuntimeException(e);
    } finally{
        //@After
        System.out.println("【环绕后置通知】【"+name+"】方法结束");
    }

    //反射调用后的返回值也一定返回出去
    return proceed;
}
}

没有异常的输出:   

环绕前置通知】【div方法开始】
[LogUtils-前置]【div】方法开始执行,用的参数列表【[1, 1]】
方法内部执行
环绕返回通知】【div方法返回,返回值1】
环绕后置通知】【div】方法结束

[LogUtils-后置]【div】方法最终结束了
[LogUtils-返回]【div】方法正常执行完成,计算结果是:1

异常输出:  

环绕前置通知】【div方法开始】
[LogUtils-前置]【div】方法开始执行,用的参数列表【[1, 0]】
环绕异常通知】【div】方法出现异常,异常信息:java.lang.ArithmeticException: / by zero
环绕后置通知】【div】方法结束

[LogUtils-后置]【div】方法最终结束了
[LogUtils-异常]【div】方法执行出现异常了,异常信息是【java.lang.RuntimeException: java.lang.ArithmeticException: / by zero】:;这个异常已经通知测试小组进行排查

java.lang.RuntimeException: java.lang.ArithmeticException: / by zero

    at com.atguigu.utils.LogUtils.myAround(LogUtils.java:143)

注:如果环绕通知显示的抛出异常,那么普通通知是感知不到异常的,这个时候普通通知还是正常执行正常返回。 


 四、多个切面-普通通知

@Aspect
@Component
@Order(1)//使用Order改变切面顺序;数值越小优先级越高
public class LogUtils {

@Pointcut("execution(public int com.atguigu.impl.MyMathCalculator.*(..))")
public void hahaMyPoint(){};

//想在执行目标方法之前运行;写切入点表达式
//execution(访问权限符  返回值类型  方法签名)
@Before("hahaMyPoint()")
public static void logStart(JoinPoint joinPoint){
    //获取到目标方法运行是使用的参数
    Object[] args = joinPoint.getArgs();
    //获取到方法签名
    Signature signature = joinPoint.getSignature();
    String name = signature.getName();
    System.out.println("[LogUtils-前置]【"+name+"】方法开始执行,用的参数列表【"+Arrays.asList(args)+"】");
}

//想在目标方法正常执行完成之后执行
@AfterReturning(value="hahaMyPoint()",returning="result")
public static void logReturn(JoinPoint joinPoint,Object result){
    Signature signature = joinPoint.getSignature();
    String name = signature.getName();
    System.out.println("[LogUtils-返回]【"+name+"】方法正常执行完成,计算结果是:"+result);
}

//想在目标方法出现异常的时候执行
@AfterThrowing(value="hahaMyPoint()",throwing="exception")
public static void logException(JoinPoint joinPoint,Exception exception) {
    System.out.println("[LogUtils-异常]【"+joinPoint.getSignature().getName()+"】方法执行出现异常了,异常信息是【"+exception+"】:;这个异常已经通知测试小组进行排查");
}

//想在目标方法结束的时候执行
@After("hahaMyPoint()")
private int logEnd(JoinPoint joinPoint) {
    System.out.println("[LogUtils-后置]【"+joinPoint.getSignature().getName()+"】方法最终结束了");
    return 0;
}

}
@Aspect
@Component
@Order(2)
public class ValidateApsect {


@Before("com.atguigu.utils.LogUtils.hahaMyPoint()")
public void logStart(JoinPoint joinPoint){
	Object[] args = joinPoint.getArgs();
	Signature signature = joinPoint.getSignature();
	String name = signature.getName();
	System.out.println("[VaApsect-前置]【"+name+"】方法开始执行,用的参数列表【"+Arrays.asList(args)+"】");
}

@AfterReturning(value="com.atguigu.utils.LogUtils.hahaMyPoint()",returning="result")
public void logReturn(JoinPoint joinPoint,Object result){
	Signature signature = joinPoint.getSignature();
	String name = signature.getName();
	System.out.println("[VaApsect-返回]【"+name+"】方法正常执行完成,计算结果是:"+result);
}

@AfterThrowing(value="com.atguigu.utils.LogUtils.hahaMyPoint()",throwing="exception")
public void logException(JoinPoint joinPoint,Exception exception) {
	System.out.println("[VaApsect-异常]【"+joinPoint.getSignature().getName()+"】方法执行出现异常了,异常信息是【"+exception+"】:;这个异常已经通知测试小组进行排查");
}

@After("com.atguigu.utils.LogUtils.hahaMyPoint()")
private int logEnd(JoinPoint joinPoint) {
	System.out.println("[VaApsect-后置]【"+joinPoint.getSignature().getName()+"】方法最终结束了");
	return 0;
}
}

 没有异常的输出:  

[LogUtils-前置]【div】方法开始执行,用的参数列表【[1, 1]】
[VaApsect-前置]【div】方法开始执行,用的参数列表【[1, 1]】
方法内部执行
[VaApsect-后置]【div】方法最终结束了
[VaApsect-返回]【div】方法正常执行完成,计算结果是:1
[LogUtils-后置]【div】方法最终结束了
[LogUtils-返回]【div】方法正常执行完成,计算结果是:1

异常输出:  

[LogUtils-前置]【div】方法开始执行,用的参数列表【[1, 0]】
[VaApsect-前置]【div】方法开始执行,用的参数列表【[1, 0]】
[VaApsect-后置]【div】方法最终结束了
[VaApsect-异常]【div】方法执行出现异常了,异常信息是【java.lang.ArithmeticException: / by zero】:;这个异常已经通知测试小组进行排查
[LogUtils-后置]【div】方法最终结束了
[LogUtils-异常]【div】方法执行出现异常了,异常信息是【java.lang.ArithmeticException: / by zero】:;这个异常已经通知测试小组进行排查

java.lang.ArithmeticException: / by zero

    at com.atguigu.impl.MyMathCalculator.div(MyMathCalculator.java:39)

 注:通知的执行顺序就是先进后出的执行顺序。谁先执行是按照通知类的类名字典序排序,当然可以用 @Order() 指定,值越小越靠前。

 

[LogUtils-前置]【add】方法开始执行,用的参数列表【[1, 1]】
[VaApsect-前置]【add】方法开始执行,用的参数列表【[1, 1]】
方法内部执行
[VaApsect-后置]【add】方法最终结束了
[VaApsect-返回]【add】方法正常执行完成,计算结果是:2
[LogUtils-后置]【add】方法最终结束了
[LogUtils-返回]【add】方法正常执行完成,计算结果是:2

五、多切面-普通通知-环绕通知

现在我们给 LogUtils 加上环绕通知 

@Aspect
@Component
@Order(1)//使用Order改变切面顺序;数值越小优先级越高
public class LogUtils {
    
@Pointcut("execution(public int com.atguigu.impl.MyMathCalculator.*(..))")
public void hahaMyPoint(){};




//想在执行目标方法之前运行;写切入点表达式
//execution(访问权限符  返回值类型  方法签名)
@Before("hahaMyPoint()")
public static void logStart(JoinPoint joinPoint){
    //获取到目标方法运行是使用的参数
    Object[] args = joinPoint.getArgs();
    //获取到方法签名
    Signature signature = joinPoint.getSignature();
    String name = signature.getName();
    System.out.println("[LogUtils-前置]【"+name+"】方法开始执行,用的参数列表【"+Arrays.asList(args)+"】");
}

//想在目标方法正常执行完成之后执行
@AfterReturning(value="hahaMyPoint()",returning="result")
public static void logReturn(JoinPoint joinPoint,Object result){
    Signature signature = joinPoint.getSignature();
    String name = signature.getName();
    System.out.println("[LogUtils-返回]【"+name+"】方法正常执行完成,计算结果是:"+result);
}


//想在目标方法出现异常的时候执行
@AfterThrowing(value="hahaMyPoint()",throwing="exception")
public static void logException(JoinPoint joinPoint,Exception exception) {
    System.out.println("[LogUtils-异常]【"+joinPoint.getSignature().getName()+"】方法执行出现异常了,异常信息是【"+exception+"】:;这个异常已经通知测试小组进行排查");
}

//想在目标方法结束的时候执行
@After("hahaMyPoint()")
private int logEnd(JoinPoint joinPoint) {
    System.out.println("[LogUtils-后置]【"+joinPoint.getSignature().getName()+"】方法最终结束了");
    return 0;
}

@Around("hahaMyPoint()")
public Object myAround(ProceedingJoinPoint pjp) throws Throwable{
    
    Object[] args = pjp.getArgs();
    String name = pjp.getSignature().getName();
    //args[0] = 100;
    Object proceed = null;
    try {
        //@Before
        System.out.println("【环绕前置通知】【"+name+"方法开始】");
        //就是利用反射调用目标方法即可,就是method.invoke(obj,args)
        proceed = pjp.proceed(args);
        //@AfterReturing
        System.out.println("【环绕返回通知】【"+name+"方法返回,返回值"+proceed+"】");
    } catch (Exception e) {
        //@AfterThrowing
        System.out.println("【环绕异常通知】【"+name+"】方法出现异常,异常信息:"+e);
        //为了让外界能知道这个异常,这个异常一定抛出去
        throw new RuntimeException(e);
    } finally{
        //@After
        System.out.println("【环绕后置通知】【"+name+"】方法结束");
    }
    
    //反射调用后的返回值也一定返回出去
    return proceed;
}
}

 没有异常的输出:   

【环绕前置通知】【div方法开始】
[LogUtils-前置]【div】方法开始执行,用的参数列表【[1, 1]】
[VaApsect-前置]【div】方法开始执行,用的参数列表【[1, 1]】
方法内部执行
[VaApsect-后置]【div】方法最终结束了
[VaApsect-返回]【div】方法正常执行完成,计算结果是:1

【环绕返回通知】【div方法返回,返回值1】
【环绕后置通知】【div】方法结束

[LogUtils-后置]【div】方法最终结束了
[LogUtils-返回]【div】方法正常执行完成,计算结果是:1

异常输出:   

【环绕前置通知】【div方法开始】
[LogUtils-前置]【div】方法开始执行,用的参数列表【[1, 0]】
[VaApsect-前置]【div】方法开始执行,用的参数列表【[1, 0]】
[VaApsect-后置]【div】方法最终结束了
[VaApsect-异常]【div】方法执行出现异常了,异常信息是【java.lang.ArithmeticException: / by zero】:;这个异常已经通知测试小组进行排查
【环绕异常通知】【div】方法出现异常,异常信息:java.lang.ArithmeticException: / by zero
【环绕后置通知】【div】方法结束
[LogUtils-后置]【div】方法最终结束了
[LogUtils-异常]【div】方法执行出现异常了,异常信息是【java.lang.RuntimeException: java.lang.ArithmeticException: / by zero】:;这个异常已经通知测试小组进行排查

java.lang.RuntimeException: java.lang.ArithmeticException: / by zero

    at com.atguigu.utils.LogUtils.myAround(LogUtils.java:86)

 

LogProxy{
	环绕前置
	method.invoke(){
		VaProxy{
			va前置
	   		  target
			va后置
			va返回
		}

	}
	环绕返回
	环绕后置
}
1、Spring-IOC-AOP(动态代理);多层代理
               LogAspectpRroxy{
                         try{
                              @Before
                              method.invoke()//pjp.procced(args){
                                        BAspectProxy{
                                                       @Before
                                                       method.invoke()//---目标方法
                                                         @AfterReturning
                                                        //xxxxxxxx
                                                            //修改了返回值
                                        }
                              }
                              @AfterReturning
                         }catch(e){
                               @AfterThrowing
                         }finally{
                               @After 
                         }        
               }

 注:LogUtils 里面的环绕通知,跟ValidateApsect里面的通知没有关系,他只是影响当前切面


六、如何选择通知

由于环绕通知比较强大,他能直接控制目标方法的执行。如果我们是做日志记录等仅仅感知目标方法执行,我们使用普通通知即可。如果我需要写一一个动态代理,要影响目标方法,那么我就使用环绕通知。


<?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-4.0.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

	<context:component-scan base-package="com.atguigu"/>

	<!--  开启基于注解的AOP功能;aop名称空间-->
	<aop:aspectj-autoproxy/>
</beans>
<dependencies>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>4.0.0.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.0.0.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>4.0.0.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-expression -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-expression</artifactId>
        <version>4.0.0.RELEASE</version>
    </dependency>


    <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.1.3</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>4.0.0.RELEASE</version>
    </dependency>

    <!--基本切面包-->
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>4.0.0.RELEASE</version>
    </dependency>

    <!--切面plus包-->

    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.6.12</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.6.12</version>
    </dependency>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>2.2</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/junit/junit -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>


</dependencies>

 

 

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_40794973/article/details/97317835

智能推荐

iOS 证书管理、验证、打包流程_怎么查看ipa用哪个证书打包的-程序员宅基地

文章浏览阅读1.1k次。背景iOS软件的开发和发布离不开证书和配置文件,如果要想发布app到Apple Store或者使用苹果的推送通知功能,都需要个人开发者证书签名该app,以便通过苹果的认证和审核。由于我们公司的app不是单独一个,而是一个客户对应一个app,在新版本中,需要用到推送通知功能,就需要发布app到Apple Store,通过认证后才能正常使用苹果提供的这个服务,同时,为了满足部分客户要把自己的app发布..._怎么查看ipa用哪个证书打包的

Mac 储存空间“其他”占据这么多?一招带你搞定_其他用户与共享 mac 数据删除-程序员宅基地

文章浏览阅读3.5k次。手动清除这些“其他”文件的方法比较适合专业的、熟悉Mac规则的用户。对于普通Mac用户,如果怕误删系统文件,还是将清理“其他”文件的任务交给专业的第三方清理工具吧,例如CleanMyMac X。2、在“访达”中选中用户名,右侧空白处,右键“查看显示选项”-勾选“显示‘资源库’文件夹”1、打开“访达”-“偏好设置”-“边栏”中,勾选“个人用户名”;1、点击左上角  -“关于本机”-“储存空间”-“管理”;2、点击“文稿”-“文件浏览器”-“资源库”;1)安装应用程序,点击侧边栏中的“系统垃圾”。..._其他用户与共享 mac 数据删除

区块链赋能供应链金融风险管控探析_区块链对风险防控的作用-程序员宅基地

文章浏览阅读717次。本文转自:人民银行本溪市中心支行供应链金融是一种从产业链中来到产业链中去,不断夯实和优化产业链的生态金融,为有效破解中小微企业融资难开辟了一条新途径。但实践中,供应链金融风险管理一直面临难以突破的瓶颈,供应链金融风险无法准确评估和度量,履约风险无法有效控制,而且信息不对称大幅拉升风险管理成本,严重拖累了供应链金融的持续稳健发展,业务辐射范围受限。而区块链作为构建数字信任机制的全新模式,具有分布式记账、可追溯、不可篡改等技术特性,恰好直击供应链金融风控的痛点,为助推供应链金融稳健发展提供了切实可行的解._区块链对风险防控的作用

docker中mysql容器自动停止原因及解决方法_为什么我部署在docker里面的mysql,会被关闭-程序员宅基地

文章浏览阅读6.5k次,点赞3次,收藏23次。docker update -m 400M --memory-reservation 400M --memory-swap 500M 容器名。将docker.cnf 复制到mysql容器内(容器名可用docker ps -a 查看到name列,即为容器名)sudo docker cp ./docker.cnf 容器名:/etc/mysql/conf.d。第五步:限制mysql内存占用(本机器内存为1G,可视自己的机器内容设置)查看设置内容后docker容器内存使用情况:docker stats。_为什么我部署在docker里面的mysql,会被关闭

【K8S系列】深入解析K8S存储-程序员宅基地

文章浏览阅读2.3w次,点赞91次,收藏122次。在 Kubernetes 中,存储具有非常广泛的应用场景。可以根据实际需求选择适合自己的存储方案,以便更好地管理容器化应用程序中的数据和资源。本文会从以下三个方面,带你了解k8s存储:1.k8s存储类型;2.存储使用场景;3.存储使用案例

oracle 重新编译用户无效对象_oracle重新编译失效对象-程序员宅基地

文章浏览阅读1.2w次。oracle sys用户无效对象select owner,object_name, replace(object_type,' ','') object_type,to_char(created,'yyyy-mm-dd') as created,to_char(last_ddl_time,'yyyy-mm-dd') as last_ddl_time,statusfrom dba_o_oracle重新编译失效对象

随便推点

初中学历学前端难不难_计算机前端初中学难不难-程序员宅基地

文章浏览阅读609次。初中学历学前端难不难那肯定难啊。如果年纪不大,而且对IT这方面又比较感兴趣,我建议先去想办法提升一下自己的学历,成人本科也是可以的,该说不说,这东西花点钱还是可以弄到的,毕竟现在IT行业还是很看重学历的,学历是工作的第一块敲门砖,可能有人会说能力更重要,但是我告你你没有学历,别人根本不会去了解你是否有能力。当然花钱买的那个学历也不是处处管用,像大一点的公司需要的学历是需要学信网认证的,但你搞个学历,很多你之前初中文凭进不去的公司可能就会考虑你。如果真的考虑好了的话我建议去报个短期的培训班,毕竟是初中文凭_计算机前端初中学难不难

ThreadX学习(2)——线程_threadx教程-程序员宅基地

文章浏览阅读3.9k次,点赞26次,收藏48次。ThreadX学习(2)——线程学习参考:ThreadX中的线程线程创建堆栈分配互斥锁线程优先级优先级反转优先级继承抢占阈值线程状态数据结构TCB就绪列表API学习参考:《Real-Time Embedded Multithreading: Using ThreadX and ARM》安富莱_STM32-V7开发板ThreadX内核教程(V0.7)ThreadX中的线程在ThreadX中,一般没有进程的概念,统称为线程。关于调度器的实现细节,ThreadX可能是用汇编写的,没看懂。T_threadx教程

苹果手机sim卡无效怎么办_苹果手机存储空间不足怎么办-程序员宅基地

文章浏览阅读127次。  虽然现在的手机存储空越来越大,不过,娱乐的文件已经各种软件的体积也越来越大,而不像其他安卓手机可以通过内存卡对存储空间进行扩充,苹果手机存储空间不足怎么办,下面就为大家介绍一下解决方法。苹果手机存储空间不足怎么办  步骤1:当手机存储空间不足时,我们先得要查清楚,究竟是什么占用了大量的手机存储空间。打开苹果主屏上的“设置”应用。  步骤2:在设置列表中找到“通用”项,点击进入,接下来在通用中找..._萍果7手机sm1卡失效怎么办

中国大学生计算机设计大赛省级赛事管理系统_计算机设计大赛管理信息系统类-程序员宅基地

文章浏览阅读1.1k次,点赞18次,收藏21次。根据提示输入参赛队编号,若查找成功,输出该赛事类别对应的基本信息(参赛作品名称、参赛学校、赛事类别、参赛者和指导老师信息),同时,输出查找成功时的平均查找长度ASL;能够管理各参赛队的基本信息(包含参赛队编号,参赛作品名称,参赛学校,赛事类别,参赛者,指导老师),赛事类别共11项(参见大赛官网。1、请根据任务描述的问题,设计合理的菜单,菜单交互设计要合理,便于用户根据提示使用系统的所有功能。来定义参赛队编号,参赛作品名称,参赛学校,赛事类别,参赛者,指导老师等基本信息。包括增加、删除、修改参赛队伍的信息。_计算机设计大赛管理信息系统类

2024软件测试学习线路图~-程序员宅基地

文章浏览阅读879次,点赞17次,收藏17次。一个好的心态和一个坚持的心很重要,很多冲着高薪的人想学习前端,但是能学到最后的没有几个,遇到困难就放弃了,这种人到处都是,就是因为有的东西难,所以他的回报才很大,我们评判一个前端开发者是什么水平,就是他解决问题的能力有多强。分享一些简单的前端面试题以及学习路线给大家,狂戳这里即可获取一个好的心态和一个坚持的心很重要,很多冲着高薪的人想学习前端,但是能学到最后的没有几个,遇到困难就放弃了,这种人到处都是,就是因为有的东西难,所以他的回报才很大,我们评判一个前端开发者是什么水平,就是他解决问题的能力有多强。

计算机毕业设计选什么题目好?springboot 高校宣讲会管理系统-程序员宅基地

文章浏览阅读128次。前方高能!享受这场视觉盛宴!计算机毕业设计选什么题目好?springboot 高校宣讲会管理系统

推荐文章

热门文章

相关标签