目录
前言
- 尚硅谷这个老师的笔记已经做的非常好了,平时我做笔记就是类似的方法,所以这里只做扩展,具体的笔记可以从尚硅谷公众号获取,老师的源码感觉不是很规范,凑活看吧;
- 尚硅谷老师的笔记已经上传,可以直接零资源下载,见Spring5 王老师原版笔记。
参考文献
1、Spring5 入门案例
1.1 Spring5 下载
由于版本不一致,老师的下载过程和我们的也不太一样,这里本人参考了Spring下载教程,如果使用 maven 的话,可以直接跳过下载 Spring 这一步,但是我们需要知道目前发布的最稳定的 Spring 框架版本是多少。
- 进入 Spring 官网https://spring.io/,点击 Spring FrameWork;
- 到页面后点击 learn 一栏,其中看到的后缀为
GA
的就是稳定版,这里我们选 5.3.20 ,即最新的稳定版。
1.2 创建一个基于 maven 的 Spring 项目
这里老师讲解了最原始的创建 Spring 项目的方法,不过既然学了 maven 的话,就不用自己导包,太麻烦了;而且老师在创建过程中貌似没有使用 module,新讲一块儿内容就创建一个 project,属实是 eclipse 的玩儿法了。
- 新建一个空的 project ,用来放整个 Spring 学到的内容:
注意:
- 这里创建空项目之后,可能导致出现项目文件夹不出现在 project 栏里面的情况,可以参考IDEA 中 project窗口,不显示项目工程目录,解决方法,我采用的是删除 idea 文件夹,然后用 idea 重新打开 Spring 这个项目的方式,是可以的。
- 创建一个新的 module 为 maven 工程,命名为 Spring_demo01,这样我们可以通过配置 maven 自动导包,不用自己去下载了。
- 导入老师要求我们手动导入的那 5 个 jar包,进入网站https://mvnrepository.com/;
- 从下载 Spring 框架的网站,我们可以知道我们想要的是 5.3.20 版本;
- 到这个网站中找到想要的依赖,然后找到对应版本点进去,将 maven 下面的复制粘贴到我们的 pom.xml 文件中就可以了;
- 导入 Spring5 相关 jar 包,spring包
beans、core、context、expression、commons-logging
;
在我们的 pom.xml 文件中配置,这里为了后面方便测试,需要将测试需要的依赖也导入进来:
<!--导入依赖的jar包-->
<dependencies>
<!-- spring包 beans、core、context、expression、commons-logging-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.20</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.20</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.20</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.3.20</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!-- Mybatis核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
<!-- junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<!-- log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
- 在
src/main/java
下新建com.atguigu.spring5.bean
的子包,在其中新建 User 类以及普通方法:
public class User {
public void add() {
System.out.println("User.add......");
}
}
- 在
src/main/resources
下创建 Spring 配置文件bean.xml
;
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置User对象创建-->
<bean id="user" class="com.atguigu.spring5.bean.User"></bean>
</beans>
- 在
src/test/java
下新建com.atguigu.spring5.test
的子包,在其中新建 UserTest 类进行测试
public class UserTest {
@Test
public void testAdd() {
//1 加载spring配置文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//2 获取配置创建的对象
User user = context.getBean("user", User.class);
System.out.println(user);
user.add();
}
}
- 运行测试类,项目运行成功:
2、IOC - 控制反转
2.1 IOC 是什么?
- 在传统的 Java 应用中,一个类想要调用另一个类中的属性或方法,通常会先在其代码中通过 new Object() 的方式将后者的对象创建出来,然后才能实现属性或方法的调用。也就是说,调用者掌握着被调用者对象创建的控制权。(存在极大的耦合度)
- 在 Spring 应用中,Java 对象创建的控制权是掌握在 IoC 容器手里的,谁想用哪一个对象,就从 IOC 容器中去取;
- 对于该对象依赖的属性值,也会在 IOC 容器来负责,通过反射将该对象依赖的属性值注入进去。
2.2 IOC 底层原理
IoC 底层通过工厂模式、Java 的反射机制、XML 解析等技术,将代码的耦合度降低到最低限度,其主要步骤如下:
- 在配置文件(例如 bean.xml)中,对各个对象以及它们之间的依赖关系进行配置;
- 我们可以把 IoC 容器当做一个工厂,这个工厂的产品就是 Spring Bean;
- 容器启动时会加载并解析这些配置文件,得到对象的基本信息以及它们之间的依赖关系;
- IoC 利用 Java 的反射机制,根据类名生成相应的对象(即 Spring Bean),并根据依赖关系将这个对象注入到依赖它的对象中。
2.3 IOC 容器的两种实现方式
- IOC 思想基于 IOC 容器完成,IOC 容器底层就是对象工厂;
- 有两种实现方式(两个接口):BeanFactory 和 ApplicationContext 接口。
- 我们主要用的是 ApplicationContext 接口的 ①② 两种实现类;
- 接口 ③ 主要包含 BeanFactory 相关的一些扩展功能,以后可能会用到。
2.3.1 BeanFactory
-
BeanFactory 是 IoC 容器的基本实现,也是 Spring 提供的最简单的 IoC 容器,它提供了 IoC 容器最基本的功能,由 org.springframework.beans.factory.BeanFactory 接口定义。
-
BeanFactory 采用懒加载(lazy-load)机制,容器在加载配置文件时并不会立刻创建 Java 对象,只有程序中获取(使用)这个对对象时才会创建。
2.3.2 ApplicationContext
- 对于实现类①:使用的时候要写从盘符开始的路径,例如
D:\Projects\IdeaProjects\Spring5\Spring5_demo01\src\main\resources\bean.xml
- 对于实现类②:写文件在
src
下的路径就可以,例如bean.xml
,这里对于 maven 工程,我放置在src/main/resources
目录下,也可以这么使用(参考上面实现的第一个 Spring 项目)。
2.4 IOC 操作的 Bean 管理
2.4.1 什么是 Bean 管理
Bean 管理指的是两个操作:
- Spring 创建对象:创建 BeanFactory;
注意要创建类的无参构造器,如果没有创建无参构造器,会报错:
- Spirng 注入属性:注入普通属性、注入字面量(null 值、空字符串)、注入 外部 Bean、注入内部 Bean(即一个类的某一属性是自定义类型,比如一对多关联)、注入集合、
2.4.2 Bean 管理操作有两种方式
- 基于 xml 配置文件方式实现(可以看尚硅谷笔记)
- 基于注解方式实现
2.4.3 注入普通属性需要注意的点
- 使用 set 方法注入属性的时候,即在
bean
标签内部使用property
标签进行属性注入要创建对应的 set 方法,然后配置 xml 文件才有效,否则会报错
- 同理,使用有参构造器设置属性的时候,即在
bean
标签内部使用constructor-arg
标签进行属性注入,要求类中有对应参数的构造器; - p名称空间注入,添加p空间约束,在bean标签内部使用“p:属性”来定义(了解)
2.4.4 注入外部 bean 需要注意的点
- bean 标签创建对象,创建的是某个类的对象,不能用接口,即 class 定位到的全类名到实现类,而不是到接口。
2.4.5 注入内部 bean 需要注意的点
- 注意在打算注入内部 bean 的外层 property 标签上只有 name 标属性,不要使用 value/ref 属性。
- 级联赋值中第二种写法,记得还要在 property 标签内用ref
属性引入外部链接,否则是没办法设置dept.name
的,报错如下:
没有用 ref 标签级联报错信息如下,表示当前 emp 内部的 dept 属性为空:
Caused by: org.springframework.beans.NullValueInNestedPathException: Invalid property 'dept' of bean class [com.atguigu.spring5.bean.Emp]: Value of nested property 'dept' is null
2.4.6 注入集合属性需要注意的点
- 在注入数组属性的时候,property 标签内部既可以用 array 标签也可以用 list 标签:
- 在将 list 集合注入提取为一个 util 类的时候,这个 id 是自己随便取的,就像 util 工具类中你命名的某个方法一样,只不过引用的时候 ref 标签要写这个 id 。
2.4.7 FactoryBean
- 工厂 bean 定义的时候,只要你这个 bean 是生产哪个类的,你使用的时候传进来的必须是这个类(不管有没有定义泛型,重写方法返回的是不是 Object),否则就会报错:
正确的使用方式如下:
2.4.8 bean 作用域
- bean 作用域:在 Spring 里面,设置创建 bean 实例是单实例还是多实例:
- singleton:单例,默认也是这个,加载 spring 配置文件时候就会创建单实例对象
- prototype:多例,在调用 getBean 方法时候创建多实例对象
2.4.9 bean 生命周期(从对象创建到对象销毁的过程)
基本的生命周期(5 步):
- 通过构造器创建 bean 实例(无参数构造)
- 为 bean 的属性设置值和对其他 bean 引用(调用set方法)
- 调用 bean 的初始化的方法(需要进行配置初始化的方法)
- bean 可以使用了(对象获取到了)
- 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
演示结果:
加入后置处理器的生命周期(7 步):
- 通过构造器创建 bean 实例(无参数构造)
- 为 bean 的属性设置值和对其他 bean 引用(调用set方法)
- 把 bean 实例传递给 bean 后置处理器的方法 postProcessBeforeInitialization
- 调用 bean 的初始化的方法(需要进行配置初始化的方法)
- 把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
- bean 可以使用了(对象获取到了)
- 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
演示结果:
- 需要注意的是,在配置文件中配置了后置处理器,对当前配置文件中的所有 bean 都有效。
2.4.10 xml 自动装配属性
- 使用 byName 来装配的话,属性中的 set 方法后面的名称(第一个字母小写之后)要和 id 中配置的一致:
- 使用 byType 来装配的话,不能定义多个,会报错 NoUniqueBeanDefinitionException,没有定义唯一的一个 bean:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.atguigu.spring5.bean.Dept' available: expected single matching bean but found 2: dept,dept1
2.4.11 配置外部属性文件
演示配置数据库信息
- 在 pom.xml 文件中导入 druid 依赖:
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.10</version>
</dependency>
- 创建 jdbc.properties 配置文件:
prop.driverClass=com.mysql.cj.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/userDb
prop.userName=root
prop.password=root
- 把外部 properties 属性文件引入到 spring 配置文件中:
引入context名称空间:
在 spring 配置文件使用标签引入外部属性文件:
2.5 基于注解的开发(推荐)
2.5.1 什么是注解?
(1)注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值..)
(2)使用注解,注解作用在类上面,方法上面,属性上面
(3)使用注解目的:简化 xml 配置
2.5.2 Spring 针对 Bean 管理中创建对象提供注解
(1)@Component
,表示普通注解创建对象
(2)@Service
,一般用在业务逻辑层
(3)@Controller
,一般用在 web 层上
(4)@Repository
,一般用在 DAO 层
- 上面四个注解功能是一样的,都可以用来创建 bean 实例,但是约定大于配置,配置大于编码,所以为了好区分,最好是遵守规范。
2.5.3 基于注解方式实现对象创建
- 引入依赖 spring-aop:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.20</version>
</dependency>
- 开启组件扫描(这里仍然用 xml 配置文件的方式来实现),需要在配置文件中进行如下 3 步配置:
注意:
- 如果扫描多个包,多个包使用逗号隔开
<context:component-scan base-package="com.atguigu.spring5.dao,com.atguigu.spring5.service"></context:component-scan>
- 扫描包上层目录(推荐使用)
<!--开启组件扫描-->
<context:component-scan base-package="com.atguigu.spring5"></context:component-scan>
- 创建类,在类上面添加创建对象注解:
//在注解里面value属性值可以省略不写,
//默认值是类名称,首字母小写
//UserService -- userService
//@Component(value = "userService") //<bean id="userService" class=".."/>
@Service
public class UserService {
public void add() {
System.out.println("service add......." + name);
}
}
注意:
- 在注解里面 value 属性值可以省略不写,默认值是类名称,首字母小写,即
UserService -- userService
;
测试注解是否配置成功:
//测试注解开发,需要在配置文件中开启组件扫描
@Test
public void testService1() {
ApplicationContext context
= new ClassPathXmlApplicationContext("bean1.xml");
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService);
userService.add();
}
输出结果,创建成功,执行了其中的 add() 方法:
2.5.4 Spring 针对 Bean 管理中属性注入提供注解
(1)@Autowired
:根据属性类型进行自动装配
(2)@Qualifier
:根据属性名称进行注入,必须与 @Autowired
一起使用
(3)@Resource
:可以根据类型注入,可以根据名称注入(JDK 11 以后该注解被取消了,如果要使用的话,添加下面的依赖)
<!-- https://mvnrepository.com/artifact/javax.annotation/javax.annotation-api -->
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
(4)@Value
:注入普通类型属性
2.5.5 基于注解方式实现属性注入
- 沿用上面的对象创建过程,创建 DAO 层和 service 层,并且在 service 层中新建 DAO 属性,使用注解
@Qualifier
和@Autowired
进行实现:
@Repository(value = "userDaoImpl1")
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("dao add.....");
}
}
@Service
public class UserService {
//定义dao类型属性
//不需要添加set方法
//添加注入属性注解
@Autowired //根据类型进行注入
@Qualifier(value = "userDaoImpl1") //根据名称进行注入,和@Autowired 一起使用
private UserDao userDao;
public void add() {
System.out.println("service add.......");
userDao.add();
}
}
注意:
- 使用
@Autowired
注解进行属性注入不需要添加 set 方法; - 单独使用
@Autowired
注解,只能有一个实现类,多个实现类会报错; @Qualifier
必须与@Autowired
一起使用,唯一的确定某类型某实现类的属性;
- 使用注解
@Resource
进行实现,记得添加依赖:
注意:
- 单单使用一个
@Resource
代表根据类型进行注入,只能有一个实现类; - 使用
@Resource(name="实现类名称")
根据名称进行注入,可以有多个实现类,根据名称确定唯一的一个。
- 使用
@Value
在 service 层注入普通类型属性,这里演示的是属性值固定了,以后应该不这样用:
结果演示:
2.6 完全注解开发
- 创建配置类,替代 xml 配置文件来开启组件扫描:
@Configuration //作为配置类,替代xml配置文件
@ComponentScan(basePackages = {"com.atguigu.spring5"})
public class SpringConfig {
}
- 编写测试类,调用注解设置的配置类,和调用配置文件使用的函数不同,需要使用
AnnotationConfigApplicationContext(传入配置类)
:
//测试完全注解开发,需要创建配置类,替代xml配置文件
@Test
public void testService2() {
//加载配置类
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService);
userService.add();
}
结果:
3、AOP - 面向切面编程
3.1 AOP 是什么?
- 面向切面编程(方面),利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
- 通俗描述:不通过修改源代码方式,在主干功能里面添加新功能。
也可以说是将通用代码抽取出来,作为一个通用功能,但是为了降低耦合度,需要使用动态代理来实现,而不是在类中使用定义好的工具类方法来实现。
3.2 AOP 底层原理和两种实现方式
-
、AOP 底层使用动态代理;
-
动态代理:代理类要指定被代理类什么时候创建,调用什么方法,因为运行的时候才能知道加载了哪一个类,所以还要通过反射获取当前加载的类,然后看你实现了什么接口,我代理类实现和你一样的接口,就可以代理你实现你的功能了,并且在前置、后置等地方添加通用的功能;(此处参考康师傅 Java 基础部分,尚硅谷Java入门视频教程(在线答疑+Java面试真题) P662 - P665,康师傅 yyds)
-
当我想实现增强的功能定义在一个接口中的时候,JDK 动态代理;
-
当我想增强的功能没有在接口中的时候,使用 CGLIB 动态代理;
3.3 AOP 常用术语
- 连接点,多个可能被增强的方法
- 切入点,实际被增强的若干个方法
- 通知(增强)
前置通知:在切入点之前执行的方法
后置通知:在切入点之后执行的方法
环绕通知:在切入点前后都会执行的方法
异常通知:在切入点出现异常的时候执行,相当于 catch 捕获异常其中会执行的方法
最终通知:相当于 try/actch/finally 的 finally ,一定会被执行的代码 - 切面:一个动作,我要在某切入点使用增强方法的过程
3.4 JDK动态代理(当想增强的功能有接口的时候)
- 定义接口
public interface UserDao {
public int add(int a,int b);
public String update(String id);
}
- 定义目标类实现接口(被代理类)
public class UserDaoImpl implements UserDao {
@Override
public int add(int a, int b) {
System.out.println("add方法执行了.....");
return a+b;
}
@Override
public String update(String id) {
System.out.println("update方法执行了.....");
return id;
}
}
- 使用 Proxy 类里面的方法创建代理对象(代理类)
方法有三个参数:(参考康师傅 Java 基础)
参数1:当前被代理类的类加载器;
参数2:增强方法所在的类,这个类实现的接口,支持多个接口;
参数3:接口 InvocationHandler 的一个实现类:该实现类通过 invoke 调用被代理类中想要被增强的方法,并在其中写增强方法。
- 定义代理类实现 InvocationHandler 接口
class MyInvocationHandler implements InvocationHandler {
//1 创建的是谁的代理对象,就把谁传递过来
//有参数构造传递
private Object obj;
public MyInvocationHandler(Object obj) {
this.obj = obj;
}
//增强的逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法之前
System.out.println("方法之前执行...." + method.getName() + " :传递的参数..." + Arrays.toString(args));
//被增强的方法执行
Object res = method.invoke(obj, args);
//方法之后
System.out.println("方法之后执行...." + obj);
return res;
}
}
注意:
- 这里面需要将被代理类通过有参构造传进来;
- 当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke(),这时底层实现的。
- 将被代理类要执行的方法a的功能就声明在 invoke() 中
- invoke 方法里面有你的代理类,你当前想增强的方法,这个方法传进来的参数。
- 测试,这里这个老师和康师傅讲的不一样,下面康师傅的代码:
其实两个代码同理:
① 表示被代理类对象
② 表示根据该代理类生成了一个代理类对象
③ 代理类调用 被代理类中的某方法 a,那么执行该方法,和 MyInvocationHandler 中定义的增强方法或者通用方法。
执行结果:
3.5 CGLIB动态代理(当想增强的功能没有接口的时候)
3.5.1 准备工作
3.5.2 使用注解实现基于 AspectJ 的 AOP 操作
- 引入 AspectJ 依赖
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.20</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.20</version>
</dependency>
<!-- https://mvnrepository.com/artifact/net.sourceforge.cglib/com.springsource.net.sf.cglib -->
<dependency>
<groupId>net.sourceforge.cglib</groupId>
<artifactId>com.springsource.net.sf.cglib</artifactId>
<version>2.2.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aopalliance/com.springsource.org.aopalliance -->
<dependency>
<groupId>org.aopalliance</groupId>
<artifactId>com.springsource.org.aopalliance</artifactId>
<version>1.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/com.springsource.org.aspectj.weaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>com.springsource.org.aspectj.weaver</artifactId>
<version>1.7.2.RELEASE</version>
</dependency>
- 创建被增强类,其中有切入点 add() 方法
//被增强的类
public class User {
public void add() {
System.out.println("add.......");
}
}
- 创建增强类(编写增强逻辑)
//增强的类
public class UserProxy {
//前置通知
public void before() {
System.out.println("before.........");
}
}
- 配置文件(注解)实现:
(1)开启组件扫描,xml文件中,别忘了增加 xml 文件开头的 aop 和 context 约束:
<context:component-scan base-package="com.atguigu.spring5.aopanno"></context:component-scan>
(2)创建增强类和被增强类的对象:@Component
(3)增强类上加 @Aspect
注解:生成代理对象
(4)开启生成代理对象,xml 文件中:
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
- 配置不同类型的通知:
前置通知:@Before(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
后置通知:@AfterReturning
,返回值之后执行
环绕通知:@Around
,//被增强的方法执行,proceedingJoinPoint.proceed();
异常通知:@AfterThrowing
最终通知:@After
,在方法之后执行,不管有没有异常都执行
//增强的类
@Component
@Aspect //生成代理对象
public class UserProxy {
//相同切入点抽取
@Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void pointdemo() {
}
//前置通知
//@Before注解表示作为前置通知
@Before(value = "pointdemo()")
public void before() {
System.out.println("before.........");
}
//后置通知(返回通知)
@AfterReturning(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void afterReturning() {
System.out.println("afterReturning.........");
}
//最终通知
@After(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void after() {
System.out.println("after.........");
}
//异常通知
@AfterThrowing(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void afterThrowing() {
System.out.println("afterThrowing.........");
}
//环绕通知
@Around(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕之前.........");
//被增强的方法执行
proceedingJoinPoint.proceed();
System.out.println("环绕之后.........");
}
}
-
相同切入点抽取:
@Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
,在配置注解之后使用value = "pointdemo()"
来调用切入点方法即可,上述 前置增强已经使用。 -
测试
@Test
public void testAopAnno() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean1.xml");
User user = context.getBean("user", User.class);
user.add();
}
没有异常的时候:
有异常的时候:
3.5.3 设置增强类优先级
默认的 Order 值,非常大,即优先级非常低,所以只要设置了数字就会先执行:
3.5.4 完全注解开发
测试:
//测试完全注解开发
@Test
public void testAopFullyAnno() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigAop.class);
User user = context.getBean("user", User.class);
user.add();
}