Spring概述 -- 《Spring In Action》

Spring 根本使命: 简化Java开发替换重量级的JavaEE技术(eg: EJB)

简化Java开发

  • 为了降低Java开发的复杂性,Spring提供4中策略:
    1. 基于POJO的轻量级和最小侵入式开发;
    2. 通过依赖注入和面向接口实现松耦合;
    3. 基于切面和惯例进行声明式编程
    4. 通过切面和样板减少样板式代码

Spring使用Java Bean或Bean来表示应用组件,一个Spring组件可以是任何形式的POJO(Plain Old Java Object)

依赖注入

任何一个有实际意义的App都是由两个或更多的类组成,这些类之间相互协作来完成特定的业务逻辑。每个对象负责管理与自己相互协作的对象引用,这将导致高度耦合难以测试的代码~
创建应用组件之间协作的行为称为装配(wiring)

第一种装配方式——构造器注入

class 鸭叫行为{
public void 鸭叫(){
System.out.println("嘎 嘎 嘎 ...");
}
}
interface 鸭子{
}
class 红头鸭 implements 鸭子{
private 鸭叫行为 鸭叫行为;
// 与鸭叫行为紧密耦合,难以复用、扩展以及测试
public 红头鸭(){
this.鸭叫行为 = new 鸭叫行为();
}
public void 执行鸭叫行为(){
鸭叫行为.鸭叫();
}
}

在红头鸭的构造函数中自定创建鸭叫行为,使得红头鸭与鸭叫行为紧密耦合在一起难以扩展

耦合具有两面性(two-headed beast)
坏处:紧密耦合的代码难以测试、难以复用、难以理解,会出现“打地鼠”一样的bug
好处:一定程度的耦合是必须的-完全没有耦合的代码什么也做不了,为了完成有实际意义的功能,不同类必须以适当的方式进行交互。

⭐️ DI(Dependency Injection) : 通过DI,对象的依赖关系将由系统中负责协调各对象的第三方组件在创建对象的时候进行设定。对象无需自行创建或管理他们的依赖关系。

DI会将依赖关系自动交给目标对象,而不是让对象自己去获取依赖。

interface 鸭叫行为{
public void 鸭叫();
}
interface 鸭子{
}
class 红头鸭 implements 鸭子{
private 鸭叫行为 鸭叫行为;
// 鸭叫行为注入进来(此种注入是DI注入方式之一:构造器注入)
public 红头鸭(鸭叫行为 _鸭叫行为){
this.鸭叫行为 = _鸭叫行为;
}
public void 执行鸭叫行为(){
鸭叫行为.鸭叫();
}
}

此时鸭叫行为是一种接口,红头鸭没有自行创建对象之间关系而是在构造的时候将对象关系构建起来,传入的鸭叫行为是一种接口是所有鸭叫行为实现类都必须实现的接口,这样鸭叫行为不单单是一种行为而是多种实现。具体类型将不在单一,这就是DI带来最大好处 -- 松耦合

如果一个对象只通过接口(而不是具体实现或初始化过程)来表明依赖关系,这种依赖能在对象本身不知情的情况下,用不同的具体实现进行替换。

第二种装配方式——XML注入

  • Java核心逻辑
interface 鸭叫行为{
public void 鸭叫();
}
class 吱吱吱叫 implements 鸭叫行为{
private PrintStream printStream;
public 吱吱吱叫(PrintStream printStream){
this.printStream = printStream;
}
@Override
public void 鸭叫() {
printStream.println("吱吱吱...");
}
}
  • 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">
<bean id="吱吱吱" class="ink.openmind.space01.吱吱吱叫">
<constructor-arg value="#{T(System).out}" />
</bean>
</beans>

第三种装配方式——基于Java配置注入

interface Knight{
}
interface Quest{
}
class BraveKnight implements Knight{
private Quest quest;
public BraveKnight(Quest quest){
this.quest = quest;
}
}
class SlayDragonQuest implements Quest{
private PrintStream printStream;
public SlayDragonQuest(PrintStream printStream){
this.printStream = printStream;
}
}
@Configuration
class KnightConfig{
@Bean
public Knight knight(){
return new BraveKnight(quest());
}
@Bean
public Quest quest(){
return new SlayDragonQuest(System.out);
}
}

