7.ssm之springAOP

SpringAOP

1 AOP的初涉

1.1 AOP简单介绍

​ AOP:Aspect Oriented Programming:面向切面编程,基于oop基础之上的。指的是程序运行期间,将某段代码动态的切入到指定方法的指定位置进行运行的编程方式。

1.2 为什么要使用AOP

​ 想象下面的场景,开发中在多个模块间有某段重复的代码,我们通常是怎么处理的?显然,没有人会靠“复制粘贴”吧。在传统的面向过程编程中,我们也会将这段代码,抽象成一个方法,然后在需要的地方分别调用这个方法,也就是所谓的工具类,这样当这段代码需要修改时,我们只需要改变这个方法就可以了。然而需求总是变化的,有一天,新增了一个需求,需要再多处做修改,我们需要再抽象出一个方法,然后再在需要的地方分别调用这个方法,又或者我们不需要这个方法了,我们还是得删除掉每一处调用该方法的地方。实际上涉及到多个地方具有相同的修改的问题我们都可以通过 AOP 来解决。

1.3 AOP实现的方式

场景:计算器运行计算方法的时候进行日志记录。

方式一:给每一个方法代码中添加一个日志记录相关的代码。

缺点:

  • 需要修改源码,需要给每一个方法都添加一个日志记录的代码。
  • 将业务逻辑和核心业务耦合,造成代码耦合度提高。

方式二:将日治管理的代码进行封装,然后需要的时候进行调用,日志工具类。

缺点:

  • 需要考虑封装的方法的兼容性,依旧在方法中调用,依旧存在耦合。

方式三:使用动态代理的方式增加日志管理

动态代理

package com.atguigu.proxy;

import static org.hamcrest.CoreMatchers.nullValue;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.atguigu.inter.Calculator;
import com.atguigu.utils.LogUtils;

//生成代理对象的类:使用反射包下的代理类
public class CaculatorProxy {
     
	public static Calculator getProxy(final Calculator calculator) {
     
		//方法执行器:帮我们目标对象执行目标方法
		InvocationHandler handler=new InvocationHandler() {
     
			/**
			 * proxy:代理对象,给jdk使用,我们不要动。
			 * method:当前要执行的目标对象的方法。
			 * Object[] args:方法调用的时候传入的参数值
			 * 
			 */
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     
				Object result=null;
				try {
     
					LogUtils.logStart(method, args);
					//利用反射执行目标方法:result是目标方法执行之后的返回值
					result=method.invoke(calculator, args);
					LogUtils.logReturn(method, result);
				} catch (Exception e) {
     
					LogUtils.logException(method, e);
				}finally {
     
					LogUtils.logEnd(method);
				}
				return result;
			}
		};
		//获取被代理类实现的接口
		Class<?>[] interfaces=calculator.getClass().getInterfaces();
		//获取代理类对象
		Object proxy=Proxy.newProxyInstance(calculator.getClass().getClassLoader(), interfaces, handler);
		return (Calculator)proxy;
	}
}

LogUtils:

package com.atguigu.utils;

import java.lang.reflect.Method;

public class LogUtils {
     
    public static void logStart(Method method,Object...objects ) {
     
        System.out.println("["+method.getName()+"]开始执行。");
    }
    public static void logReturn(Method method,Object result ) {
     
        System.out.println("["+method.getName()+"]正常计算完成。");
    }
    public static void logException(Method method,Exception e) {
     
        System.err.println("["+method.getName()+"]计算过程出错,出现了异常::"+e.getCause());
    }
    public static void logEnd(Method method) {
     
        System.err.println("["+method.getName()+"]结束了");
    }
}

优点:

  • 我们可以在方法的执行前后执行一些我们的操作,可已经将日志管理做的很好,而且还可以进行异常处理。
  • 降低了耦合度。

缺点:

  • JDK默认的动态代理,如果目标对象没有实现任何接口,代理对象就无法被创建。

    • 代理对象实际的类型:class com.sun.proxy.$Proxy4。代理对象和被代理对象能产生的唯一关联就是实现同一个接口。Arrays.asList(proxy.getClass().getInterfaces())==>[interface com.atguigu.inter.Calculator]
  • 写起来太难。

