学习Spring的第6天

学习Spring的第6天

学习Spring的第6天_第1张图片
这些就是第三步,我们要告诉这些通知方法,在什么时候需要执行。
学习Spring的第6天_第2张图片
配置里面的最后一个:
开启基于注解的AOP模式。
在配置文件ioc.xml里面去配置。

学习Spring的第6天_第3张图片
引入aop名称空间
学习Spring的第6天_第4张图片
学习Spring的第6天_第5张图片

<?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.3.xsd">
    <context:component-scan base-package="com.rtl" ></context:component-scan>
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

AOP的测试:

1、因为我们之前在配置的时候,就已经将切面类LogUtils和目标类MyCaculator加入到Spring容器里面去了。
所以我们在测试的时候,需要使用ioc.getBean的形式去获取对象。而不要new MyCaculator()
这里切记:

如果我们在使用getBean方法获取对象的时候使用的是传入类型(getBean(xxx.class)),那么这个类千万不要使本类,而要是接口类。

MyCalculator implements Caculator
学习Spring的第6天_第6张图片

Calculator calculator = ioc.getBean(Calculator.class);

测试类:
学习Spring的第6天_第7张图片
测试结果:学习Spring的第6天_第8张图片
测试结果是正确的,但是,测试结果显示的执行顺序是有问题的。
我们后面来解决这个顺序的问题。

AOP的细节

细节1:

查看一下getBean获取的对象的类型。

Calculator calculator = ioc.getBean(Calculator.class);

学习Spring的第6天_第9张图片
学习Spring的第6天_第10张图片
AOP的底层就是动态代理。
所以,容器中保存的其实就是它的代理对象
通过bean.getClass(),得到的是$Proxy22
$Proxy22这个就不是它的本类类型。

问:本类的类型在IOC容器里面有吗?
答:有。
学习Spring的第6天_第11张图片
加了@Service注解的,时候,容器已启动,需要为它创建对象,但是创建的不是本类对象,而是它的代理对象。$Proxy22,而不是MyMathCalculator.class
之前也说过:代理对象和被代理对象唯一能产生关联的就是,他们都有同一个接口:Calculator
学习Spring的第6天_第12张图片
学习Spring的第6天_第13张图片

补充以往不懂现在领悟的知识点:

背景:
学习Spring的第6天_第14张图片

学习Spring的第6天_第15张图片
在IOCTest类上面没有注解,然后IOCTest这个类有个成员变量,叫做@Autowired private BookService bookService;
加了自动注入的标签,BookService 类上加了注解,容器启动可以为它创建对象,但是最后打印,它的对象就是null。
解释:
test方法执行,
立马执行:

ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc4.xml");

这句话,容器启动,会创建好BookService对象在容器里面,
但是,如果你要使用@Autowired这个注解进行DI自动注入,那么,你这个IOTest必须要是Spring的会员才行。但是你并不是。所以。你不能把Spring容器里面已经创建好的BookService对象自动注入给你的成员变量。
但是下面这种情况又不是null了
学习Spring的第6天_第16张图片
原因就是,它使用的是getBean的方式去获取对象,而不是自动注入的形式。

接着将AOP
问:为什么我们在测试AOP的时候,用getBean的方式去获取对象的时候,要传入接口的类型呢?

Calculator calculator = ioc.getBean(Calculator.class);

学习Spring的第6天_第17张图片
答:因为,代理对象和被代理对象的唯一联系就是他们实现了同一个接口Calculator

问:Calculator 接口并没有加到容器里面去,为什么通过getBean的形式可以获取到呢?

Calculator calculator = ioc.getBean(Calculator.class);

学习Spring的第6天_第18张图片
答:我们应该逆向来看。
首先:我们传入的是Calculator.class,那Spring会先按照类型去容器里面找对象。这时候找的就是它的实现类了。
所以,一般不会给接口加上注解。
要不然,容器一启动还要给接口创建对象,就很尴尬
接口上加了对象,Spring也很智能,它一看这个是interface,他就不会创建。

结论:在IOC容器里面保存的是代理对象。
下面:
如果LogUtils类上的@Component注解去掉的话。
学习Spring的第6天_第19张图片

问:含义是什么?
答:浅显意思是:Spring容器里面已经没有这个LogUtils对象了。深层意思:之前LogUtils类是一个切面类。它里面的通知方法需要动态的切入到MyMathCalculator类的方法去。现在切面类没有了,也就是没有人去切MyMathCalculator你了,这时候,你自己还是你自己,而不是什么代理对象了。
测试:
学习Spring的第6天_第20张图片
学习Spring的第6天_第21张图片
然后在加上:
学习Spring的第6天_第22张图片
学习Spring的第6天_第23张图片
结论:如果有切面对象去切你了,那容器就要为你创建代理对象来实现动态的切入。

3、如果你不用接口类型拿,你用id拿,也是代理对象:
id写的是实现类MyMathCalculator类名的首字母小写。
学习Spring的第6天_第24张图片
补充:
之前说过,使用动态代理写代码的形式,必须有一个硬性条件,就是需要实现接口。
那现在使用AOP的形式,就没有这个条件要求了;例如:
我们的MyMathCalculator类现在不实现接口了
学习Spring的第6天_第25张图片
那我们写测试的时候。获取对象的时候,就直接写本类

MyMathCalculator bean = ioc.getBean(MyMathCalculator.class);

