AOP
Aspect Oriented Programming(面向切面编程、面向方面编程),可以简单理解为:就是面向特定方法编程
假如,有个场景:需要统计每一个方法执行的耗时
假如笨方法,一个一个加,肯定不行,太费事
iOS可以利用runtime做
而Java,使用的是AOP
AOP的优点:
- 减少重复代码
- 代码无侵入
- 提高开发效率
- 维护方便
AOP是一种思想,而在Spring框架中对这种思想进行的视线,就是Spring AOP
AOP核心概念
- 连接点:JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)
- 通知:Advice,指那些重复的逻辑,也就是共性功能(最终体现为一个方法)
- 切入点:PointCut,匹配连接的条件,通知仅会在切入点方法执行时被应用
- 切面:Aspect,描述通知与切入点的对应关系(通知+切入点)
- 目标对象:Target,通知所应用的对象

AOP的底层,是动态代理
也差不多,都是动态的
通知类型
根据通知方法执行时机的不同,将通知类型分为以下常见的五类:
- @Around: 环绕通知,此注解标注的通知方法在目标方法前、后都被执行(有异常也执行)
- @Before: 前置通知,此注解标注的通知方法在目标方法前被执行
- @After: 后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行
- @AfterReturning: 返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行
- @AfterThrowing: 异常后通知,此注解标注的通知方法发生异常后执行
注意:
- @Around环绕通知,需要自己调用 ProceedingJoinPoint.proceed() 来让原始方法执行,其他通知不需要考虑目标方法执行
- @Around环绕通知,方法的返回,必须指定为Object,来接收原始方法的返回值

通义千问太狗了,我照着视频,要去搞个异常,我刚放21行,直接就把视频里面的
int i = 1/0;给我推出来了。。。
当有多个切面的切入点,都匹配到了目标方法,目标方法运行时,多个通知方法都会被执行,但是,执行顺序怎么样呢?
通知顺序
执行顺序:
方法一:
不同切面类中,默认按照切面类的类名字母排序:
- 目标方法前的通知方法:字母排名靠前的先执行
- 目标方法后的通知方法:字母排名靠前的后执行
示例结果:
MyAspect2 -> before ...
MyAspect3 -> before ...
MyAspect4 -> before ...
MyAspect4 -> after ...
MyAspect3 -> after ...
MyAspect2 -> after ...
类似洋葱,或者栈
方法二:
用@Order(数字)加在切面类上来控制顺序
- 目标方法前的通知方法:数字小的先执行
- 目标方法后的通知方法:数字小的后执行
@Order(n)
切入点表达式
常见形式:
- execution(…):根据方法的签名来匹配
- @annotation(…): 根据注解匹配
切入点表达式-execution
execution 主要根据方法的 返回值、包名、类名、方法名、方法参数 等信息来匹配,语法为:
execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?)
其中,带 ? 的表示可以省略的部分
- 访问修饰符:可省略(比如:public、protected)
- 包名.类名:可省略(但是,强烈建议不省略)
- throws 异常:可省略(注意是方法上声明抛出的异常,不是实际抛出的异常)
可以使用通配符描述切入点
1 * :单个独立的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分
executtion(* com.*.service.*.update*(*))
2 .. :多个连续的任意符号,可以通配任意层级的包,或任意类型、任意个数的参数
execution(* com.itheima..DeptService.*(..))
注意:根据业务需要,可以使用 且(&&)、或(||)、非(!) 来组合比较复杂的切入点表达式。
连接点
在Spring中,用JoinPoint抽象了连接点,用它可以获得方法执行时的相关信息,如:目标类名、方法名、方法参数等。
- 对于
@Around通知,获取连接点信息只能使用ProceedingJoinPoint - 对于其他四种通知,获取连接点信息只能使用
JoinPoint,它是ProceedingJoinPoint的父类型
ThreadLocal
ThreadLocal并不是一个Thread,而是Thread的局部变量
ThreadLocal为每个线程提供一份单独的存储空间,具有线程隔离效果,不同的线程之间不会相互打扰。
Spring的原理
端口优先级:
如果同时存在:.properties、.yml.、.yaml文件
则:properties里面的优先级 > .yml > .yaml
除了这种配置文件属性配置以外,还支持Java系统属性和命令行参数的方式进行属性配置
Java系统属性
-Dserver.port=9000
命令行参数
--server.port=10010