3 总结

​ Spring实现AOP功能,实质是动态将某段代码切入到指定的方法,不需要将代码直接写死,底层就是利用动态代理实现的。使用Spring可以简单创建动态代理,实现简单且不强制要求实现接口。

类别 机制 原理 优点 缺点
静态AOP 静态织入 在编译期,切面直接以字节码的形式编译到目标自己码文件中 对系统无性能影响 灵活度低
动态AOP JDK动态代理 在运行期,目标的类加载之后,为接口生成代理类,将切面织入到代理类中 相对于静态AOP灵活一些 切入的关注点需要实现接口,对系统的性能有一定的影响
动态字节码生成 CGLIB 在运行期,目标的类加载之后,动态生成目标类的子类,将切面逻辑添加到子类中去 没有接口也可以织入 扩展类的实例方法用final修饰,则无法进行织入
自定义类加载器 在运行期,目标类加载前,将切面逻辑加到目标的字节码里 对绝大多数类进行织入 代码中如果使用其他类加载器,则这些类将不会被织入
字节码转换 在运行期,所有类加载器的加载字节码文件前进行拦截 对所有类进行织入

1.4 常见的概念

1 AOP的术语

  • 通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
  • 连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
  • 切点(PointCut): 可以插入增强处理的连接点。
  • 切面(Aspect): 切面是通知和切点的结合。
  • 引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。
  • 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。

2 图解

7.ssm之springAOP_第1张图片

2 AOP的初步体验

2.1 环境的搭建

1 导包

7.ssm之springAOP_第2张图片

面向切面编程加强的包:即便目标对象没有实现任何接口也能创建动态代理,子类代理的方式。

2 写配置

  1. 将目标类和切面类(通知方法)创建好并加入到ioc容器中。
    1. 引入context命名空间。
    2. 配置包扫描。
    3. 目标类和切面类加入相应的注解。

image-20201029170104036

  1. 还应该告诉Spring,哪一个是切面类。使用@Aspect类注解切面类。

7.ssm之springAOP_第3张图片

  1. 告诉Spring,切面类里面每一个方式,都是何时何地运行。
  • @Before:方法执行之前
  • @After:目标方法结束之后
  • @AfterReturning:目标方法正常返回之后
  • @AfterThrowing:目标方法抛出异常之后运行
  • @Around:环绕 :后置通知+@After

注意:

注解需要声明切入点表达式:execution(访问权限符 返回值类型 方法签名)
execution(public int com.atguigu.inter.impl.MyMathCalculator.add(int, int)):表示只在加法之前。
execution(public int com.atguigu.inter.impl.MyMathCalculator.*(int, int)):表示所有方法之前。

7.ssm之springAOP_第4张图片

  1. 开启基于注解的AOP模式

image-20201029171155543

如果成功会存在标志:小箭头

7.ssm之springAOP_第5张图片

通知方法类的代码:

package com.atguigu.utils;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogUtils {
     
    /**
     * 告诉Spring每个方法都什么时候执行。
     * try{
     * 		@Before
     * 		method.invoke(obj,args)
     * 		@AfterReturning
     * }catch(e){
     * 		@AfterThrowing
     * }finally{
     * 		@After
     * }
     * 通知注解:
     * @Before:方法执行之前 :前置通知+@Around
     * @After:目标方法结束之后
     * @AfterReturning:目标方法正常返回之后  :返回通知
     * @AfterThrowing:目标方法抛出异常之后运行  :异常通知
     * @Around:环绕 :后置通知+@After 
     * 注意:before需要声明切入点表达式:execution(访问权限符 返回值类型 方法签名)
     * execution(public int com.atguigu.inter.impl.MyMathCalculator.add(int, int)):表示只在加法之前。
     * execution(public int com.atguigu.inter.impl.MyMathCalculator.*(int, int)):表示所有方法之前。
     */
    @Before("execution(public int com.atguigu.inter.impl.MyMathCalculator.add(int, int))")
    public static void logStart() {
     
        System.out.println("[xxx]开始执行。");
    }
    @AfterReturning("execution(public int com.atguigu.inter.impl.MyMathCalculator.*(int, int))")
    public static void logReturn() {
     
        System.out.println("[xxx]正常计算完成。");
    }
    @AfterThrowing("execution(public int com.atguigu.inter.impl.MyMathCalculator.*(int, int))")
    public static void logException() {
     
        System.out.println("[xxx]计算过程出错,出现了异常::");
    }

    @After("execution(public int com.atguigu.inter.impl.MyMathCalculator.*(int, int))")
    public static void logEnd() {
     
        System.out.println("[xxx]最终结束了");
    }
}

