-
干货分享,感谢您的阅读! 在这个迅速发展的技术时代,开源框架如春天般蓬勃生长,其中Spring框架则以其灵活性和强大功能赢得了开发者们的青睐。而在这个庞大的框架中,有一个小小的角色却承担着至关重要的任务,那就是BeanDefinition。它不仅是Spring容器的“元信息”守护者,更是实现高效、灵活应用的秘密武器。 想象一下,如果没有BeanDefinition,开发者们在创建和管理Bean时将面临怎样的混乱局面?是不是像失去了指南针的探险者,在茫茫大海中寻找方向?本文将带你深入探讨BeanDefinition的奥秘,从基本概念到实际应用,甚至是各种构造方式的对比分析,让你在一篇文章中全面理解这个看似不起眼但却不可或缺的组成部分。快来一起揭开BeanDefinition的面纱,看看它如何在背后默默地为你的应用程序提供支持和灵活性吧! 一、对BeanDefinition的理解 BeanDefinition 可以被认为是 Bean 的元信息。它不是 Bean 实例本身,而是描述了如何创建 Bean 实例以及 Bean 的配置信息。因此,BeanDefinition 提供了关于 Bean 的元数据,包括类名、作用域、构造函数参数、属性值等等。在 Spring 容器启动时,BeanDefinition 被解析并用于实例化和管理 Bean 实例。 (一)理解元信息 在计算机科学中,元信息是用于描述其他数据的数据。元信息提供关于数据的属性、结构、类型或其他相关信息,通常用于解释和管理数据,而不是直接操作数据本身。 在软件开发中,元信息的概念非常常见。比如,在数据库中,表的结构信息(如列名、数据类型、约束等)就是元信息;在编程语言中,注解(Annotation)提供了关于类、方法或字段的额外信息,也可以看作是元信息;在 Web 开发中,HTML 标签中的属性、HTTP 头部中的信息等都可以视为元信息。 (二)BeanDefinition理解分析 BeanDefinition 是关于 Bean 的元数据集合(包含了创建和管理 Bean 所需的所有信息),这些信息在 Spring 容器启动时被解析,并用于实例化和管理 Bean 实例。通过配置和操作 BeanDefinition可以控制和定制 Bean 的行为,实现更加灵活和高效的应用程序开发。具体的元信息可总结如下表进行查看: 元信息 描述 类信息 (Class Information) Bean的类名,用于指定如何实例化Bean。 作用域信息 (Scope Information) Bean的作用域,决定了Bean的生命周期范围,包括singleton、prototype、request、session等。 构造函数参数 (Constructor Arguments) 描述了Bean构造函数的参数信息,用于实例化Bean对象。 属性信息 (Property Information) 描述了Bean的属性值信息,包括属性名和属性值,用于在实例化后设置Bean的属性。 初始化和销毁方法 (Initialization and Destruction Methods) 指定了Bean的初始化方法和销毁方法,用于在Bean实例化后或销毁前执行额外的操作。 依赖信息 (Dependency Information) 描述了Bean之间的依赖关系,使得容器能够按正确的顺序实例化和管理Bean。 其他元信息 (Other Metadata) 包括Bean的描述、别名等其他元信息,用于进一步描述Bean的用途和特性。 二、BeanDefinition的结构设计分析 (一)整体结构体会 (二)重要接口和类分析 我们选取一些重要接口和类整理表格来快速回顾(源码暂时不进行分析了): 接口/类 描述 BeanDefinition 接口 定义了 Bean 的元信息,包括类名、作用域、构造函数参数、属性值等。 AbstractBeanDefinition 抽象类 BeanDefinition 接口的抽象实现类,提供了通用属性的默认实现,如 Bean 类型、作用域、懒加载等。 GenericBeanDefinition 类 AbstractBeanDefinition 的具体实现类,用于描述通用的 BeanDefinition 结构,适用于多种配置方式。 RootBeanDefinition 类 GenericBeanDefinition 的子类,增加了对 Bean 类型自动检测的支持,可以根据配置自动确定 Bean 的类型。 AnnotatedBeanDefinition 接口 表示使用注解配置的 BeanDefinition,继承自 BeanDefinition 接口,用于描述通过注解方式配置的 Bean。 BeanDefinitionHolder 类 BeanDefinition 的持有者,包含一个 BeanDefinition 对象以及与之关联的名称和别名。 BeanDefinitionRegistry 接口 定义了 BeanDefinition 注册的方法,允许向 Spring 容器注册新的 BeanDefinition,或者从容器中移除已有的 BeanDefinition。 DefaultListableBeanFactory 类 BeanDefinitionRegistry 接口的默认实现类,实现了 BeanDefinition 的注册和管理功能,是 Spring 容器的核心部分之一。 接下来我们重点看如何构造BeanDefinition。 三、构造 BeanDefinition:模式注解 + 组件扫描方式 基于约定优于配置的原则 使用注解(如 @Component、@Service、@Repository、@Controller 等)标记类,然后通过组件扫描(Component Scanning)方式,Spring 容器会自动扫描指定的包,并根据这些注解自动创建相应的 BeanDefinition。 这种方式在平时开发中最为常见,比如直接定义一个简单的组件类并用 @Component 注解进行标记如下: package org.zyf.javabasic.spring.beandefinition; import org.springframework.stereotype.Component; /** * @program: zyfboot-javabasic * @description: 定义一个简单的组件类,并使用 @Component 注解进行标记 * @author: zhangyanfeng * @create: 2024-05-02 11:03 **/ @Component public class ZyfComponent { public void sayHello() { System.out.println("Hello from ZyfComponent!"); } } 为符合其构造,我们在定义一个配置类并在其中启用组件扫描如下: package org.zyf.javabasic.spring.beandefinition; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * @program: zyfboot-javabasic * @description: 定义一个配置类,并在其中启用组件扫描。 * @author: zhangyanfeng * @create: 2024-05-02 11:08 **/ @Configuration @ComponentScan("org.zyf.javabasic.spring.beandefinition") public class ZyfAppConfig { // 配置类的其他内容 } 直接验证如下:Spring 容器在启动时扫描指定包路径下的组件类,并将标记了 @Component 注解的类注册为 BeanDefinition,这样就可以方便地在应用程序中使用这些 Bean。 package org.zyf.javabasic.spring.beandefinition; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @program: zyfboot-javabasic * @description: ceshi * @author: zhangyanfeng * @create: 2024-05-02 11:09 **/ public class ZyfApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ZyfAppConfig.class); ZyfComponent myComponent = context.getBean(ZyfComponent.class); myComponent.sayHello(); context.close(); } } 四、构造 BeanDefinition:配置类 + @Bean 注解方式 显式配置每个 Bean 的创建过程 创建一个配置类,通常使用 @Configuration 注解标记,在这个类中,使用 @Bean 注解标记方法,方法返回的对象就是一个 Bean。 简单来说,可以先创建一个普通的 Java 类作为需要被 Spring 管理的 Bean: package org.zyf.javabasic.spring.beandefinition; /** * @program: zyfboot-javabasic * @description: 创建一个普通的 Java 类,作为需要被 Spring 管理的 Bean。 * @author: zhangyanfeng * @create: 2024-05-02 11:18 **/ public class ZyfService { public void performAction() { System.out.println("Performing action in MyService!"); } } 在刚刚创建的配置类ZyfAppConfig中使用 @Bean 注解标记方法增加即可: package org.zyf.javabasic.spring.beandefinition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * @program: zyfboot-javabasic * @description: 定义一个配置类,并在其中启用组件扫描。 * @author: zhangyanfeng * @create: 2024-05-02 11:08 **/ @Configuration @ComponentScan("org.zyf.javabasic.spring.beandefinition") public class ZyfAppConfig { // 配置类的其他内容 @Bean public ZyfService zyfService() { return new ZyfService(); } } 直接验证如下: package org.zyf.javabasic.spring.beandefinition; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @program: zyfboot-javabasic * @description: ceshi * @author: zhangyanfeng * @create: 2024-05-02 11:09 **/ public class ZyfApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ZyfAppConfig.class); ZyfComponent myComponent = context.getBean(ZyfComponent.class); myComponent.sayHello(); ZyfService zyfService = context.getBean(ZyfService.class); zyfService.performAction(); context.close(); } } 返回结果符合预期。 五、构造 BeanDefinition:通过XML配置文件 通过 XML 配置文件构造 BeanDefinition 是 Spring 框架最传统的方式之一。 传统的方式是通过 XML 配置文件来定义 Bean,XML 配置文件中的 <bean> 元素就是描述 BeanDefinition 的方式之一。 现在,我们将之前的ZyfService配置到XML 配置文件中如下: <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="myTestMsgBean" class="org.zyf.javabasic.springextend.bfppext.MyTestMsgBean">--> <!-- <property name="message" value="Original Message"/>--> <!-- </bean>--> <!-- <bean class="org.zyf.javabasic.springextend.bfppext.MyBeanFactoryPostProcessorForUpdateBean"/>--> <!-- <bean class="org.zyf.javabasic.springextend.bfppext.MyBeanFactoryPostProcessorForNewDefinRegi"/>--> <!-- <bean class="org.zyf.javabasic.springextend.bfppext.MyBeanFactoryPostProcessorForDefineMata"/>--> <bean id="zyfService" class="org.zyf.javabasic.spring.beandefinition.ZyfService"/> </beans> 使用 ClassPathXmlApplicationContext 类来加载配置文件,从而启动 Spring 容器如下: package org.zyf.javabasic.spring.beandefinition; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @program: zyfboot-javabasic * @description: ceshi * @author: zhangyanfeng * @create: 2024-05-02 11:09 **/ public class ZyfApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ZyfAppConfig.class); ZyfComponent myComponent = context.getBean(ZyfComponent.class); myComponent.sayHello(); ZyfService zyfService = context.getBean(ZyfService.class); zyfService.performAction(); ClassPathXmlApplicationContext contextXml = new ClassPathXmlApplicationContext("zyf_application_context.xml"); ZyfService myServiceXml = context.getBean("zyfService", ZyfService.class); myServiceXml.performAction(); context.close(); } } 六、构造 BeanDefinition:通过编程方式 通过编程方式构造 BeanDefinition允许开发人员完全控制 BeanDefinition 的创建过程。 我们可以使用 Spring 提供的类(如 GenericBeanDefinition、RootBeanDefinition 等)来创建 BeanDefinition 对象,并指定 Bean 的各种属性和配置信息。 也就是可以直接操作代码即可,在原始代码上直接修改如下: package org.zyf.javabasic.spring.beandefinition; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @program: zyfboot-javabasic * @description: ceshi * @author: zhangyanfeng * @create: 2024-05-02 11:09 **/ public class ZyfApplication { public static void main(String[] args) { System.out.println("===================通过模式注解 + 组件扫描方式==================="); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ZyfAppConfig.class); ZyfComponent myComponent = context.getBean(ZyfComponent.class); myComponent.sayHello(); System.out.println("===================通过配置类 + @Bean 注解方式==================="); ZyfService zyfService = context.getBean(ZyfService.class); zyfService.performAction(); System.out.println("===================通过XML配置文件方式==================="); ClassPathXmlApplicationContext contextXml = new ClassPathXmlApplicationContext("zyf_application_context.xml"); ZyfService myServiceXml = context.getBean("zyfService", ZyfService.class); myServiceXml.performAction(); System.out.println("===================通过编程方式==================="); // 创建一个 BeanDefinition,并指定类名 GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(ZyfService.class); // 将 BeanDefinition 注册到 Spring 容器中 DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getBeanFactory(); beanFactory.registerBeanDefinition("zyfService", beanDefinition); // 获取 Bean,并调用方法 ZyfService myService = context.getBean("zyfService", ZyfService.class); myService.performAction(); context.close(); } } 验证打印如下,符合预期。 七、对构造分析对比 方式 配置灵活性 可读性和维护性 适用场景 模式注解 + 组件扫描方式 配置简单,基于约定优于配置的原则 适用于大部分情况 配置简单,但可能导致扫描过多的类,降低可读性 简单的项目或小规模团队 快速开发原型或中小型项目 配置类 + @Bean 注解方式 灵活性更高,可以通过代码进行复杂的配置和逻辑处理 相对易读,也更易于维护,因为 Bean 的创建过程明确可见 需要更灵活配置的项目 对可维护性要求较高的大型项目 通过 XML 配置文件 传统方式,配置直观,但可读性较差 相对直观,但随项目规模增大,配置文件可能变得臃肿 传统项目 需要与其他框架整合的场景 通过编程方式 灵活性最高,可以完全控制 BeanDefinition 的创建过程 -代码量较多,可读性较差,维护成本相对较高 需要动态配置 BeanDefinition 的场景 需要在运行时动态注册 Bean 的场景 ———————————————— 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.csdn.net/xiaofeng10330111/article/details/138388372
-
Spring IOC容器 Spring框架的主要功能是通过Spring容器实现的,Spring容器可以管理人们开发的各种Bean。Spring提供了相应API来管理Bean,在Spring容器的API里,最常用的是BeanFactory和ApplicationContext这两个接口。 🚦BeanFactory接口 BeanFactory 是 Spring 框架中的一个核心接口,它定义了 Spring IoC 容器的基本功能。BeanFactory 提供了配置应用程序组件的方式,并且负责这些组件的生命周期和依赖关系的管理。它是 Spring 框架中最原始的 IoC 容器的表现形式。BeanFactory 通常由 ApplicationContext 接口实现。 BeanFactory接口的一些关键方法示例: Object getBean(String name):根据给定的 Bean 名称获取一个 Bean 实例。 <T> T getBean(String name, Class<T> requiredType):根据给定的 Bean 名称和期望的类型获取一个 Bean 实例。 Object getBean(String name, Object... args):根据给定的 Bean 名称和构造函数参数获取一个 Bean 实例。 <T> T getBean(Class<T> requiredType):根据给定的类型获取一个 Bean 实例。 <T> T getBean(Class<T> requiredType, Object... args):根据给定的类型和构造函数参数获取一个 Bean 实例。 boolean containsBean(String name):检查是否包含一个具有给定名称的 Bean。 boolean isSingleton(String name):检查一个具有给定名称的 Bean 是否是单例作用域。 boolean isPrototype(String name):检查一个具有给定名称的 Bean 是否是原型作用域。 boolean isTypeMatch(String name, Class<?> targetType):检查一个具有给定名称的 Bean 是否是指定类型的一个匹配。 Class<?> getType(String name):获取一个具有给定名称的 Bean 的类型。 String[] getAliases(String name):获取一个具有给定名称的 Bean 的所有别名。 🚦ApplicationContext接口 ApplicationContext 接口是 Spring 框架中 BeanFactory 的一个扩展,它提供了更多的高级功能和特性,以支持企业级应用开发。ApplicationContext 包含了 BeanFactory 的所有功能。 ApplicationContext 接口的一些关键实现包括: ClassPathXmlApplicationContext:从类路径下的 XML 配置文件中加载上下文定义。 FileSystemXmlApplicationContext:从文件系统下的 XML 配置文件中加载上下文定义。 AnnotationConfigApplicationContext:从注解配置类中加载上下文定义。 WebApplicationContext:用于 Web 应用程序,如 ServletContext 或 PortletContext。 🎯Bean的配置 在Spring框架中,Bean的配置是将类定义为Spring容器管理的对象的过程。Spring支持多种方式来配置Bean,包括XML配置、注解和Java配置类。 🚦Bean元素的多个属性 在Spring的XML配置中,<bean>元素具有多个属性,用于定义和管理Bean的创建和行为。以下是一些常用的<bean>元素属性: id:Bean的唯一标识符。通常用于通过getBean方法按名称检索Bean。 class:Bean的全限定类名。Spring将使用这个类名来实例化Bean。 name:为Bean提供额外的名称。可以有多个名称,它们之间用逗号、空格或分号分隔。 scope:定义Bean的作用域。常见的作用域包括singleton(单例,默认值)、prototype(原型)、request(Web应用中的请求)、session(Web应用中的会话)等。 lazy-init:指定Bean是否应该延迟初始化,直到它被首次请求时才创建。 🚦Bean元素的多个子元素 在Spring的XML配置中,<bean>元素可以包含多个子元素,这些子元素用于定义Bean的属性、构造函数参数、元数据以及其他配置。以下是一些常用的<bean>子元素: <property>:用于设置Bean的属性。它包含一个或多个<value>、<ref>或<bean>子元素,用于指定属性的值。 <constructor-arg>:用于提供构造函数参数。可以包含<value>、<ref>或<bean>子元素,或者通过index或type属性指定参数的位置或类型。 <property>的集合:可以定义多个<property>子元素来设置Bean的多个属性。 🎯Bean的实例化 在Spring框架中,Bean的实例化是指创建Bean实例的过程。Spring提供了多种方式来实例化Bean,这些方式可以通过配置来指定。以下是Spring中常见的Bean实例化方式: 🚦构造方法实例 在Spring中,构造方法实例化是通过调用类的构造方法来创建Bean实例的一种方式。这种方式适用于需要通过构造参数来初始化Bean的情况。以下是如何在Spring中使用构造方法实例化Bean的步骤: ✨pom.xml文件 <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.8.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.2.8.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.2.8.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>5.2.8.RELEASE</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> </dependencies> ✨创建Bean1类 public class Bean1 { public Bean1(){ System.out.println("Bean1"); } } ✨编写applicationContext.xml文件 <bean id="bean1" class="com.han.Bean1"></bean> ✨编写测试类 public class Bean1Test { public static void main(String[]args){ ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml"); Bean1 bean= (Bean1) applicationContext.getBean("bean1"); System.out.println(bean); } } 🚦静态工厂实例化 静态工厂实例化是指使用类的静态方法来创建Bean实例的方式。在Spring中,可以通过配置XML文件来指定使用哪个类的静态方法来创建Bean。这种方式特别适用于那些没有构造函数参数或者需要通过特定工厂方法来创建实例的情况。 ✨创建Bean2类 public class Bean2 { public Bean2(){ System.out.println("Bean2"); } } ✨创建MyBean2Factory类 public class Bean2Factory { public static Bean2 createBean(){ return new Bean2(); } } ✨编写applicationContext.xml文件 <bean id="bean2" class="com.han.Bean2Factory" factory-method="createBean"></bean> ✨编写测试类 public class Bean2Test { public static void main(String[]args){ ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml"); Bean2 bean2= (Bean2) applicationContext.getBean("bean2"); System.out.println(bean2); } } 🚦实例工厂实例化 在Spring中,实例工厂实例化是一种创建Bean实例的方法,它允许你通过工厂方法来创建Bean,而不是通过构造函数或静态工厂方法。这种方式提供了更高的灵活性,因为你可以在工厂方法中执行复杂的逻辑来决定哪个Bean的实例应该被创建 ✨创建Bean3类 public class Bean3 { public Bean3(){ System.out.println("Bean3"); } } ✨创建MyBean3Factory类 public class MyBean3Factory { public MyBean3Factory(){ System.out.println("bean3工厂实例化中"); } public Bean3 createBean(){ return new Bean3(); } } ✨编写applicationContext.xml文件 <bean id="mybean3" class="com.han.MyBean3Factory"/> <bean id="bean3" factory-bean="mybean3" factory-method="createBean"/> ✨编写测试类 public class Bean3Test { public static void main(String[]args){ ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml"); Bean3 bean3= (Bean3) applicationContext.getBean("bean3"); System.out.println(bean3); } } 🎯Bean的作用域 在bean标签中可以通过scope属性指定对象的作用域 scope="singleton"表示当前bean是单例模式(默认饿汉模式,Spring容器初始化阶段就会完成此对象的创建,在bean标签中设置lazy-init="true"时变为懒汉模式) scope="prototype"表示当前bean为非单例模式,每次通过Spring容器获取此bean的对象时都会创建一个新的对象。 单例 <bean id="book2Spring" class="com.haust.pojo.Book" scope="singleton" lazy-init="true"></bean> 非单例 <bean id="book1Spring" class="com.haust.pojo.Book" scope="prototype"></bean> bean的声明周期方法 在bean标签中通过init-method属性指定当前bean的初始化方法,初始化方法在构造器执行之后执行。 在bean标签中通过destory-method属性指定当前bean的销毁方法,销毁方法在对象销毁之前执行。 🎯Bean的装配方式 🚦基于Xml的装配 基于XML的装配是Spring框架中的一种配置方式,它允许开发者通过XML文件来定义和管理Bean的创建和依赖关系。这种方式在早期的Spring应用中非常常见,尽管现在注解和Java配置逐渐成为主流,但XML配置仍然是一种重要的配置手段,特别是在需要配置大量复杂Bean关系时。 在XML配置中,Bean的装配可以通过以下几种方式实现: setter注入(Setter Injection):这是最常见的装配方式,通过在XML文件中使用<property>标签来注入依赖。这种方式要求Bean类必须提供一个无参构造方法,并且需要注入的属性必须有对应的setter方法。例如: <bean id="student1" class="educoder.Student"> <property name="name" value="张三" /> <property name="age" value="25" /> </bean> 构造注入(Constructor Injection):通过构造器注入的方式装配Bean,需要在XML配置文件中使用<constructor-arg>标签来定义构造方法的参数。这种方式适用于需要通过构造器传递依赖的场景。例如: <bean id="student2" class="educoder.Student"> <constructor-arg index="0" value="李四" /> <constructor-arg index="1" value="24" /> </bean> 🎯基于注解的装配 基于注解的装配是Spring框架中的一种依赖注入方式,它通过在代码中使用特定的注解来实现Bean的自动装配,从而减少了XML配置的复杂性。这种方式使得配置更加简洁,代码与配置的耦合度更低,提高了开发效率。以下是Spring中常用的几种注解装配方式: @Component、@Service、@Repository、@Controller:这些注解用于声明一个类作为Spring容器管理的Bean。@Component是通用的,而其他注解提供了额外的语义信息。 @Value:用于注入外部配置的值,如properties文件中的值。 @Resource:这是Java标准的一部分,可以通过名称来自动装配Bean,如果Bean的名称与字段名或setter方法名相同,Spring容器会自动装配。 @Autowired:这是最常见的自动装配注解,Spring容器会通过类型来自动装配Bean。如果存在多个相同类型的Bean,可以通过@Qualifier注解来指定具体的Bean。 下面通过一个案例来演示用注解装配Bean,具体步骤及代码如下: 🚦导入依赖 <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.2.8.RELEASE</version> </dependency> 🚦创建XML配置文件 在appicationContext.xml文件中添加如下代码: <context:component-scan base-package="com.han"/> 代码中指定要扫描的包为com.han,这个包下的注解就会生效。 🚦定义实体类 实体类User是一个简单的Java类,使用@Component注解标记为Spring管理的Bean,并设置了作用域为单例。类中定义了三个属性:id、username和password,以及它们的getter和setter方法。@Value注解用于注入配置文件中的值。toString方法被覆盖以提供类的字符串表示。 @Component("user") @Scope("singleton") public class User { @Value("1") private int id; @Value("haha") private String username; @Value("789") private String password; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + '}'; } } 🚦定义DAO层 DAO层定义了一个接口UserDao,其中包含一个save方法。这个接口代表了与数据库交互的数据访问对象,save方法用于表示保存数据的操作。 public interface UserDao { public void save(); } 🚦实现DAO层 UserDaoImpl类实现了UserDao接口,并使用@Repository注解标记为Spring的Repository Bean。它的save方法中通过Spring的ApplicationContext获取了User Bean的实例,并打印了用户信息和保存操作的消息。 //使用Repository将UserDaoImpl类标识为Spring中的Bean @Repository("userDao") public class UserDaoImpl implements UserDao{ @Override public void save() { ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml"); User user= (User) applicationContext.getBean("user"); System.out.println(user); System.out.println("执行UserDaoImpl.save()"); } } 🚦定义Service层 Service层定义了一个接口UserService,其中包含一个save方法。这个接口代表了业务逻辑层,save方法用于表示执行保存操作的业务逻辑。 public interface UserService { public void save(); } 🚦实现Service层 UserServiceImpl类实现了UserService接口,并使用@Repository注解标记为Spring的Service Bean。它通过@Resource注解注入了UserDao,并在save方法中调用了UserDao的save方法,然后打印了业务逻辑层的保存操作消息。 @Repository("userService") public class UserServiceImpl implements UserService{ //使用Resource注解注入UserDao @Resource(name="userDao") private UserDao userDao; @Override public void save() { this.userDao.save(); System.out.println("执行UserServiceImpl.save()"); } } 🚦定义Controller层 Controller层定义了一个UserController类,使用@Controller注解标记为Spring的Controller Bean。它通过@Resource注解注入了UserService,并在save方法中调用了UserService的save方法,然后打印了控制器层的保存操作消息。 //使用Controller注解将UserController类标识为Bean @Controller public class UserController { @Resource(name="userService") private UserService userService; public void save(){ this.userService.save(); System.out.println("执行UserController.save()"); } } 🚦定义测试类 测试类AnnotationTest包含了一个main方法,用于初始化Spring的ApplicationContext并获取UserController的实例。然后调用UserController的save方法来执行保存操作,从而测试整个Spring应用的流程是否正确。 public class AnnotationTest { public static void main(String[]args){ ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml"); UserController userController= (UserController) applicationContext.getBean("userController"); userController.save(); } } 以上就是今天要讲的内容了,主要介绍了Bean的配置,实例化,作用域以及装配方式等相关内容,如果您感兴趣的话,可以订阅我的相关专栏。非常感谢您的阅读,如果这篇文章对您有帮助,那将是我的荣幸。我们下期再见啦🧸! ———————————————— 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.csdn.net/m0_74202856/article/details/142881403
-
使用 Hystrix实现容错 Hystrix 是一个实现了超时机制和断路器模式的工具类库。先来了解什么是Hystrix。 Hystrix简介 Hystrix 是由 Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。Hystrix主要通过以下几点实现延迟和容错。 ·包裹请求:使用HystrixCommand (或HystrixObservableCommand)包裹对依赖的调用逻辑,每个命令在独立线程中执行。这使用了设计模式中的“命令模式”。 ·跳闸机制:当某服务的错误率超过一定阈值时,Hystrix可以自动或者手动跳闸,停止请求该服务一段时间。 ·资源隔离:Hystrix为每个依赖都维护了一个小型的线程池(或者信号量)。如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等候,从而加速失败判定。·监控:Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时和被拒绝的请求等。 ·回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑可由开发人员自行提供,例如返回一个缺省值。 使用Hyur实 ·自我修复:断路器打开一段时间后,会自动进入“半开"状态。断路器打开,关 半开的逻辑转换,前面已经详细探讨过了,本小节不再赘述。 。Hystrix的GitHub: https://github.com/Netflix/Hystrix. ,命令模式:https://en.wikipedia.org/wiki/Command_pattern. 通用方式整合Hystrix 在Spring Cloud中,整合Hystrix非常方便。以项目microservice-consumer-movie-ribbon为例,我们来为它整合Hystrix。 1.复制项目microservice-consumer-movie-ribbon,将ArtifactId修改为microservice-con- sumer-movie-ribbon-hystrix。 2.为项目添加以下依赖。 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId></dependency> 3.在启动类上添加注解@EnableCircuitBreaker 或EnableHystrix ,从而为项目启用断路器支持。 4.修改MovieController,让其中的findByld方法具备容错能力。@RestController public class MovieController { private static final Logger LOGGER=LoggerFactory.getLogger(MovieController. class);@Autowired private RestTemplate restTemplate;@Autowired private LoadBalancerClient loadBalancerClient; @HystrixCommand(fallbackMethod =*findByIdFallback*)@GetMapping("/user/{id}") public User findById(@PathVariable Long id){ return this.restTemplate,getForObject("http://microservice-provider-user/" id,User.class); end Pgdn enter ns del 使用 Hystrix实现容错 } public User findByIdFallback(Long id){ User user = new User(); user.setId(-1L); user.setName("默认用户*); return user;} 由代码可知,为findById方法编写了一个回退方法findByldFallback,该方法与find- Byld 方法具有相同的参数与返回值类型,该方法返回了一个默认的User。 在findById方法上,使用注解@HystrixCommand的fallbackMethod属性,指定回退方法是findByIdFallback。注解@HystrixCommand由名为javanica(https://github.com/Netflix/ Hystrix/tree/master/hystrix-contrib/hystrix-javanica )的Hystrix contrib库提供。javanica是一个Hystrix 的子项目,用于简化Hystrix的使用。Spring Cloud 自动将Spring bean与该注解封装在一个连接到Hystrix断路器的代理中。 @HystrixCommand的配置非常灵活,可使用注解@HystrixProperty的commandProperties属性来配置@HystrixCommand。例如: @HystrixCommand(fallbackMethod ="findByIdFallback",commandProperties={ @HystrixProperty(name ="execution.isolation.thread.timeoutInMilliseconds", value ="5000"), @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value =' 10000") },threadPoolProperties ={ @HystrixProperty(name = "coreSize", value = "1"),@HystrixProperty(name = "maxQueueSize",value ="10")}) @GetMapping("/user/{id}*) public User findById(@PathVariable Long id){//... 限于篇幅,笔者就不一一讲解Hystrix 的配置属性了。 https://github.com/Netflix/ Hystrix/tree/master/hystrix-contrib/hystrix-javanica#conf- iguration,详细了解注解@HystrixCommand如何使用。
-
你们觉得现在后端除了spring boot,其他语言是不是都被淘汰了
-
大家好8月份给大家带来的主要是SpringWebFlux方面的知识。希望可以帮助大家。 1.SpringWebFlux响应式框架---HandlerMapping https://bbs.huaweicloud.com/forum/thread-02102160107395888002-1-1.html 2.SpringWebFlux响应式框架---HandlerAdapter https://bbs.huaweicloud.com/forum/thread-0205160107492076001-1-1.html 3.SpringWebFlux响应式框架---HandlerResultHandler https://bbs.huaweicloud.com/forum/thread-0205160107548247002-1-1.html 4.SpringWebFlux响应式框架---WebFilter https://bbs.huaweicloud.com/forum/thread-0249160107639633003-1-1.html 5.SpringWebFlux响应式框架---ServerWebExchange https://bbs.huaweicloud.com/forum/thread-0204160107694268002-1-1.html 6.SpringWebFlux响应式框架---ServerHttpRequest和ServerHttpResponse https://bbs.huaweicloud.com/forum/thread-0205160107731252003-1-1.html 7.SpringWebFlux响应式框架---WebSession https://bbs.huaweicloud.com/forum/thread-02108160108766229004-1-1.html 8.SpringWebFlux响应式框架---Reactive Streams https://bbs.huaweicloud.com/forum/thread-0205160108940083004-1-1.html 9.SpringWebFlux响应式框架---Reactor https://bbs.huaweicloud.com/forum/thread-02115160109128396001-1-1.html 10.SpringWebFlux响应式框架---WebClient https://bbs.huaweicloud.com/forum/thread-02102160109167400003-1-1.html 11.SpringWebFlux响应式框架---Spring Data Reactive https://bbs.huaweicloud.com/forum/thread-02115160109216453002-1-1.html 12.SpringWebFlux响应式框架---Spring Security Reactive https://bbs.huaweicloud.com/forum/thread-02115160109258160003-1-1.html 13. SpringWebFlux响应式框架---HttpHandler https://bbs.huaweicloud.com/forum/thread-0249160109341677005-1-1.html 14.SpringWebFlux响应式框架---ContextPathCompositeHandler https://bbs.huaweicloud.com/forum/thread-02108160109459892006-1-1.html 15.SpringWebFlux响应式框架---DispatcherHandler https://bbs.huaweicloud.com/forum/thread-0250160107329479004-1-1.html 16.深度长文解析SpringWebFlux响应式框架15个核心组件源码(一) https://bbs.huaweicloud.com/forum/thread-0250160107200239003-1-1.html
-
Keepalived结合Nginx实现高可用性的案例涉及在两台或多台服务器上部署Nginx和Keepalived,以确保当主Nginx服务器出现故障时,备份Nginx服务器能够自动接管服务,从而保证服务的连续性和可用性。以下是一个详细的案例说明:一、方案概述目标:通过Keepalived和Nginx实现Web服务的高可用性。环境:两台服务器,分别配置为Master和Backup,均安装Nginx和Keepalived。二、环境准备安装Nginx:在两台服务器上分别安装Nginx。配置Nginx作为负载均衡器或Web服务器,具体取决于实际需求。安装Keepalived:在两台服务器上分别安装Keepalived。Keepalived的安装方式可以是使用包管理器(如yum、apt)或从源代码编译安装。三、配置Keepalived配置Master节点:编辑/etc/keepalived/keepalived.conf文件,设置状态为MASTER,并配置虚拟IP(VIP)。配置健康检查脚本,用于检查Nginx的状态。如果Nginx停止运行,则尝试重启;如果重启失败,则停止Keepalived服务,将VIP转移到Backup节点。示例配置(Master节点):! Configuration File for keepalived global_defs { router_id LVS_DEVEL_MASTER } vrrp_script chk_nginx { script "/etc/keepalived/check_nginx.sh" interval 2 weight -5 } vrrp_instance VI_1 { state MASTER interface eth0 virtual_router_id 51 priority 101 advert_int 1 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { 192.168.1.200 } track_script { chk_nginx } }配置Backup节点:编辑/etc/keepalived/keepalived.conf文件,设置状态为BACKUP,并配置与Master节点相同的虚拟IP(VIP)。配置健康检查脚本,与Master节点相同。示例配置(Backup节点):! Configuration File for keepalived global_defs { router_id LVS_DEVEL_BACKUP } vrrp_script chk_nginx { script "/etc/keepalived/check_nginx.sh" interval 2 weight -5 } vrrp_instance VI_1 { state BACKUP interface eth0 virtual_router_id 51 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { 192.168.1.200 } track_script { chk_nginx } }编写Nginx健康检查脚本(check_nginx.sh):该脚本用于检查Nginx是否正在运行。如果未运行,则尝试启动;如果启动失败,则停止Keepalived服务。示例脚本:#!/bin/bash counter=$(ps -C nginx --no-heading | wc -l) if [ "${counter}" -eq 0 ]; then systemctl start nginx sleep 2 counter=$(ps -C nginx --no-heading | wc -l) if [ "${counter}" -eq 0 ]; then systemctl stop keepalived fi fi四、启动Keepalived服务在两台服务器上分别启动Keepalived服务。使用systemctl start keepalived命令启动服务,并确保服务设置为开机自启。五、测试高可用性通过VIP访问Nginx服务:确保通过VIP(在本例中为192.168.1.200)可以访问Nginx服务。模拟故障转移:停止Master节点的Nginx服务或Keepalived服务。观察VIP是否自动转移到Backup节点。通过VIP访问服务,验证服务是否仍然可用。六、注意事项确保两台服务器的网络互通,且时间同步。监控Keepalived和Nginx的日志文件,以便及时发现和解决问题。定期检查Keepalived和Nginx的配置,确保配置的正确性和安全性。通过以上步骤,你可以实现基于Keepalived和Nginx的高可用性Web服务。这种配置方式可以有效地防止单点故障,提高服务的可靠性和可用性。
-
在Spring Security中动态加载用户权限通常意味着你需要实现一个用户详情服务(UserDetailsService),这个服务不仅需要根据用户名查找用户信息,还需要能够动态地加载该用户的权限信息。权限信息可能来自数据库、缓存或任何其他动态数据源。以下是一个基本步骤,指导你如何在Spring Security中动态加载用户权限:1. 创建用户实体首先,你需要一个用户实体(例如User),这个实体将包含用户名、密码(通常是加密的)、以及其他任何与权限相关的字段(如角色列表)。@Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; @JsonIgnore private String password; @ManyToMany(fetch = FetchType.EAGER) @JoinTable( name = "user_roles", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id") ) private Set<Role> roles = new HashSet<>(); // Getters and Setters }2. 创建角色实体接着,创建一个角色实体(例如Role),它将表示系统中的不同角色,每个角色都可以有零个或多个权限。@Entity public class Role { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; // Getters and Setters }3. 实现UserDetailsService现在,你需要实现UserDetailsService接口,该接口定义了loadUserByUsername方法,该方法将根据用户名从数据库中加载用户及其权限。@Service public class CustomUserDetailsService implements UserDetailsService { @Autowired private UserRepository userRepository; // 假设你有一个用户仓库 @Override @Transactional public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username) .orElseThrow(() -> new UsernameNotFoundException("User not found with username: " + username)); return User.withUsername(user.getUsername()) .password(user.getPassword()) .roles(user.getRoles().stream() .map(role -> "ROLE_" + role.getName().toUpperCase()) .collect(Collectors.toList())) .build(); } }注意:这里的User.withUsername(...)、.password(...)、.roles(...) 和 .build() 是为了演示而简化的代码,你需要自己实现这些方法或类似功能来创建org.springframework.security.core.userdetails.UserDetails对象。4. 配置Spring Security在Spring Security配置中,你需要配置使用你的CustomUserDetailsService。@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private CustomUserDetailsService customUserDetailsService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(customUserDetailsService) .passwordEncoder(new BCryptPasswordEncoder()); } // 其他配置... }5. 处理权限更新由于权限是动态加载的,如果你在运行时更改了用户的权限,Spring Security的SecurityContext不会自动更新。你需要在更改权限后重新认证用户或手动更新SecurityContext。一个常见的做法是在修改用户权限后,重定向用户到登录页面或使用其他机制要求用户重新登录。结论以上就是如何在Spring Security中动态加载用户权限的基本步骤。记得根据你的具体需求调整和优化代码。特别是用户实体的结构、权限的管理和认证机制可能需要根据你的应用场景进行调整。
-
Spring Security 结合 JWT(JSON Web Tokens)实现无状态认证是现代Web应用中的一种常见做法,特别适用于需要RESTful API的微服务架构。JWT提供了一种在客户端和服务器之间安全传输信息的方式,无需在服务器端存储用户的会话信息,从而实现了无状态认证。1. 理解JWTJWT是一种用于双方之间安全传输信息的简洁的、URL安全的令牌标准。一个JWT实际上是一个紧凑的、URL安全的JSON对象,它传递了信息的一种方式。JWT由三部分组成,它们通过点(.)分隔,分别是:Header(头部):声明了令牌的类型(通常是JWT)以及所使用的签名算法(如HMAC SHA256或RSA)。Payload(负载):包含了声明(claims)。声明是关于实体(通常是用户)和其他数据的声明。声明有三种类型:注册声明、公共声明和私有声明。Signature(签名):是对前两部分的签名,以防止数据被篡改。2. 引入依赖首先,在你的Spring Boot项目中引入Spring Security和JWT相关的依赖。如果你使用Maven,可以添加如下依赖:<!-- Spring Boot Security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- JWT --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency>3. 配置JWT过滤器你需要创建一个JWT过滤器,用于解析JWT,并基于JWT中的信息设置Spring Security的SecurityContext。@Component public class JwtRequestFilter extends OncePerRequestFilter { @Autowired private JwtUtil jwtUtil; @Autowired private UserDetailsService userDetailsService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { final String authorizationHeader = request.getHeader("Authorization"); String username = null; String jwt = null; if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { jwt = authorizationHeader.substring(7); username = jwtUtil.extractUsername(jwt); } if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); if (jwtUtil.validateToken(jwt, userDetails)) { UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); } } chain.doFilter(request, response); } }4. 配置Spring Security在你的Spring Security配置中,你需要配置HTTP安全,添加JWT过滤器,并配置无状态会话。@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private JwtRequestFilter jwtRequestFilter; @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .authorizeRequests() .antMatchers("/api/public/**").permitAll() .anyRequest().authenticated() .and() .exceptionHandling().and().sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); } // 其他配置... }5. 创建JWT工具类你需要一个JWT工具类来生成和验证JWT。@Component public class JwtUtil { // JWT密钥 private String secret = "your_secret_key"; // 生成JWT public String generateToken(String username) { // 逻辑... } // 验证JWT public boolean validateToken(String token, UserDetails userDetails) { // 逻辑... } // 提取JWT中的用户名 public String extractUsername(String token) { // 逻辑... } }6. 认证和授权在你的登录接口中,当用户成功认证后,生成一个JWT并返回给客户端。客户端在后续的请求中携带这个JWT,Spring Security通过JWT过滤器解析JWT,并进行相应的授权检查。以上是使用Spring Security和JWT实现无状态认证的基本步骤。根据你的具体需求,可能还需要进行一些调整和扩展。
-
使用Nacos搭建配置中心是一个涉及多个步骤的过程,主要适用于微服务架构中,以实现配置信息的集中管理和动态更新。以下是详细的步骤说明:一、Nacos安装与启动下载Nacos安装包从Nacos的GitHub或官方网站下载最新版本的Nacos安装包。解压并配置解压下载的安装包到指定目录。如果需要使用外部数据库(如MySQL)来存储配置信息,需要修改Nacos的配置文件(如application.properties),配置数据库连接信息。启动Nacos服务进入Nacos的bin目录,执行启动命令(如startup.cmd -m standalone在Windows上,或在Linux/Mac上使用sh startup.sh -m standalone)。启动后,可以通过浏览器访问Nacos的管理界面(默认地址为http://localhost:8848/nacos),进行进一步配置和管理。二、在Spring Cloud项目中集成Nacos配置中心引入依赖在项目的pom.xml文件中引入Nacos配置中心的依赖。例如,对于Spring Boot 2.x版本,可以添加如下依赖:<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <version>对应的版本号,如2.x.x.RELEASE</version> </dependency>注意版本号要与Spring Boot的版本相匹配。配置bootstrap.yml在src/main/resources目录下创建或修改bootstrap.yml文件,配置Nacos服务器的地址、应用的名称、配置文件的Data ID、Group等信息。例如:spring: application: name: your-application-name cloud: nacos: config: server-addr: localhost:8848 group: DEFAULT_GROUP file-extension: yaml prefix: your-prefix refresh-enabled: truebootstrap.yml文件用于加载外部配置中心的配置,它比application.yml更早加载,确保Nacos中的配置能够覆盖本地配置。在Nacos中添加配置登录Nacos管理界面,进入配置管理页面。点击“+”号新增配置,填写Data ID、Group、配置格式(YAML或Properties)等信息,并在配置内容区域填写具体的配置信息。Data ID的命名规则通常为${prefix}-${spring.profiles.active}.${file-extension},其中prefix默认为spring.application.name的值,spring.profiles.active为当前环境对应的profile(如dev、test、prod),file-extension为配置文件的格式(如yaml)。使用配置在Spring Boot应用中,通过@Value或@ConfigurationProperties注解来注入Nacos中的配置。如果需要实现配置的自动刷新,可以在配置类上添加@RefreshScope注解。三、测试与验证启动Spring Boot应用,检查应用是否能够正确加载Nacos中的配置。修改Nacos中的配置信息,并观察应用是否能够实时更新配置(如果使用了@RefreshScope注解)。通过以上步骤,你可以成功使用Nacos搭建配置中心,并在Spring Cloud项目中集成使用。Nacos配置中心提供了配置管理的集中化、动态化、版本化等功能,有助于提升微服务架构下应用的可维护性和可扩展性。
-
ContextPathCompositeHandler 是 Spring WebFlux 中的一个组件,它允许在同一服务器上将多个应用程序映射到不同的上下文路径(context paths)。这类似于在传统的 Servlet 容器中为每个 Web 应用程序配置不同的 URL 路径。以下是对 ContextPathCompositeHandler 组件的源码实现逻辑和步骤的详细分析:ContextPathCompositeHandler 接口定义ContextPathCompositeHandler 实际上不是一个接口,而是 HandlerMapping 接口的一个实现,它组合了多个 Handler 对象,每个对象都关联一个上下文路径。主要属性contextPaths:存储上下文路径和对应的 Handler 映射。pattern:用于匹配请求路径的正则表达式。上下文路径映射ContextPathCompositeHandler 维护了一个映射,将每个上下文路径映射到一个 Handler:private final Map<String, HttpHandler> contextPaths = new ConcurrentHashMap<>();添加应用程序应用程序可以在初始化时通过 ContextPathCompositeHandler 的 addHandler 方法添加到映射中:public void addHandler(String contextPath, HttpHandler handler) { this.contextPaths.put(contextPath, handler); // 更新正则表达式模式以匹配所有注册的上下文路径 updatePattern(); }处理请求ContextPathCompositeHandler 通过 getHandler 方法来确定请求应该由哪个 Handler 处理:@Override public Mono<Object> getHandler(ServerWebExchange exchange) { String path = extractContextPath(exchange); return Mono.justOrEmpty(contextPaths.get(path)) .map(HandlerAdapter::new) .defaultIfEmpty(Mono.defer(() -> createNotFoundError(exchange))); }extractContextPath:提取请求的上下文路径。getHandler:根据上下文路径从映射中获取对应的 Handler。正则表达式模式ContextPathCompositeHandler 使用正则表达式来匹配请求路径:private void updatePattern() { // 构建匹配所有注册上下文路径的正则表达式 String regex = contextPaths.keySet().stream() .map(this::toRegex) .collect(Collectors.joining("|", "^(", ")$")); this.compiledPattern = Pattern.compile(regex); }错误处理如果没有找到匹配的上下文路径,ContextPathCompositeHandler 会创建一个表示 "Not Found" 的错误处理器:private Mono<HandlerAdapter> createNotFoundError(ServerWebExchange exchange) { return Mono.just(new HandlerAdapter() { @Override public boolean supports(Object handler) { return true; } @Override public Mono<Void> handle(ServerWebExchange exchange, Object handler) { return ServerResponse.notFound().build().writeTo(exchange); } }); }小结一下ContextPathCompositeHandler 组件是 Spring WebFlux 中用于将多个应用程序映射到不同上下文路径的 HandlerMapping 实现。它通过维护一个上下文路径到 HttpHandler 的映射,允许每个应用程序处理其自己的请求路径。通过正则表达式匹配请求路径,并使用 HandlerAdapter 来适配和调用相应的处理器。这种设计模式使得在单个服务器实例中部署和管理多个 WebFlux 应用程序变得简单和高效,每个应用程序都可以有自己的上下文路径,而 ContextPathCompositeHandler 负责将请求路由到正确的应用程序处理器。转载自https://www.cnblogs.com/wgjava/p/18282994
-
HttpHandler 组件在 Spring WebFlux 中是一个用于处理 HTTP 请求的接口,它是响应式编程模型中最低层次的 HTTP 请求处理契约。HttpHandler 作为一个共同的接口,允许不同的运行时环境通过不同的实现来处理 HTTP 请求。以下是对 HttpHandler 组件的源码实现逻辑和步骤的详细分析:HttpHandler 接口定义HttpHandler 接口定义了一个 handle 方法,用于处理传入的 HTTP 请求并返回一个响应:public interface HttpHandler { Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response); }handle(ServerHttpRequest request, ServerHttpResponse response):处理给定的请求并构造响应。核心职责HttpHandler 的核心职责包括:接收请求:接收 ServerHttpRequest 对象,该对象封装了 HTTP 请求的详细信息。构造响应:根据请求信息构造 ServerHttpResponse 对象,设置状态码、响应头等。返回结果:返回一个 Mono<Void> 对象,表示异步的响应处理过程。实现步骤创建 HttpHandler 实例:实现 HttpHandler 接口或使用现有的实现。处理请求:在 handle 方法中编写逻辑以处理请求,例如路由、认证、业务处理等。构造响应:根据请求的处理结果构造响应,设置状态码、响应头和响应体。返回 Mono<Void>:返回一个 Mono<Void>,表示响应已经发送或将被发送。错误处理:在 handle 方法中处理可能发生的异常,确保它们被适当地转换为响应。示例实现以下是一个简单的 HttpHandler 实现示例,它返回一个固定的响应:public class SimpleHttpHandler implements HttpHandler { @Override public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) { String body = "Hello, World!"; response.getHeaders().add("Content-Type", "text/plain"); return response.writeWith(Flux.just(DataBufferUtils.wrap(body))); } }小结一下HttpHandler 组件是 Spring WebFlux 中用于处理 HTTP 请求的基础接口。它提供了一个简单而灵活的方式来处理 HTTP 请求和构造响应。通过实现 HttpHandler 接口,开发者可以控制整个请求处理流程,包括请求解析、业务逻辑处理和响应构建。HttpHandler 的实现可以与其他 Spring WebFlux 组件(如 DispatcherHandler、HandlerMapping、HandlerAdapter 等)结合使用,以构建一个完整的响应式 Web 应用程序。这种低层次的接口为需要高度定制的 Web 应用程序提供了强大的灵活性。转载自https://www.cnblogs.com/wgjava/p/18282994
-
Spring Security Reactive 是 Spring Security 的响应式扩展,它为响应式应用程序提供了安全和认证支持。以下是对 Spring Security Reactive 组件的源码实现逻辑和步骤的详细分析:Spring Security Reactive 核心概念ServerSecurityContextRepository:用于在请求中存储和检索 SecurityContext。ReactiveSecurityContextHolder:管理 SecurityContext 的持有者。ServerSecurityConfigurer:用于配置安全上下文。ServerHttpSecurity:定义了响应式 HTTP 安全策略。ReactiveAuthenticationManager 和 ReactiveUserDetailsService:用于用户认证和用户详情服务。ServerSecurityContextRepository 接口定义public interface ServerSecurityContextRepository { Mono<Void> save(ServerSecurityContext context); Mono<ServerSecurityContext> load(); void invalidate(); }save:保存 ServerSecurityContext。load:加载 ServerSecurityContext。invalidate:使 ServerSecurityContext 无效。ServerHttpSecurity 配置public class ServerHttpSecurity { public ServerHttpSecurity(ReactiveAuthenticationManager authentication) { // ... } public SecurityWebFilterChain build() { // ... } public ServerHttpSecurity authorizeExchange(Consumer<ServerAuthorizeExchangeSpec> configurer) { // ... } // 其他配置方法,例如 cors, csrf, formLogin, httpBasic 等 }authorizeExchange:配置授权策略。build:构建 SecurityWebFilterChain。响应式认证和授权步骤配置认证管理器:创建并配置 ReactiveAuthenticationManager。配置用户服务:创建并配置 ReactiveUserDetailsService。构建 ServerHttpSecurity:使用 ServerHttpSecurity 构建安全策略。配置安全上下文存储:配置 ServerSecurityContextRepository。注册 WebFilter:将 SecurityWebFilterChain 注册到 Web 过滤器链中。处理认证和授权:在请求处理过程中,Spring Security Reactive 拦截请求并处理认证和授权。源码实现逻辑初始化:在应用程序启动时,Spring Security Reactive 初始化安全配置。请求拦截:SecurityWebFilterChain 拦截请求并根据配置的安全策略进行处理。认证:使用 ReactiveAuthenticationManager 进行用户认证。授权:根据 ServerHttpSecurity 配置的授权规则,使用 ReactiveAccessDecisionManager 进行访问控制。安全上下文:使用 ServerSecurityContextRepository 管理每个请求的安全上下文。异常处理:处理安全相关的异常,如认证失败或访问拒绝。响应:根据认证和授权的结果,构建响应并返回给客户端。小结一下Spring Security Reactive 为响应式应用程序提供了全面的安全支持。它基于 Spring Security 的核心概念,并通过响应式编程模型提供了异步、非阻塞的安全处理能力。通过 ServerHttpSecurity 的配置,开发者可以灵活地定义认证和授权策略,以满足不同应用程序的安全需求。Spring Security Reactive 的设计允许它与 Spring WebFlux 无缝集成,为响应式 Web 应用程序提供强大的安全保障。通过使用 Spring Security Reactive,开发者可以构建安全、可靠且易于维护的响应式应用程序。转载自https://www.cnblogs.com/wgjava/p/18282994
-
Spring Data Reactive 是 Spring Data 项目的一部分,它提供了一组用于访问响应式数据存储的抽象。它允许以声明式和响应式的方式进行数据访问和操作,支持如 MongoDB、Redis、R2DBC(Reactive Relational Database Connectivity)等响应式数据库。以下是对 Spring Data Reactive 组件的源码实现逻辑和步骤的详细分析:Spring Data Reactive 核心概念Reactive Repository:扩展了 Reactive Streams 规范,提供了异步的 CRUD 操作。ReactiveCrudRepository:基础接口,提供基本的 CRUD 操作。ReactiveMongoRepository、ReactiveRedisRepository 等:特定数据库的实现。Reactive Repository 接口定义public interface ReactiveCrudRepository<T, ID> extends ReactiveRepository<T, ID> { Mono<T> save(T entity); Flux<T> findAll(); Mono<T> findById(ID id); Mono<Void> deleteById(ID id); // 其他方法... }save(T entity):保存实体。findAll():查找所有记录。findById(ID id):通过 ID 查找记录。deleteById(ID id):通过 ID 删除记录。响应式数据访问步骤定义实体类:创建一个实体类,使用 JPA 注解或数据库特定的注解标记字段。定义仓库接口:创建一个继承自 ReactiveCrudRepository 或特定数据库的 Repository 接口。 public interface MyEntityRepository extends ReactiveCrudRepository<MyEntity, Long> { // 可以添加自定义查询方法 }配置数据源:配置响应式数据源和客户端,例如配置 MongoDB 的 ReactiveMongoDatabase。使用仓库:在服务层注入并使用仓库接口进行数据操作。构建查询:使用仓库接口提供的方法或自定义查询方法构建查询。异步处理:处理查询结果,使用 Mono 或 Flux 的异步特性。源码实现逻辑实体和仓库定义:定义数据实体和仓库接口。Spring 应用上下文:Spring 应用上下文扫描仓库接口并创建代理实现。执行查询:当调用仓库接口的方法时,代理将方法调用转换为数据库操作。结果封装:查询结果封装在 Mono 或 Flux 中返回。错误处理:处理可能发生的异常,将它们转换为合适的响应。响应式流控制:使用 Reactive Streams 规范控制数据流。响应式数据库操作示例@Service public class MyEntityService { private final MyEntityRepository repository; @Autowired public MyEntityService(MyEntityRepository repository) { this.repository = repository; } public Mono<MyEntity> addMyEntity(MyEntity entity) { return repository.save(entity); } public Flux<MyEntity> getAllMyEntities() { return repository.findAll(); } }小结一下Spring Data Reactive 通过提供响应式仓库接口,简化了响应式数据访问的实现。它利用了 Reactive Streams 规范,允许以非阻塞的方式进行数据库操作,提高了应用程序的性能和可伸缩性。开发者可以轻松地定义仓库接口,并使用 Spring 提供的 CRUD 方法或自定义查询方法进行数据操作。Spring Data Reactive 组件的设计允许它与现代响应式编程模型和框架(如 WebFlux)无缝集成,为构建响应式应用程序提供了强大的数据访问能力。通过使用 Spring Data Reactive,开发者可以构建高效、弹性的应用程序,同时保持代码的简洁性和可维护性。转载自https://www.cnblogs.com/wgjava/p/18282994
-
WebClient 是 Spring WebFlux 中用于发起 HTTP 请求的非阻塞响应式客户端。它允许你以声明式的方式构建请求并处理响应。以下是对 WebClient 组件的源码实现逻辑和步骤的详细分析:WebClient 接口定义WebClient 提供了发起请求的方法:public interface WebClient { default URI uri() { return URI.create(this.baseUrl); } <T> Mono<T> getForObject(String url, Class<T> responseType, Object... uriVariables); <T> Flux<T> getForFlux(String url, Class<T> elementType, Object... uriVariables); // 其他 HTTP 方法的重载,例如 postForObject, putForObject 等 }uri():返回基础 URI。getForObject(String url, ...):发起 GET 请求并期望获取对象响应。getForFlux(String url, ...):发起 GET 请求并期望获取元素流响应。WebClient.Builder 构建器WebClient 的实例是通过 WebClient.Builder 构建的:public final class WebClient.Builder { private final String baseUrl; public Builder(String baseUrl) { this.baseUrl = baseUrl; } public WebClient build() { return new ExchangeStrategiesDefaultWebClient(this); } // 其他配置选项,例如设置 ExchangeStrategies, ClientHttpRequestFactory 等 }baseUrl:定义客户端的基础 URL。请求构建和发送创建 WebClient 实例:使用 WebClient.Builder 创建并配置 WebClient 实例。构建请求:使用 WebClient 的方法来添加请求头、查询参数、请求体等。发起请求:调用 HTTP 方法对应的方法(如 getForObject、postForObject)来发起请求。处理响应:响应以 Mono 或 Flux 的形式返回,可以进一步处理。源码实现步骤配置和创建:通过 WebClient.Builder 配置基础 URL 和其他选项,然后创建 WebClient 实例。WebClient webClient = WebClient.builder().baseUrl("http://example.com").build();构建请求:使用 WebClient 的方法链式构建请求。 Mono<Person> personMono = webClient.get() .uri("/person/{id}", id) .retrieve() .bodyToMono(Person.class);发起请求并获取响应:调用 retrieve() 方法并指定响应体转换的方式。响应体转换:使用 bodyToMono 或 bodyToFlux 等方法将响应体转换为指定类型。错误处理:使用 onErrorResume 或 onErrorMap 等操作符处理可能发生的错误。订阅和消费:订阅响应体 Mono 或 Flux 并消费数据。并发和异步处理WebClient 支持并发和异步处理,允许以非阻塞的方式发起多个请求:使用 Flux 可以处理多个响应。可以使用 Scheduler 来控制并发级别。小结一下WebClient 是 Spring WebFlux 中一个强大且灵活的组件,用于构建非阻塞的响应式 HTTP 客户端。它允许以声明式的方式构建请求,并通过 Reactive Streams 规范支持异步数据处理。WebClient 的设计使得它非常适合在响应式应用程序中使用,可以充分利用现代异步编程的优势,提高应用程序的性能和可伸缩性。开发者可以轻松地使用 WebClient 与外部服务进行通信,获取数据,并以响应式的方式处理这些数据。通过 WebClient,Spring WebFlux 应用程序可以无缝地集成到更大的响应式系统中。转载自https://www.cnblogs.com/wgjava/p/18282994
-
Reactor 是一个基于 Reactive Streams 规范的库,用于构建异步、非阻塞的响应式应用程序。它是 Spring WebFlux 的反应式编程基础。以下是对 Reactor 库组件的源码实现逻辑和步骤的详细分析:Reactor 核心组件Reactor 提供了以下核心组件:Flux:代表一个包含 0 到 N 个元素的响应式序列。Mono:代表一个包含 0 到 1 个元素的响应式序列。Scheduler:用于控制并发和执行异步操作的调度器。Flux 和 Mono 的实现逻辑数据流创建:通过静态方法(如 Flux.just(), Mono.just())或构造函数创建 Flux 或 Mono 实例。操作符:Reactor 提供了丰富的操作符来处理数据流,例如 map、flatMap、filter 等。订阅机制:通过 subscribe() 方法订阅数据流,并提供 Subscriber 来接收数据。数据请求:使用 request() 方法控制数据的请求数量。数据推送:数据通过 onNext() 方法推送给订阅者。错误和完成处理:通过 onError() 和 onComplete() 方法处理数据流的错误和完成事件。Scheduler 的实现逻辑调度器创建:创建 Scheduler 实例,例如使用 Schedulers.parallel() 创建并行调度器。任务调度:使用 schedule() 方法调度任务,返回 Mono 或 Flux。并发控制:Scheduler 可以控制任务的并发执行,例如限制并发数量。异步执行:任务在非阻塞的线程池中异步执行。源码实现步骤定义数据源:创建 Flux 或 Mono 实例作为数据源。应用操作符:使用操作符对数据流进行转换、过滤或组合。错误处理:使用 onErrorResume() 或 doOnError() 等操作符处理错误。背压管理:使用 onBackpressureBuffer() 或 onBackpressureDrop() 等操作符处理背压。订阅和消费:调用 subscribe() 方法订阅数据流,并提供 Subscriber 来消费数据。调度任务:使用 Scheduler 调度异步任务。资源清理:使用 dispose() 方法在不再需要时释放资源。小结一下Reactor 库通过 Flux、Mono 和 Scheduler 等组件,提供了一种强大的方式来构建响应式应用程序。它遵循 Reactive Streams 规范,支持异步非阻塞的数据流处理。Reactor 的操作符丰富,可以轻松实现复杂的数据处理逻辑。同时,它还提供了灵活的并发控制和调度机制,以适应不同的应用场景。Reactor 的设计哲学是提供声明式的数据处理能力,让开发者能够以一种直观和灵活的方式构建响应式系统。通过 Reactor,开发者可以充分利用现代硬件的多核特性,提高应用程序的性能和可伸缩性。转载自https://www.cnblogs.com/wgjava/p/18282994
上滑加载中
推荐直播
-
华为AI技术发展与挑战:集成需求分析的实战指南
2024/11/26 周二 18:20-20:20
Alex 华为云学堂技术讲师
本期直播将综合讨论华为AI技术的发展现状,技术挑战,并深入探讨华为AI应用开发过程中的需求分析过程,从理论到实践帮助开发者快速掌握华为AI应用集成需求的框架和方法。
去报名 -
华为云DataArts+DWS助力企业数据治理一站式解决方案及应用实践
2024/11/27 周三 16:30-18:00
Walter.chi 华为云数据治理DTSE技术布道师
想知道数据治理项目中,数据主题域如何合理划分?数据标准及主数据标准如何制定?数仓分层模型如何合理规划?华为云DataArts+DWS助力企业数据治理项目一站式解决方案和应用实践告诉您答案!本期将从数据趋势、数据治理方案、数据治理规划及落地,案例分享四个方面来助力企业数据治理项目合理咨询规划及顺利实施。
去报名
热门标签