JavaWeb开发---学习---(二)

AOP

Aspect Oriented Programming(面向切面编程、面向方面编程),可以简单理解为:就是面向特定方法编程

假如,有个场景:需要统计每一个方法执行的耗时

假如笨方法,一个一个加,肯定不行,太费事
iOS可以利用runtime做
而Java,使用的是AOP

AOP的优点:

  1. 减少重复代码
  2. 代码无侵入
  3. 提高开发效率
  4. 维护方便

AOP是一种思想,而在Spring框架中对这种思想进行的视线,就是Spring AOP

AOP核心概念

  • 连接点:JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)
  • 通知:Advice,指那些重复的逻辑,也就是共性功能(最终体现为一个方法)
  • 切入点:PointCut,匹配连接的条件,通知仅会在切入点方法执行时被应用
  • 切面:Aspect,描述通知与切入点的对应关系(通知+切入点)
  • 目标对象:Target,通知所应用的对象

在这里插入图片描述

AOP的底层,是动态代理

也差不多,都是动态的

通知类型

根据通知方法执行时机的不同,将通知类型分为以下常见的五类:

  1. @Around: 环绕通知,此注解标注的通知方法在目标方法前、后都被执行(有异常也执行)
  2. @Before: 前置通知,此注解标注的通知方法在目标方法被执行
  3. @After: 后置通知,此注解标注的通知方法在目标方法被执行,无论是否有异常都会执行
  4. @AfterReturning: 返回后通知,此注解标注的通知方法在目标方法被执行,有异常不会执行
  5. @AfterThrowing: 异常后通知,此注解标注的通知方法发生异常后执行
注意:
  1. @Around环绕通知,需要自己调用 ProceedingJoinPoint.proceed() 来让原始方法执行,其他通知不需要考虑目标方法执行
  2. @Around环绕通知,方法的返回,必须指定为Object,来接收原始方法的返回值

在这里插入图片描述

通义千问太狗了,我照着视频,要去搞个异常,我刚放21行,直接就把视频里面的 int i = 1/0;给我推出来了。。。

当有多个切面的切入点,都匹配到了目标方法,目标方法运行时,多个通知方法都会被执行,但是,执行顺序怎么样呢?

通知顺序

执行顺序:

方法一:

不同切面类中,默认按照切面类的类名字母排序:

  • 目标方法前的通知方法:字母排名靠前的先执行
  • 目标方法后的通知方法:字母排名靠前的后执行

示例结果:

MyAspect2 -> before ...
MyAspect3 -> before ...
MyAspect4 -> before ...

MyAspect4 -> after ...
MyAspect3 ->  after ...
MyAspect2 -> after ...

类似洋葱,或者栈

方法二:

用@Order(数字)加在切面类上来控制顺序

  • 目标方法前的通知方法:数字小的先执行
  • 目标方法后的通知方法:数字小的后执行
    @Order(n)

切入点表达式

常见形式:

  1. execution(…):根据方法的签名来匹配
  2. @annotation(…): 根据注解匹配
切入点表达式-execution

execution 主要根据方法的 返回值、包名、类名、方法名、方法参数 等信息来匹配,语法为:
execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?)

其中,带 ? 的表示可以省略的部分

  1. 访问修饰符:可省略(比如:public、protected)
  2. 包名.类名:可省略(但是,强烈建议不省略)
  3. 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 的实例,或你自定义的 ManagerModel 实例)。区别是,iOS 中你通常手动创建和管理这些对象,而 Spring 中是由容器自动管理。

二、Bean 的核心作用(“为什么需要它”)

Spring 管理 Bean 的核心目的是 “解耦”“自动化管理”,具体体现在:

  • 解耦对象创建:不需要手动 new 对象,由 Spring 容器负责实例化,避免硬编码依赖。

  • 管理生命周期:容器负责 Bean 的初始化、销毁等过程,无需手动处理。

  • 依赖注入(DI):自动将一个 Bean 的依赖(如 Service 依赖 DAO)注入到目标 Bean 中,无需手动创建依赖对象。

  • 类比 iOS:如果 iOS 中所有对象的创建、依赖注入都由一个“中央容器”自动管理,而不是你在 initviewDidLoad 中手动 alloc init,这就是 Spring 管理 Bean 的核心价值——把对象的创建和依赖关系从业务代码中抽离,交给框架统一管理

三、Bean 的生命周期(“从创建到销毁的过程”)

Spring 中一个 Bean 的完整生命周期包含以下阶段(类比 iOS 对象的生命周期,更清晰):

Spring Bean 生命周期阶段作用类比 iOS 中的对应过程
1. 实例化(Instantiation)容器通过反射创建 Bean 实例(相当于 allocNSObjectalloc 阶段
2. 属性注入(Property Injection)容器为 Bean 注入依赖的其他 Bean(如给 Service 注入 DAO)手动给对象设置属性(如 self.manager = [[Manager alloc] init]
3. 初始化前(BeanPostProcessor.postProcessBeforeInitialization)自定义前置处理(如修改 Bean 属性)无直接类比,类似初始化前的“钩子方法”
4. 初始化(Initialization)执行 Bean 的初始化方法(如 @PostConstruct 标注的方法)NSObjectinit 完成后,或 viewDidLoad 阶段
5. 初始化后(BeanPostProcessor.postProcessAfterInitialization)自定义后置处理(如给 Bean 做代理增强)无直接类比,类似初始化后的“钩子方法”
6. 使用(Usage)业务代码中使用 Bean正常调用对象的方法
7. 销毁(Destruction)容器销毁 Bean 前执行销毁方法(如 @PreDestroy 标注的方法)NSObjectdealloc 阶段
  • 举个例子:
    你写了一个 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),有两种常见方式:

  1. 注解配置:在类上标注 @Component(通用组件)、@Service(服务层)、@Repository(DAO 层)、@Controller(控制层)等注解,Spring 会自动扫描并创建 Bean。

    • 类比 iOS:相当于在类上打一个“标记”,告诉依赖注入框架“这个类需要被管理”。
  2. 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容器中,导入形式主要有以下几种:

  1. 导入 普通类 :@Import(TokenParser.class) // 导入的是普通类
  2. 导入 配置类:@Import(HeaderConfig.class) // 导入的是配置类
  3. 导入 ImportSelector 接口实现类 /@Import(MyImportSelector.class) // MyImportSelector实现类 - 批量导入
  4. 终极做法,使用注解 : @EnableHeaderConfig

好家伙,用了三种方法,引出了注解的作用。虽然还是懵懵懂懂,但是,至少知道了注解的作用


Maven高级-分模块设计与开发

分模块设计-策略:

策略一:按照功能模块拆分,比如:公共组件、商品模块、搜索模块、购物车模块、订单模块等。
策略二:按层拆分,比如:公共组件、实体类、控制层、业务层、数据访问层
策略三:按照功能模块 + 层拆分

在这里插入图片描述

继承关系实现

  1. 创建maven模块tlias-parent,该工程为父工程,设置打包方式为pom(默认jar)
  2. 在子工程的pom.xml文件中,配置继承关系
  3. 在父工程中配置各个工程共有的依赖(子工程会自动继承父工程的依赖)

关于打包方式:
jar: 普通模块打包,springboot项目基本都是jar包(内嵌tomcat运行)
war: 普通web程序打包,需要部署在外部tomcat服务器中运行
pom: 父工程或聚合工程,该模块不写代码,仅进行依赖管理

版本锁定

maven中,可以在父工程的pom文件中,通过<dependencyManagement>来统一管理依赖版本。
在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值