3 AOP测试

package com.atguigu.test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.atguigu.inter.Calculator;

public class AOPTest {
     
	ApplicationContext ioc=new ClassPathXmlApplicationContext("applicationContext.xml");
	@Test
	public void test01() {
     
		//需要从ioc中那。如果想用类型来获取,一定要用他的接口类型。
		Calculator calculator=ioc.getBean(Calculator.class);
		int result=calculator.add(1, 3);
		System.out.println(result);
	}
}

结果展示:

  • 无异常情况下

7.ssm之springAOP_第6张图片

  • 存在异常

image-20201029171516624

输出的顺序似乎有所不对?

7.ssm之springAOP_第7张图片

3 AOP中存在的细节

3.1 IOC容器中保存的是代理类对象

@Test
public void test01() {
     
    //需要从ioc中那。如果想用类型来获取,一定要用他的接口类型。
    Calculator calculator=ioc.getBean(Calculator.class);
    System.out.println(calculator);
    System.out.println(calculator.getClass());
}

代码输出:

image-20201029171742528

分析:

  1. AOP底层是动态代理,容器中保存的组件是他的代理对象。$Proxy13一定不是他本类的类型。
  2. 接口一般不加载到容器中,可以加载,但是不会创建其对象,也创建不了对象。
  3. 如果存在切面,那么获取到的对象的类型是代理类对象,如果没有切面获取到的对象类型是该类的。

image-20201029171907121

  1. 如果按照id来获取该对象,存在切面的情况之下,获取到的依旧是代理类对象。

7.ssm之springAOP_第8张图片

image-20201029172143104

问题:为什么CGLIB可以没有接口的情况下实现动态代理?

​ 我们去除掉实现的接口的情况下,我们依旧可以创建动态代理,在这种情况下,获取到的是本类的类型,但是CGLIB也帮我们创建了其子类对象,因此CGLIB也称为子类代理。

@Test
public void test02() {
     
    //不存在接口的情况下
    MyMathCalculator calculator=ioc.getBean(MyMathCalculator.class);
    calculator.add(1, 2);
    System.out.println(calculator);
    System.out.println(calculator.getClass());
}

image-20201029172157010

3.2 切入点表达式的写法(通配符)

​ 固定格式:execution(访问权限符 返回值类型 方法全类名(参数列表))

通配符:

1)*

  • 匹配一个或者多个字符
//MyMath开头,r结尾的类的下的所有的方法(参数是int,int)
execution(public int com.atguigu.inter.impl.MyMath*r.*(int, int))"
  • 匹配任意类型的参数–适用于存在方法重载的情况下
//MyMath开头类的下的所有参数为(int,任意)的方法。
execution(public int com.atguigu.inter.impl.MyMath*.*(int, *))"
  • *放在路径位置,只能匹配一层路径
  • 权限位置不能写*,权限位置也可以省略,只能切public类型的方法

2)…

  • 匹配任意多个任意参数类型
//MyMath开头类的下的所有的方法。
execution(public int com.atguigu.inter.impl.MyMath*.*(..))"
  • 匹配多层路径
//匹配com.atguigu下任意包下的MyMath开头的类的任意方法
execution(public int com.atguigu..MyMath*.*(..))"

记住两种:

  • 最精确的:
execution()execution(public int com.atguigu.impl.MyMathCalculator.add(int,int))
  • 最模糊的:
execution(* * . *(..))  //任意包下的任意类,千万不能写

拓展:

可以使用逻辑运算符:

  • &&:表示我们要切入的位置需要满足多个表达式。
  • ||:表示我们要切入的位置满足两者之一。
  • !:表示不满足该表达式即可。