这样就是Spring AOP的cglib帮我们创建了。
还是照样实现动态的切入
cglib帮我们创建好了代理对象

学习Spring的第6天_第26张图片

细节2:切入点表达式的写法

固定写法:

 @Before("execution(访问权限符 返回值类型方法全类名(参数类型))")

切入点表达式的通配符:
1、 *
匹配一个或多个字符
匹配类名是MyMath开头的

@Before("execution(public int com.rtl.impl.MyMath*.*(int,int))")

匹配任意一个参数
第一个参数是int类型,第二个参数是任意类型,且只有两个参数。

@Before("execution(public int com.rtl.impl.MyMathCalculator.*(int,*))")

2、…
匹配任意多个参数,任意类型的参数

细节三:切面类里面的通知方法的执行顺序

学习Spring的第6天_第27张图片
以除法为例需要看正常运行和异常运行的情况。
学习Spring的第6天_第28张图片

除法正常运行:

学习Spring的第6天_第29张图片
学习Spring的第6天_第30张图片
所以结论就是:
方法如果正常执行的话。
方法切入的顺序是:
@Before 前置通知
@After 后置通知
@AfterReturing 正常返回

除法的异常执行:

学习Spring的第6天_第31张图片
学习Spring的第6天_第32张图片所以方法异常执行的顺序就是:
1、@Before
2、@After
3、@AfterThrowing

方法正常执行 VS 异常执行 顺序:

在这里插入图片描述

细节四:我们可以在通知方法运行的时候,拿到目标方法的详细信息

JointPoint获取目标方法的详细信息

现在的切面类里面的通知方法,没有打印出通知方法的详细信息。
例如:方法名,方法的参数值,方法的返回结果
学习Spring的第6天_第33张图片
现在只是简单的打印出一个字符串罢了。
做法:
只需要在通知方法里面传入一个参数:
JoinPoint 封装了当前目标方法的详细信息。
对比:
之前的通知方法:
学习Spring的第6天_第34张图片
现在的通知方法:
学习Spring的第6天_第35张图片

JoinPoint这个类是来自这里的:注意是大写的JointPoint

import org.aspectj.lang.JoinPoint;

JointPoint:这个类封装了当前目标方法(加减乘除)的详细信息

1、获取目标方法的运行参数:

 Object[] args = joinPoint.getArgs();

2、获取目标方法的方法签名:

 Signature signature = joinPoint.getSignature();

获取方法名:

Signature signature = joinPoint.getSignature();
String name = signature.getName();

测试:
学习Spring的第6天_第36张图片
学习Spring的第6天_第37张图片

现在已经可以拿到目标方法的方法签名和方法的参数列表。 还没有拿到方法的返回值和方法出现异常时候的异常信息。

1、目标方法的返回值的获取
第一步:
在通知方法上加上参数,Object result
学习Spring的第6天_第38张图片
第二步:
我们要告诉Spring,这通知方法的第二个参数Object result是用来接收目标方法的返回值的。
怎么告诉呢?
我们发现@AfterReturing注解里面有四个属性,其中第三个属性就是returing
学习Spring的第6天_第39张图片

@AfterReturning(value = "execution(public int com.rtl.impl.MyMathCalculator.*(int,int))",returning = "result")

学习Spring的第6天_第40张图片
returning = “result” 后面这个result就是对应参数名。
学习Spring的第6天_第41张图片
2、目标方法执行过程中如果出现异常了,怎么获取异常信息呢?
我们发现@AfterThrowing注解里面有四个属性,其中第三个属性就是throwing
学习Spring的第6天_第42张图片

 @AfterThrowing(value = "execution(public int com.rtl.impl.MyMathCalculator.*(int,int))",
                    throwing = "exception")

学习Spring的第6天_第43张图片
现在测试:方法的返回值的获取和异常信息的获取。
1、返回值测试:
学习Spring的第6天_第44张图片
2、异常信息的获取:
学习Spring的第6天_第45张图片
Spring对通知方法的要求不严格,唯一有要求的就是通知方法的参数不能乱写:
学习Spring的第6天_第46张图片
为什么对通知方法的参数要求严格呢?
因为:通知方法的调用不是由我们cxy去调用的,而是,Spring在合适的时机帮我们调用的,那么调用这些方法就需要给参数传值,框架是利用反射技术传值的。所以,每一次,方法的调用,必须要确定这个参数的值,所以参数表的每一个参数,Spring 都需要认识。
我们在写参数的时候,都会说明
比如:我们写了一个参数
Exception exception
那么我们在上面的注解就说明了参数的含义
throwing = “exception”

学习Spring的第6天_第47张图片
还有一个要注意的就是:
我们在写异常的时候,一定要往大里面写。
加入你写的是A异常,但是这个时候,通知方法发生异常的时候,这个原因不是A异常,那这个时候就打印不出来异常信息了。
但是如果你写的是Exception最大的异常,那么,不管目标方法发生什么异常,都能拿到它的异常信息。

环绕通知 @Around

它很强大,但是很少使用
它之所以强大,是因为,它可以改变目标方法的参数。比如:目标方法的参数值。
比如本来人家是参数是10/2,,结果被你改成20/2
其他的四个通知
@Before
@After
@AfterThrowing
@AfterReturn
都不能改变目标方法的参数。

在这里插入图片描述

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