BraveKnight依赖于Quest但不知道传递给它的是什么类型的Quest,也不知道Quest来自哪里。SlayDragonQuest依赖于PrintStream但不知道这个PrintStream是什么样子,只有Spring通过了解这些组成部分是如何装配起来的

Spring AOP 实现应用切面(公共服务如何能够实现在保持自身内聚性和不侵入核心业务逻辑的前提下相互协作实现业务功能)

DI能够让相互协作的软件组件保持松散耦合,而面向切面编程(Aspect-oriented Programming,AOP)允许把遍布应用各处的功能分离出来形成可重用的组件。
面向切面编程促使软件系统实现关注点分离的一项技术。

系统由许多不同的组件组成,每一个组件负责一块特定功能。除了实现自身核心的功能之外,这些组件还承担额外的职责例如日志、事务管理和和安全等系统服务经常需要融合到自身具体核心业务逻辑的组件中,这些公共系统服务称之为横切关注点,因为他们横跨系统的多个组件。

利用AOP,系统范围内的关注点覆盖在他们所影响组件之上

  • 定义切点和切面类
class 公共服务{
public void 执行日志(){
// 内部逻辑
}
public void 结束日志(){
// 内部逻辑
}
}
class 支付系统{
// 切点
public void 支付订单(){
}
}
  • 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" 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/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="支付系统" class="ink.openmind.space01.支付系统"/>
<bean id="公共服务" class="ink.openmind.space01.公共服务"/>
<aop:config>
<!-- 声明切面 -->
<aop:aspect ref="公共服务">
<!--声明切点-->
<aop:pointcut id="支付系统切点" expression="execution(* *.支付订单(..))"/>
<!--声明前置通知-->
<aop:before pointcut-ref="支付系统切点" method="执行日志"/>
<!--声明后置通知-->
<aop:after pointcut-ref="支付系统切点" method="结束日志"/>
</aop:aspect>
</aop:config>
</beans>

Spring的aop配置命名空间将公共服务bean声明一个切面。
aop:aspect声明切面bean
aop:pointcut声明切点
aop:before在切点之前执行前置通知(before advice)
aop:after在切点之后执行后置通知(after advice)
pointcut-ref: 引用名字为“支付系统切点"的切入点由pointcut定义

Spring 容器(存放和管理bean的区域)

基于Spring开发的应用,应用对象生存与Spring Container中,Spring容器负责创建对象,装配对象以及配置对象并管理其整个生命周期(new -> finalize())。

Spring容器使用DI管理构成应用的组件,创建相互协作之间的关联。
Spring存在多个容器可归为两种类型

  1. bean工厂(org.springframework.beans.factory.beanFactory)接口定义,提供简单的DI支持。
  2. ApplicationContext应用上下文(org.springframeowrk.context.ApplicationContext)接口定义。

使用ApplicationContext应用上下文

  • AnnotationConfigApplicationContext: 从一个或多个基于Java的配置类加载Spring应用上下文
ApplicationContext context = new AnnotationConfigApplicationContext(com.springinaction.test.config.BeanConfig.class)
  • AnnotationConfigWebApplicationContext: 从一个或多个基于Java的配置类加载Spring Web应用上下文
  • ClassPathXmlApplicationContext: 从类路径下的一个或多个XML配置文件中加载上下文定义,把应用上下文的定义文件作为类资源
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  • FileSystemXmlApplicationContext: 从文件系统下的一个或多个XML配置文件加载上下文定义
ApplicationContext context = new FileSystemXmlApplicationContext("c:/applicationContext.xml");
  • XmlWebApplicationContext: 从Web应用下的一个或多个XML中加载上下文定义

Spring系列框架

总结

Spring致力Java EE开发,促进代码松散耦合。成功的关键在于依赖注入和AOP。

posted @   Felix_Openmind  阅读(518)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
*{cursor: url(https://files-cdn.cnblogs.com/files/morango/fish-cursor.ico),auto;}
点击右上角即可分享
微信分享提示