3.3 通知方法的执行顺序问题

  • 正常执行:@Before(前置通知)>@After(后置通知)>@AfterReturning
  • 异常执行:@Before(前置通知)>@After(后置通知)>@AfterThrowing

3.4 JoinPoint获取目标方法的信息

​ 我们如果无法在方法运行的时候无法获取方法的详细信息,那么就是没有用的,因此我们需要获取到目标方法的详细信息。

步骤:

  1. 为通知方法的参数列表上写一个参数:JoinPoint joinPoint。封装了方法参数的详细信息。包为import org.aspectj.lang.JoinPoint;
  2. 使用该对象的方法来获取具体的信息方法名和参数列表信息。
    1. Object[] args = joinPoint.getArgs();获取参数列表
    2. Signature signature = joinPoint.getSignature();获取方法签名。
      1. String name=signature.getName();根据方法的签名获取方法的名称。

3.5 获取异常信息和计算结果

步骤:

  1. 参数列表中加入值
  2. 告诉Spring:result或者exception为结果或者异常。

代码:

package com.atguigu.utils;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogUtils {
     
	
    /**
     * 告诉Spring每个方法都什么时候执行。
     * try{
     * 		@Before
     * 		method.invoke(obj,args)
     * 		@AfterReturning
     * }catch(e){
     * 		@AfterThrowing
     * }finally{
     * 		@After
     * }
     * 通知注解:
     * @Before:方法执行之前 :前置通知+@Around
     * @After:目标方法结束之后
     * @AfterReturning:目标方法正常返回之后  :返回通知
     * @AfterThrowing:目标方法抛出异常之后运行  :异常通知
     * @Around:环绕 :后置通知+@After
     * 注意:before需要声明切入点表达式:execution(访问权限符 返回值类型 方法签名)
     * execution(public int com.atguigu.inter.impl.MyMathCalculator.add(int, int)):表示只在加法之前。
     * execution(public int com.atguigu.inter.impl.MyMathCalculator.*(int, int)):表示所有方法之前。
     * 
     */
    @Before("execution(public int com.atguigu.inter.impl.MyMathCalculator.*(..))")
    public static void logStart(JoinPoint joinPoint) {
     
        Object[] args = joinPoint.getArgs();
        //获取到方法的签名
        Signature signature = joinPoint.getSignature();
        //获取方法名称
        String name=signature.getName();
        System.out.println("["+name+"]开始执行。参数为:"+Arrays.asList(args));
    }
    @AfterReturning(value="execution(public int com.atguigu.inter.impl.MyMathCalculator.*(..))",returning="result")
    public static void logReturn(JoinPoint joinPoint,Object result) {
     
        Signature signature = joinPoint.getSignature();
        //获取方法名称
        String name=signature.getName();
        System.out.println("["+name+"]正常执行完成。计算结果为:"+result);
    }
    @AfterThrowing(value="execution(public int com.atguigu.inter.impl.MyMathCalculator.*(..))",throwing="exception")
    public static void logException(JoinPoint joinPoint,Exception exception) {
     
        Signature signature = joinPoint.getSignature();
        //获取方法名称
        String name=signature.getName();
        System.out.println("["+name+"]计算过程出错,出现了异常::"+exception);
    }

    @After("execution(public int com.atguigu.inter.impl.MyMathCalculator.*(..))")
    public static void logEnd(JoinPoint joinPoint) {
     
        Signature signature = joinPoint.getSignature();
        //获取方法名称
        String name=signature.getName();
        System.out.println("["+name+"]最终结束了");
    }
}

测试代码:

@Test
public void test01() {
     
//需要从ioc中拿。如果想用类型来获取,一定要用他的接口类型。
Calculator calculator=ioc.getBean(Calculator.class);
System.out.println(calculator.add(1, 4));
System.out.println("================");
int result=calculator.div(10, 0);
System.out.println(result);
}

代码结果展示:

7.ssm之springAOP_第9张图片

需要注意指定的异常类型和返回类型尽量往大写,否则可能会出现错误。

3.6 Spring对通知方法的约束

  • Spring对通知方法的约束很低。
  • 唯一要求就是方法的参数列表一定不能乱写,因为他肯定是利用反射调用的,每次方法的调用需要确定这个方法的参数标的值。不知道的参数的时候一定要告诉Spring这些参数是用来干什么的。

