1. Spring 概述
1.1 Spring 简介
??Spring Framework 是一个使用Java开发的、轻量级的、开源框架,它的主要作用是为了解耦合,Spring 的核心技术是 IOC(控制反转) 和 AOP(面向切面编程),
- 官方网站: https://spring.io
??Spring 框架提高了很多功能,包括IOC容器、AOP、资料访问、事务、测验功能、定时任务、快取等等,
1.2 优点
2. IOC 控制反转
2.1 IOC 是什么
??IOC (Inversion of Control, 控制反转) 是一种理论,指导开发人员如何使用物件、管理物件,将物件的生命周期交给容器来管理,通过容器管理物件,开发人员只需要拿到物件,执行物件的方法即可,
- 控制:管理物件的创建、属性赋值、生命周期的管理,
- 正转:让开发人员掌控物件的创建、属性赋值,即整个生命周期的管理,
- 反转:把开发人员管理物件的权限转移给容器来实作,让容器完成管理,
2.2 IOC 的技术实作
??DI (Dependency Injection, 依赖注入) 是 IOC 的一种技术实作,开发人员通过物件的名称获取已初始化的物件,而物件的创建、属性赋值、物件间的呼叫等都由容器内部实作,
2.3 IOC-创建物件 牛刀小试
Source Code2.3.1 测验步骤
- 创建 maven-quickstart 项目,并调整项目结构(字符编码、JDK版本等)
- 添加依赖
- spring-context
- junit
- 定义界面和实作类
- 界面: SomeService
- 方法:
doSome(): void
- 方法:
- 实作类: SomeServiceImpl
- 界面: SomeService
- 创建 Spring 组态档(.xml),宣告需要创建的物件
- 通过
<bean>
标签宣告物件,一个标签对应一个物件,
- 通过
- 使用容器中的物件
- 创建
ApplicationContext
物件 - 通过
getBean()
获取容器中的物件
- 创建
2.3.2 依赖档案
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bpf</groupId>
<artifactId>M01-ioc-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
2.3.3 界面与实作类
- HIDE
- SomeService.java
- SomeServiceImpl.java
package com.bpf.service;
public interface SomeService {
void doSome();
}
package com.bpf.service.impl;
import com.bpf.service.SomeService;
public class SomeServiceImpl implements SomeService {
public SomeServiceImpl() {
System.out.println("[SomeServiceImpl] 无参构造方法");
}
@Override
public void doSome() {
System.out.println("[SomeServiceImpl] someService()...");
}
}
2.3.4 组态档
<?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">
<!-- bean标签
id 自定义物件的名称,保持唯一,
class 自定义物件的全限定类名,不能是界面,
>>> Spring 根据 id 和 class 创建物件,并将物件放入一个 map 物件中,
-->
<bean id="someService" />
<bean id="someService1" />
<bean id="mydate" />
</beans>
2.3.5 测验创建物件
测验创建物件: CreateBeanTest.java
package com.bpf.service;
import com.bpf.service.impl.SomeServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Arrays;
import java.util.Date;
public class CreateBeanTest {
/**
* 传统方式: new 获取物件
*/
@Test
public void testCreateBeanClassical() {
SomeService someService = new SomeServiceImpl();
someService.doSome();
}
/**
* 使用 Spring 容器方式获取物件
*/
@Test
public void testCreateBean() {
// 创建容器物件
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
// 通过 getBean() 获取 bean 物件
SomeService someService = (SomeService) ctx.getBean("someService");
// 呼叫物件方法
someService.doSome();
}
/**
* Spring 创建物件,呼叫的是类的哪个构造器呢?
* 默认呼叫的是类的无参构造器!
*/
@Test
public void testCreateStyle() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
SomeService someService = (SomeService) ctx.getBean("someService");
someService.doSome();
// 在无参构造器上添加输出陈述句,如果把无参构造器改成有参构造器,执行测验方法时会报错:无法找到默认的构造方法,
/** 执行结果
* [SomeServiceImpl] 无参构造方法
* [SomeServiceImpl] someService()...
*/
}
/**
* Spring 创建物件,是什么时候创建的呢?
* Spring在创建容器物件 ApplicationContext时,会读取组态档,并创建档案中宣告的所有java物件,
*
* 优点:获取物件速度快,
* 缺点:占用存储器,
*/
@Test
public void testCreateTime() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
/** 执行结果
* [SomeServiceImpl] 无参构造方法
* [SomeServiceImpl] 无参构造方法
*/
}
/**
* 获取Spring容器中的物件信息
*/
@Test
public void testGetCtxInfo() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
// 容器中物件的数量
int count = ctx.getBeanDefinitionCount();
// 容器中物件的名称
String[] names = ctx.getBeanDefinitionNames();
System.out.println("容器中物件的数量:" + count);
System.out.println("容器中物件的名称:" + Arrays.toString(names));
/** 执行结果
* [SomeServiceImpl] 无参构造方法
* [SomeServiceImpl] 无参构造方法
* 容器中物件的数量:2
* 容器中物件的名称:[someService, someService1]
*/
}
/**
* 创建非自定义物件
*/
@Test
public void testOtherBean() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
Date date = (Date) ctx.getBean("mydate");
System.out.println("date = " + date);
/** 执行结果
* [SomeServiceImpl] 无参构造方法
* [SomeServiceImpl] 无参构造方法
* date = Wed Dec 22 19:35:37 CST 2021
*/
}
}
2.4 Spring 的组态档
??Spring 组态档通常命名为ApplicationContext.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">
<!--
1) 根标签是 beans
2) xxx.xsd 是当前XML档案的约束档案
3) 在 beans 标签内宣告 bean 物件,
一个 bean 就是一个java物件,
-->
</beans>
??Spring 支持多组态档方式,Spring 管理多组态档常用的是包含关系,即在主组态档中使用import
标签包含其他组态档,在其他组态档中定义宣告各自的信息,
<!-- 主组态档 -->
<!-- 路径中可以使用通配符 * 同时引入多个档案 -->
<import resource="classpath:其他组态档路径" />
2.5 Spring IOC ? 创建物件
2.5.1 Spring 容器创建物件的特点
- 容器物件是
ApplicationContext
,它是一个界面,常用的实作类是ClassPathXmlApplicationContext
,并且通过getBean()
方法获取已初始化的物件, - Spring 创建物件默认呼叫类的无参构造器,
- Spring 在创建容器物件后,会读取组态档,并创建档案中宣告的所有java物件,然后都放在map物件(
ConcurrentMap
)中,
2.5.2 XML方式
??Spring 通过在组态档中使用bean
标签宣告物件,使用id
属性指定创建的物件名称,使用class
属性指定创建的物件型别,
<!-- 组态档中宣告一个 bean 标签代表一个 java物件 -->
<bean id="物件名称" />
2.5.3 注解方式
??使用注解代替组态档中的bean
标签,在Java类上使用注解,通过value
属性指定创建的物件名称(相对于标签的id
属性),同时还需要在组态档中开启注解扫描并指定扫描的包路径,
?Spring 提供了四个注解:
注解 | 说明 |
---|---|
@Component |
表示普通的java物件 |
@Repository |
常用于创建DAO层的物件,持久层物件,表示可以访问数据库 |
@Service |
常用于创建Service层的物件,业务层物件,表示拥有事务功能 |
@Controller |
常用于创建Controller层的物件,控制器物件,表示可以接收和处理请求, |
?组态档开启注解扫描:
<!-- base-package 指定要扫描的包路径,Spring 会自动扫描包及其子包内表有上述注解之一的类,并创建和管理, -->
<context:componet-scan base-package="包路径" />
<!-- 如何扫描多个包? -->
<!-- 1. 使用多个标签 -->
<context:componet-scan base-package="xx.yy.pack01" />
<context:componet-scan base-package="xx.yy.pack02" />
<!-- 2. 使用分隔符:分号(;)或逗号(,) -->
<context:componet-scan base-package="xx.yy.pack01;xx.yy.pack02" />
<!-- 3. 使用共同的父包 -->
<context:componet-scan base-package="xx.yy" />
2.6 Spring IOC ? 属性注入
Source Code2.6.1 XML方式
(1)set注入(设值注入)
特点:
- 注入的属性必须存在对应的 setter 方法
- 如果属性在物件中不存在,但存在 setter 方法,依然不会报错,
- Spring 容器只负责呼叫 setter 方法,与方法的具体实作无关,
<!-- 简单型别注入: 基本资料型别、String型别 -->
<bean id="xxx" >
<property name="属性名" value="https://www.cnblogs.com/bpf-1024/p/xxx" />
...
</bean>
<!-- 参考Java物件 -->
<bean id="xxx" >
<property name="属性名" ref="其他bean标签的id值" />
...
</bean>
<!-- 或 -->
<bean id="xxx" >
<property name="属性名">
<bean ></bean>
</property>
...
</bean>
<!-- 注入null值 -->
<bean id="xxx" >
<property name="属性名">
<null/>
</property>
...
</bean>
<!-- 集合型别 -->
<bean id="xxx" >
<property name="属性名">
<!-- 阵列 -->
<array>
<value>xxx</value>
</array>
</property>
<property name="属性名">
<!-- List -->
<list>
<value>xxx</value>
<ref bean="其他bean标签的id值" />
</list>
</property>
<property name="属性名">
<!-- Set -->
<set>
<value>xxx</value>
</set>
</property>
<property name="属性名">
<!-- Map -->
<map>
<entry key="xxx" value="https://www.cnblogs.com/bpf-1024/p/yyy" />
</map>
</property>
<property name="属性名">
<!-- 阵列 -->
<array>
<value>xxx</value>
</array>
</property>
</bean>
(2)构造注入
特点:
- 不需要属性的 setter 方法
- 需要有相对应的含参构造器
<!--
index 对应构造器的形参索引,从0开始,可以省略
name 对应构造器的形参名
value 对应构造器的形参值
ref 对应其他的Java Bean
-->
<bean id="xxx" >
<constructor-arg name="构造器形参名" value="https://www.cnblogs.com/bpf-1024/p/xxx" />
<constructor-arg index="构造器形参索引" value="https://www.cnblogs.com/bpf-1024/p/xxx" />
...
</bean>
(3)参考型别自动注入
- byName: 根据名称注入,当组态档中bean标签的id值与物件的属性名匹配且属于同个型别时,可以进行注入,
- byType: 根据型别注入,当组态档中bean标签的class值与物件的属性型别同源时,可以进行注入,
- bean标签的class值与物件的属性型别相同时,
- bean标签的class值与物件的属性型别存在父子关系时,
- bean标签的class值与物件的属性型别存在界面-实作类关系时,
特点
- byName 方式通过 bean 标签的id属性,需要保证id唯一,
- byType 方式提供 bean 标签的class属性,需要保证只能存在一个同源的bean,否则会报错,
- 参考型别自动注入本质上使用的是setter方法进行属性赋值的,
<!-- 参考型别自动注入 -->
<bean id="xxx" autowired="byName | byType">
...
</bean>
(4)小作业
主要功能:模拟用户注册操作,
- 物体类 User,保存用户资料,
- 定义一个 UserDao 界面,提供方法 insertUser(User),同时定义界面的实作类 MySqlUserDao,方法实作输出 "通过MySQL插入用户:用户资料",
- 定义一个 UserService 界面,提供方法 addUser(User),同时定义界面的实作类 UserServiceImpl,并实作方法,
要求:使用 Spring 创建和管理界面的实作类物件,并通过 Spring 获取物件完成用户注册操作,
Source Code2.6.2 注解方式
(1)@Value
@Value
注解只能为属性赋普通型别的值,@Value
注解的位置:- 属性宣告上:无需setter方法
- setter方法上:需要setter方法,并且会呼叫setter方法
- 赋的值可以通过外部组态档(.properties)指定,
<!-- 组态档中引入外部组态档 -->
<context:property-placeholder location="classpath:properties档案的路径" />
- HIDE
- anno-value-applicationContext.xml
- bean-value.properties
- Student.java
- TestAnnoValue.java
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.bpf.anno.value" />
<context:property-placeholder location="classpath:bean-value.properties" />
</beans>
stu.name=凯特斯
stu.age=13
package com.bpf.anno.value;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Student {
/**
* @Value 注解:为属性赋值
* 使用位置:
* 1. 属性宣告上:无需setter方法
* 2. setter方法上:需要setter方法且会呼叫setter方法
*/
@Value("${stu.name}")
private String name;
private Integer age;
public void setName(String name) {
System.out.println("name = " + name);
this.name = name;
}
@Value("${stu.age}")
public void setAge(Integer age) {
System.out.println("age = " + age);
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package com.bpf.xml;
import com.bpf.anno.value.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestAnnoValue {
@Test
public void test() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("anno-value-applicationContext.xml");
Student student = (Student) ctx.getBean("student");
System.out.println("student = " + student);
/** 执行结果
* age = 13
* student = Student{name='凯特斯', age=13}
*/
}
}
(2)@Autowired
@Autowired
注解可以为属性赋参考型别的值,默认方式是byType
,@Autowired
注解的位置:- 属性宣告上:无需setter方法
- setter方法上:需要setter方法,并且会呼叫setter方法
/**
* Autowired 注解原始码
* 包含了 required 属性,默认值为true,表示当赋值的属性必须有值且赋值成功,当赋值的物件为null时,会抛出例外,
*/
public @interface Autowired {
boolean required() default true;
}
- HIDE
- anno-autowired-applicationContext.xml
- UserService.java
- StudentServiceImpl.java
- Student.java
- TestAnnoAutowired.java
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.bpf.anno.autowired" />
</beans>
package com.bpf.anno.service;
public interface UserService {
void sayHello();
}
package com.bpf.anno.autowired;
import com.bpf.anno.service.UserService;
import org.springframework.stereotype.Service;
@Service
public class StudentServiceImpl implements UserService {
@Override
public void sayHello() {
System.out.println("<com.bpf.anno.autowired> [StudentServiceImpl] sayHello()...");
}
}
package com.bpf.anno.autowired;
import com.bpf.anno.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Student {
/**
* @Autowired 注解:为属性赋值
* 使用位置:
* 1. 属性宣告上:无需setter方法
* 2. setter方法上:需要setter方法且会呼叫setter方法
* 属性:
* boolean required: 表示此属性是否必须,默认值为true,表示当对应的java物件为null时会抛出例外,
* org.springframework.beans.factory.NoSuchBeanDefinitionException
*/
// @Autowired(required = false)
@Autowired
private UserService userService;
public void sayHello() {
userService.sayHello();
}
}
package com.bpf.xml;
import com.bpf.anno.autowired.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestAnnoAutowired {
@Test
public void test() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("anno-autowired-applicationContext.xml");
Student student = (Student) ctx.getBean("student");
student.sayHello();
/** 执行结果
* <com.bpf.anno.autowired> [StudentServiceImpl] sayHello()...
*
* 当 StudentServiceImpl 类去掉 @Service 注解,Student 类中参考型别 userService 注解改成 @Autowired(required=false) 时:
* 会抛出空指标例外,因为在 Student 的 sayHello() 方法中,userService未成功赋值,所以在真正使用上并不会修改 required
*/
}
}
(3)@Qualifer
??当使用@Autowired
注解进行参考型别注入时,由于默认方式为byType
,当存在多个同源的bean时,会抛出例外:org.springframework.beans.factory.NoUniqueBeanDefinitionException
,这时候就需要使用byName
方式了,
@Qualifer
注解结合@Autowired
注解使用可以实作byName
方式的参考型别自动注入,- 注解位置同上,
/**
* Qualifer 注解中只有一个属性 value, 用来指定 bean 的名称即 id,
*/
public @interface Qualifier {
String value() default "";
}
(4)@Resource
@Resource
注解是JDK自带的注解,但 Spring 支持这样的注解使用,@Resource
注解只能为属性赋参考型别的值,默认方式是byName
,- 当使用
byName
无法匹配到任何bean时,会使用byType
方式, - 通过指定
name
属性让注解只通过byName
方式注入bean,
- 当使用
- 在 JDK8 及之前是自带此注解的,更高的版本需要手动汇入依赖,
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
2.7 Spring IOC 总结
??IOC 就是用来管理物件、管理依赖关系的,通过 IOC 可以实作解决处理业务逻辑物件之间的耦合关系,即 Service 和 DAO 之间的解耦合,
- 不适合交给Spring管理的物件:
- 物体类
- servlet、listener、filter 等 WEB 中的物件,因为它们是由 Tomcat 创建和管理的物件,
补充
> 完全注解开发
> Spring Bean 的生命周期
3. AOP 面向切面编程
3.1 AOP 是什么
??AOP (Aspect Orient Programming, 面向切面编程) 是一种编程思想,它可以在不改变源代码的基础上,给业务方法新增功能,
??AOP 是一种动态的思想,它是在程序运行期间,为特定的业务创建代理,通过代理来增加切面功能,而这个代理是存在于存储器中的,
什么是切面?
- 给业务功能新增的功能就是切面,
- 切面一般是非业务功能,而且一般都是可复用的,
- 比如:日志功能、事务功能、权限检查、自变量检查、信息统计等等,
AOP的作用?
- 给业务功能新增方法不需改变源代码,
- 让开发人员专注业务逻辑,提高开发效率,
- 实作业务功能与非业务功能解耦合,
- 切面复用,
3.2 AOP 中的重要术语
术语 | 翻译 | 解释 |
---|---|---|
Aspect | 切面 | 给业务方法新增的功能 |
JoinPoint | 连接点 | 即业务方法 |
Pointcut | 切入点 | 切面的执行位置,一个或多个连接点的集合,即增加切面的所有业务方法, |
Target | 目标物件 | 业务方法的执行者 |
Advice | 通知 | 切面的执行时间, |
??AOP 中重要的三个要素:Aspect、Pointcut、Advice,表示在 Advice时间、在 Pointcut位置 执行 Aspect切面,
3.3 AOP 的使用时机
- 当某些方法需要增加相同功能,而源代码又不方便修改时
- 当给业务方法增加非业务功能时
3.4 AOP 的技术实作
??常用的 AOP 实作技术是 Spring 和 AspectJ,
- Spring:Spring 框架实作了 AOP 思想中的部分功能,但其操作比较繁琐和笨重,
- AspectJ:独立的框架,专门负责 AOP,属于 Eclipse 基金会,
- 官网: https://www.eclipse.org/aspectj/
3.5 AspectJ 框架
??AspectJ 框架中可以使用 注解 和 XML组态档 的方式实作 AOP,
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.12</version>
</dependency>
3.5.1 注解方式
Source Code(1)Advice 通知注解
?AspectJ 框架中表示切面执行的时间是五种通知注解,分别代表不同的执行时间,
注解 | 通知型别 | 执行时间 |
---|---|---|
@Before |
前置通知 | 业务方法前执行 |
@AfterReturning |
后置通知 | 业务方法后执行 |
@Around |
环绕通知 | 业务方法前和后都执行 |
@AfterThrowing |
例外通知 | 业务方法程序中出现例外时执行 |
@After |
最终通知 | 业务方法后执行 |
(2)Pointcut 切入点表达式
?AspectJ 框架中表示切面执行的位置是切入点表达式,本质上可以看作是业务方法的定位标志,
execution(访问权限? 回传值型别 全限定类名?方法名(自变量串列) 例外型别?)
?
代表可选,- 最简形式:
execution(回传值型别 方法名(自变量串列))
- 最简形式:
- 四个部分之间通过空格分开,并且都可以使用通配符??,
通配符 | 含义 |
---|---|
* |
代表任意字符 |
.. |
用在方法自变量中,表示任意自变量串列 用在包名中,表示当前包及其子包路径 |
+ |
用在类名后,表示当前类及其子类 用在界面后,表示当前界面及其实作类 |
(3)@Before 前置通知
- 注解
- 前置通知在目标方法执行之前起作用,
- 属性
value
: 切入点表达式
- 方法定义
public void 方法名(自变量)
- 第一个自变量只能是
JoinPoint
, JoinPoint
: 表示连接点,即执行的业务方法,可以获取方法的相关信息,如自变量、方法名等,
package com.bpf.before.handler;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Date;
@Component
@Aspect
public class MyBeforeAspect {
@Before("execution(public void com.bpf.before.service.impl.SomeServiceImpl.doSome(String) )")
public void addExecTime() {
System.out.println("[MyBeforeAspect] (前置通知) 当前执行时间:" + new Date());
}
@Before("execution(void do*(..))")
public void noteExecMethod(JoinPoint point) {
System.out.println("[MyBeforeAspect] (前置通知) 当前正在运行的方法是:");
System.out.println("\tSign: " + point.getSignature());
System.out.println("\tTarget: " + point.getTarget());
System.out.println("\tKind: " + point.getKind());
System.out.println("\tArgs: " + Arrays.toString(point.getArgs()));
}
}
(4)@AfterReturning 后置通知
- 注解
- 前置通知在目标方法执行之后起作用,
- 属性
value
: 切入点表达式returning
: 宣告自定义变量名,必须与形参中的变量名一致,代表目标方法的执行结果,
- 方法定义
public void 方法名(自变量)
- 第一个自变量只能是
JoinPoint
, JoinPoint
: 表示连接点,即执行的业务方法,可以获取方法的相关信息,如自变量、方法名等,Object
: 表示目标方法的执行结果,推荐使用Object
,
- 特点
- 当业务方法的回传值型别是 基本资料型别及其包装类 或 String 时,切面方法无法改变回传值内容,
- 当业务方法的回传值型别是 其他参考型别的Java物件时,切面方法可以改变回传值内容,
package com.bpf.afterreturning.handler;
import com.bpf.afterreturning.bean.Person;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class MyAfterReturningAspect {
0 评论