打印显示,端口是:10010
说明:
--server.port > -Dserver.port=9000 > 前面的文件属性配置
Bean
由此引出一个问题:什么是Bean?
参考AI给的答案:
要理解 Spring 中的 Bean,我们可以从基础概念、核心作用、生命周期、依赖注入等维度由浅入深解析,并结合你熟悉的 iOS 开发经验 进行类比,帮助你快速建立认知。
一、Bean 的基础概念(“是什么”)
在 Spring 框架 中,Bean 是由 Spring 容器(ApplicationContext)创建、管理和销毁的对象。简单来说,它就是你写的类(如 Service、DAO、工具类等)的实例,但这个实例的“生老病死”全由 Spring 接管。
- 类比 iOS:Bean 类似于 iOS 中由系统或框架管理的
NSObject实例(比如 UIViewController、UIView 的实例,或你自定义的Manager、Model实例)。区别是,iOS 中你通常手动创建和管理这些对象,而 Spring 中是由容器自动管理。
二、Bean 的核心作用(“为什么需要它”)
Spring 管理 Bean 的核心目的是 “解耦” 和 “自动化管理”,具体体现在:
-
解耦对象创建:不需要手动
new对象,由 Spring 容器负责实例化,避免硬编码依赖。 -
管理生命周期:容器负责 Bean 的初始化、销毁等过程,无需手动处理。
-
依赖注入(DI):自动将一个 Bean 的依赖(如 Service 依赖 DAO)注入到目标 Bean 中,无需手动创建依赖对象。
-
类比 iOS:如果 iOS 中所有对象的创建、依赖注入都由一个“中央容器”自动管理,而不是你在
init或viewDidLoad中手动alloc init,这就是 Spring 管理 Bean 的核心价值——把对象的创建和依赖关系从业务代码中抽离,交给框架统一管理。
三、Bean 的生命周期(“从创建到销毁的过程”)
Spring 中一个 Bean 的完整生命周期包含以下阶段(类比 iOS 对象的生命周期,更清晰):
| Spring Bean 生命周期阶段 | 作用 | 类比 iOS 中的对应过程 |
|---|---|---|
| 1. 实例化(Instantiation) | 容器通过反射创建 Bean 实例(相当于 alloc) | NSObject 的 alloc 阶段 |
| 2. 属性注入(Property Injection) | 容器为 Bean 注入依赖的其他 Bean(如给 Service 注入 DAO) | 手动给对象设置属性(如 self.manager = [[Manager alloc] init]) |
| 3. 初始化前(BeanPostProcessor.postProcessBeforeInitialization) | 自定义前置处理(如修改 Bean 属性) | 无直接类比,类似初始化前的“钩子方法” |
| 4. 初始化(Initialization) | 执行 Bean 的初始化方法(如 @PostConstruct 标注的方法) | NSObject 的 init 完成后,或 viewDidLoad 阶段 |
| 5. 初始化后(BeanPostProcessor.postProcessAfterInitialization) | 自定义后置处理(如给 Bean 做代理增强) | 无直接类比,类似初始化后的“钩子方法” |
| 6. 使用(Usage) | 业务代码中使用 Bean | 正常调用对象的方法 |
| 7. 销毁(Destruction) | 容器销毁 Bean 前执行销毁方法(如 @PreDestroy 标注的方法) | NSObject 的 dealloc 阶段 |
- 举个例子:
你写了一个UserService类,Spring 容器会先new UserService()(实例化),然后把依赖的UserDAO注入到UserService中(属性注入),接着调用UserService的初始化方法(比如初始化缓存),之后你就可以在 Controller 中调用UserService的方法(使用阶段),最后当容器关闭时,调用UserService的销毁方法(比如释放缓存资源)。
四、依赖注入(DI):Bean 之间的“协作”方式
依赖注入是 Spring 最核心的特性之一:如果 Bean A 依赖 Bean B,Spring 会自动把 B 的实例“注入”到 A 中,无需 A 手动创建 B。
-
实现方式:
- 构造器注入:通过构造方法传入依赖,如
public UserService(UserDAO userDAO) { this.userDAO = userDAO; }。 - Setter 注入:通过 Setter 方法传入依赖,如
public void setUserDAO(UserDAO userDAO) { this.userDAO = userDAO; }。 - 注解注入:用
@Autowired直接标注属性,如@Autowired private UserDAO userDAO;。
- 构造器注入:通过构造方法传入依赖,如
-
类比 iOS:
在 iOS 中,如果你有一个UserViewController依赖UserManager,通常需要手动写self.userManager = [[UserManager alloc] init];。而 Spring 的依赖注入相当于自动帮你完成这行代码,你只需要声明“我依赖 UserDAO”,容器就会自动把实例给你。
这也类似于 iOS 中使用**依赖注入框架(如 Typhoon、Swinject)**的效果——框架帮你管理对象的创建和依赖注入,减少手动硬编码。
五、Bean 的作用域(“创建多少个实例”)
Spring 支持多种作用域,决定了一个 Bean 会被创建多少个实例:
| 作用域 | 含义 | 类比 iOS 中的场景 |
|---|---|---|
| singleton(默认) | 整个容器中只有一个实例(单例) | iOS 中的单例类(如 [UIApplication sharedApplication]) |
| prototype | 每次请求都会创建新实例 | iOS 中每次 alloc init 都会生成新对象 |
| request | 每个 HTTP 请求创建一个实例(Web 场景) | 无直接类比,仅 Web 环境有效 |
| session | 每个用户会话创建一个实例(Web 场景) | 无直接类比,仅 Web 环境有效 |
- 举例:
你的UserDAO是数据库访问层,通常全局只需要一个实例(单例),所以 Spring 默认把它设为singleton;而一个OrderDTO(订单数据传输对象)可能每次请求都需要新实例,就可以设为prototype。
六、Bean 的配置方式(“如何让 Spring 识别 Bean”)
要让 Spring 管理一个类的实例(即把它变成 Bean),有两种常见方式:
-
注解配置:在类上标注
@Component(通用组件)、@Service(服务层)、@Repository(DAO 层)、@Controller(控制层)等注解,Spring 会自动扫描并创建 Bean。- 类比 iOS:相当于在类上打一个“标记”,告诉依赖注入框架“这个类需要被管理”。
-
XML 配置(较古老,现在少用):在
applicationContext.xml中通过<bean>标签配置类的路径、依赖等,如:<bean id="userService" class="com.example.UserService"> <property name="userDAO" ref="userDAO"/> </bean>- 类比 iOS:相当于在配置文件中声明“要创建 UserService 实例,并给它注入 UserDAO 实例”。
总结:Bean 是 Spring 容器管理的“自动化对象”
- 简单来说,Bean 就是 Spring 帮你创建、管理、注入依赖的对象,核心价值是解耦和自动化。
- 类比 iOS:它就像一个“被依赖注入框架托管的
NSObject实例”,你不用手动alloc init,不用手动传递依赖,框架会帮你完成这一切。
理解了 Bean,就理解了 Spring 框架“控制反转(IOC)”的核心思想——把对象的控制权从业务代码转移到框架容器中,让开发更关注业务逻辑,而非对象的创建和管理。
Bean的作用域
第三方Bean
Bean的作用域