3.7 抽取可重用的切入点表达式

步骤:

  1. 声明一个没有实现的返回void的空方法
  2. 给方法上添加注解:@Pointcut(“切入点表达式”。
@Pointcut("execution(public int com.atguigu.inter.impl.MyMathCalculator.*(..))")
	public void MyExecution() {
     
}
  1. 在需要引用的方法的注解里面写入该方法即可。此方式可以实现改一处,改所有。

最终代码:

@Pointcut("execution(public int com.atguigu.inter.impl.MyMathCalculator.*(..))")
	public void MyExecution() {
     
}
@Before("MyExecution()")
public static void logStart(JoinPoint joinPoint) {
     
	Object[] args = joinPoint.getArgs();
	//获取到方法的签名
	Signature signature = joinPoint.getSignature();
	//获取方法名称
	String name=signature.getName();
	System.out.println("["+name+"]开始执行。参数为:"+Arrays.asList(args));
}
@AfterReturning(value="MyExecution()",returning="result")
public static void logReturn(JoinPoint joinPoint,Object result) {
     
	Signature signature = joinPoint.getSignature();
	//获取方法名称
	String name=signature.getName();
	System.out.println("["+name+"]正常执行完成。计算结果为:"+result);
}
@AfterThrowing(value="MyExecution()",throwing="exception")
public static void logException(JoinPoint joinPoint,Exception exception) {
     
	Signature signature = joinPoint.getSignature();
	//获取方法名称
	String name=signature.getName();
	System.out.println("["+name+"]计算过程出错,出现了异常::"+exception);
}

@After("MyExecution()")
public static void logEnd(JoinPoint joinPoint) {
     
	Signature signature = joinPoint.getSignature();
	//获取方法名称
	String name=signature.getName();
	System.out.println("["+name+"]最终结束了");
}

3.8 环绕通知

​ @Around:是Spring中最强大的通知方法,实质上就是动态代理,就是反射帮我们调用目标方法。相当于其他四种注解的四合一版本。

实现步骤:

7.ssm之springAOP_第10张图片

代码结果演示:

7.ssm之springAOP_第11张图片

环绕通知抛出异常

7.ssm之springAOP_第12张图片

分析:

正常执行:环绕通知优先于普通通知执行。

  • 只有普通通知:【普通前置】>目标方法执行>【普通后置】==>【普通方法返回】

  • 加上环绕通知:【环绕前置执行】>【普通前置】>try{目标方法执行}>【环绕返回通知】>【环绕后置通知】>【普通后置】>【普通返回】

执行出错:环绕依旧优先于普通执行,环绕处理。

  • 只有普通通知:【普通前置】>目标方法执行>【普通后置】==>【普通异常】

  • 加上环绕通知:【环绕前置执行】>【普通前置】>try{目标方法执行}>【环绕异常通知】>【环绕后置通知】>【普通后置】>【普通异常】

总结:

7.ssm之springAOP_第13张图片

注意:

  1. 异常和返回是不共存的。
  2. 前置顺序无所谓,其他顺序严重相关。
  3. 环绕通知可以直接影响到方法的执行,可以影响到返回值和参数,其他普通通知只是再合适的时机进行调用。

3.9 多切面运行

再次创建一个普通通知类,查看两个普通切面顺序代码如下

package com.atguigu.utils;

import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class ValidateApsect {
     
	@Before("com.atguigu.utils.LogUtils.myExecution()")
	public static void logStart(JoinPoint joinPoint) {
     
		Object[] args = joinPoint.getArgs();
		//获取到方法的签名
		Signature signature = joinPoint.getSignature();
		//获取方法名称
		String name=signature.getName();
		System.out.println("ValidateApsect:["+name+"]开始执行。参数为:"+Arrays.asList(args));
	}
	@AfterReturning(value="com.atguigu.utils.LogUtils.myExecution()",returning="result")
	public static void logReturn(JoinPoint joinPoint,Object result) {
     
		Signature signature = joinPoint.getSignature();
		//获取方法名称
		String name=signature.getName();
		System.out.println("ValidateApsect:["+name+"]正常执行完成。计算结果为:"+result);
	}
	@AfterThrowing(value="com.atguigu.utils.LogUtils.myExecution()",throwing="exception")
	public static void logException(JoinPoint joinPoint,Exception exception) {
     
		Signature signature = joinPoint.getSignature();
		//获取方法名称
		String name=signature.getName();
		System.out.println("ValidateApsect:["+name+"]计算过程出错,出现了异常::"+exception);
	}
	

    @After("com.atguigu.utils.LogUtils.myExecution()s")
    public static void logEnd(JoinPoint joinPoint) {
     
        Signature signature = joinPoint.getSignature();
        //获取方法名称
        String name=signature.getName();
        System.out.println("ValidateApsect["+name+"]最终结束了");
    }
}

结果展示:

7.ssm之springAOP_第14张图片

分析:

7.ssm之springAOP_第15张图片

1.两个普通的切面顺序是由类名的字典序决定的。

2.可以使用@order(int)来改变其执行顺序。

环绕+两个普通切面

7.ssm之springAOP_第16张图片

环绕只是影响当前切面(哪个切面类中),无法影响其他切面。

7.ssm之springAOP_第17张图片

4 总结

4.1 AOP常见场景

  • AOP加日志保存到数据库。
  • AOP做权限验证
  • AOP做安全检查
  • AOP做事务控制

4.2 基于注解的AOP步骤

  1. 将目标类和切面类都加入到ioc容器中。@Component
  2. 告诉Spring哪个是切面类。@Aspect
  3. 在切面类中使用五个通知注解来配置切面中这些通知方法何时何地的运行
  4. 开启基于注解的AOP功能:

4.3 基于配置的AOP步骤

​ 实现步骤和基于注解的aop的步骤是相同的,因此我们只需要实现这个步骤就可以了。


	 
	 <bean id="myMathCalculator" class="com.atguigu.inter.impl.MyMathCalculator">bean>
	 <bean id="validateApsect" class="com.atguigu.utils.ValidateApsect">bean>
	 <bean id="logUtils" class="com.atguigu.utils.LogUtils">bean>
	 
	 <aop:config>
	 <aop:pointcut expression="execution(* com.atguigu.inter.impl.MyMathCalculator.*(..))" id="globalPoint"/>
	 	
	 	<aop:aspect ref="logUtils">
	 		<aop:pointcut expression="execution(* com.atguigu.inter.impl.MyMathCalculator.*(..))" id="mypoint"/>
	 		
	 		<aop:before method="logStart" pointcut-ref="mypoint"/>
	 		<aop:after-returning method="logReturn" pointcut-ref="mypoint" returning="result"/>
	 		<aop:after-throwing method="logException" pointcut-ref="mypoint" throwing="exception"/>
	 		<aop:after method="logEnd" pointcut-ref="mypoint"/>
	 		<aop:around method="myAround" pointcut-ref="mypoint"/>
	 	aop:aspect>
	 	<aop:aspect ref="validateApsect">
	 		
	 		<aop:before method="logStart" pointcut-ref="globalPoint"/>
	 		<aop:after-returning method="logReturn" pointcut-ref="globalPoint" returning="result"/>
	 		<aop:after-throwing method="logException" pointcut-ref="globalPoint" throwing="exception"/>
	 		<aop:after method="logEnd" pointcut-ref="globalPoint"/>
	 	aop:aspect>
	 aop:config>

注意:需要实现接口。

4.4 注解和配置的区别

  • 注解比较简单,快速;配置的方式功能完善。
  • 重要的配置用配置的方式,不重要的配置使用注解。

4.5 环绕优先级的问题

环绕通知和普通通知的优先级问题:

1) 目标方法的调用由环绕通知决定,即你可以决定是否调用目标方法,而前置和后置通知 是不能决定的,他们只是在方法的调用前后执行通知而已,即目标方法肯定是要执行的。

2) 环绕通知可以控制返回对象,即你可以返回一个与目标对象完全不同的返回值,虽然这很危险,但是你却可以办到。而后置方法是无法办到的,因为他是在目标方法返回值后调用。

你可能感兴趣的:(javaee,java,aop,spring)