- 默认bean是单例的 - singleton : 默认单例的bean是在项目启动时创建的;创建完毕后,会将该bean存入IOC容器
- @Lazy:延迟初始化 —> 延迟到第一次使用的时候,再来初始化
- @Scope(“prototype”) :每次使用都创建新的对象
如果Bean是无状态的,则可以设置为单例的Bean
如果Bean是有状态的,则可以设置为prototype(多例)的Bean
判断是不是无状态的,只需要判断对象里面是否保存数据
保存数据:有状态
不保存数据:无状态
第三方Bean
- 如果要管理的Bean对象来自于第三方(不是自定义的),是无法用
@Component及衍生注解声明bean的,需要用到@Bean注解

@Bean:将方法返回值交给IOC容器管理,成为IOC容器的bean对象
自动配置实现
方案一:添加 @ComponentScan
@ComponentScan(basePackages = {“com.itheima”, “com.example”})
方法二:使用@Import导入
@Import导入的类会被Spring加载到IOC容器中,导入形式主要有以下几种:
- 导入 普通类 :
@Import(TokenParser.class) // 导入的是普通类 - 导入 配置类:
@Import(HeaderConfig.class) // 导入的是配置类 - 导入 ImportSelector 接口实现类
/@Import(MyImportSelector.class) // MyImportSelector实现类 - 批量导入 - 终极做法,使用
注解:@EnableHeaderConfig
好家伙,用了三种方法,引出了注解的作用。虽然还是懵懵懂懂,但是,至少知道了注解的作用
Maven高级-分模块设计与开发
分模块设计-策略:
策略一:按照功能模块拆分,比如:公共组件、商品模块、搜索模块、购物车模块、订单模块等。
策略二:按层拆分,比如:公共组件、实体类、控制层、业务层、数据访问层
策略三:按照功能模块 + 层拆分

继承关系实现
- 创建maven模块
tlias-parent,该工程为父工程,设置打包方式为pom(默认jar) - 在子工程的pom.xml文件中,配置继承关系
- 在父工程中配置各个工程共有的依赖(子工程会自动继承父工程的依赖)
关于打包方式:
jar: 普通模块打包,springboot项目基本都是jar包(内嵌tomcat运行)
war: 普通web程序打包,需要部署在外部tomcat服务器中运行
pom: 父工程或聚合工程,该模块不写代码,仅进行依赖管理
版本锁定
在maven中,可以在父工程的pom文件中,通过<dependencyManagement>来统一管理依赖版本。

2636

被折叠的 条评论
为什么被折叠?



