Spring框架的扩展点

Spring框架是一个非常流行的Java应用程序框架,它提供了一系列的扩展点,使得开发者可以自定义和增强框架的功能。这些扩展点包括:

Bean生命周期回调

Spring允许通过实现特定的接口或使用注解来在Bean的生命周期的不同阶段(如初始化或销毁)插入自定义逻辑。主要的接口有InitializingBeanDisposableBean,注解方式主要是@PostConstruct@PreDestroy

在Spring框架中,Bean的生命周期是由多个阶段组成的,从Bean的定义被加载到Spring容器中,到Bean的初始化、使用和最终的销毁。Spring提供了多种方式来允许开发者在这些生命周期阶段中插入自定义逻辑。下面详细解释两种主要的方法:通过实现特定的接口和使用注解。

通过接口实现

  1. InitializingBean接口

    • 作用:当Bean的所有属性都被Spring容器设置完毕后,afterPropertiesSet()方法会被自动调用,这个方法用于Bean的初始化逻辑。
    • 使用场景:适用于Bean需要执行某些操作来完成设置过程,比如检查强制属性是否已经设置,或者需要根据属性值初始化一些内部状态。
  2. DisposableBean接口

    • 作用:在Spring容器销毁Bean之前,destroy()方法会被调用,这里可以放置清理资源的代码,比如关闭文件流或数据库连接等。
    • 使用场景:适用于Bean需要在销毁前释放资源或执行其他清理任务。

通过注解方式

Spring还提供了基于注解的方式来处理Bean的生命周期事件,这通常比实现接口更灵活且便于使用。

  1. @PostConstruct注解

    • 作用:该注解用于修饰一个非静态的void()方法,用于初始化方法。在Bean的属性被设置之后,且InitializingBeanafterPropertiesSet方法之前调用。
    • 使用场景:适用于执行初始化后的配置检查或初始化那些依赖于注入完毕的属性的组件。
  2. @PreDestroy注解

    • 作用:在Spring容器销毁Bean之前调用,用于清理资源。
    • 使用场景:当Bean需要在销毁前进行一些清理工作,如关闭网络连接或释放其他资源时使用。

使用示例

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.DisposableBean;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public class MyBean implements InitializingBean, DisposableBean {
    private String someProperty;

    // 使用接口方式
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Initializing bean");
        // 初始化代码
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("Destroying bean");
        // 清理代码
    }

    // 使用注解方式
    @PostConstruct
    public void init() {
        System.out.println("Post Construct method called");
        // 更多初始化代码
    }

    @PreDestroy
    public void cleanup() {
        System.out.println("Pre Destroy method called");
        // 更多清理代码
    }

    // getter和setter方法
    public void setSomeProperty(String someProperty) {
        this.someProperty = someProperty;
    }

    public String getSomeProperty() {
        return someProperty;
    }
}

通过上述方法,Spring的使用者可以非常灵活地控制Bean的生命周期,确保资源的高效使用和优雅的释放。

Bean后处理器

实现BeanPostProcessor接口可以在Spring容器的Bean创建之后进行额外的处理。这允许开发者在Bean初始化前后添加自定义逻辑。

BeanPostProcessor 是 Spring 框架中的一个强大功能,它允许开发者在 Spring 容器的 Bean 初始化过程中的两个关键点插入自定义逻辑:初始化前初始化后。这个接口定义了两个方法,postProcessBeforeInitializationpostProcessAfterInitialization,开发者可以通过实现这个接口来修改或包装 Spring 管理的 Bean。

实现 BeanPostProcessor 接口

  1. postProcessBeforeInitialization(Object bean, String beanName)

    • 作用:在任何 Bean 初始化回调(如 @PostConstructafterPropertiesSet)之前调用。
    • 返回值:可以返回原始的 Bean,也可以返回一个要使用的替代版本。
    • 使用场景:用于在 Bean 的初始化之前执行一些预处理操作,例如检查 Bean 的某些属性,设置默认值,或者基于特定条件动态地改变 Bean。
  2. postProcessAfterInitialization(Object bean, String beanName)

    • 作用:在 Bean 的初始化后立即调用,即所有初始化回调之后。
    • 返回值:与 postProcessBeforeInitialization 一样,可以返回原始 Bean 或修改后的版本。
    • 使用场景:适用于需要在 Bean 完全初始化后执行的后处理操作。常用于包装 Bean,例如通过动态代理增加额外的行为。

使用示例

下面是一个简单的 BeanPostProcessor 实现示例,该示例展示了如何在初始化前后打印 Bean 的名称。

 
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("Before Initialization : " + beanName);
        return bean;  // 可以返回任何对象,这里返回原始Bean
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("After Initialization : " + beanName);
        return bean;  // 可以对Bean进行增强或修改,并返回
    }
}

注意事项

  • Bean处理的顺序:如果有多个 BeanPostProcessor,它们将按照定义的顺序应用。这一点在设计系统时需要特别注意,以确保 Bean 的处理顺序符合预期。
  • 对 Bean 的影响:实现 BeanPostProcessor 时,要注意不要在 postProcessBeforeInitialization 方法中进行太重的处理,因为这可能会影响整个应用的启动性能。
  • 应用场景BeanPostProcessor 是一个非常强大的工具,可以用于许多高级的 Spring 功能,如安全框架、远程访问框架等。

通过这种方式,BeanPostProcessor 提供了一个非常灵活的机制来干预和修改 Spring 容器管理的 Bean 的生命周期和行为。

Bean定义注册后处理器

通过实现BeanDefinitionRegistryPostProcessor接口,可以在标准初始化之前修改应用程序上下文的bean定义数据。

BeanDefinitionRegistryPostProcessor 是 Spring 框架中的一个高级特性,它继承自 BeanFactoryPostProcessor。这个接口允许开发者在常规的 Bean 初始化之前,对 Bean 的定义(BeanDefinition)进行修改或添加。实现这个接口的类可以直接操作 Bean 定义的注册表,这为动态修改 Bean 定义提供了极大的灵活性。

BeanDefinitionRegistryPostProcessor 的主要方法

  1. postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)

    • 作用:这个方法允许在所有常规 Bean 定义加载之后,但在 Bean 实例化之前,对 Bean 定义进行修改或添加新的定义。
    • 使用场景:用于动态添加更多的 Bean 到 Spring 容器,或者根据某些条件修改已有的 Bean 定义。例如,可以基于某些外部配置动态决定是否注册某个 Bean。
  2. postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)

    • 作用:这是从 BeanFactoryPostProcessor 继承来的方法,允许在所有 Bean 定义加载之后,但在 Bean 被实例化之前,对整个 Bean 工厂进行配置。
    • 使用场景:适用于需要修改 Bean 工厂的配置或整体修改 Bean 定义的情况。

使用示例

以下是一个实现 BeanDefinitionRegistryPostProcessor 接口的示例,演示如何动态注册一个新的 Bean 定义:

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinitionRegistry;
import org.springframework.beans.factory.config.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.stereotype.Component;

@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        // 动态注册新的Bean定义
        RootBeanDefinition beanDefinition = new RootBeanDefinition(MyNewBean.class);
        registry.registerBeanDefinition("myNewBean", beanDefinition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 在这里可以进行Bean工厂的配置修改
        System.out.println("Configuring bean factory");
    }
}

public class MyNewBean {
    // 这是一个示例Bean,可以包含具体的业务逻辑
}

注意事项

  • 执行顺序BeanDefinitionRegistryPostProcessorpostProcessBeanDefinitionRegistry 方法会在 BeanFactoryPostProcessorpostProcessBeanFactory 方法之前执行。这是因为你需要首先注册所有的 Bean 定义,之后才能进行进一步的处理。
  • 影响范围:通过这个接口修改的 Bean 定义将影响整个应用上下文中的所有 Bean。这种改变是全局性的,因此使用时需要小心。

通过使用 BeanDefinitionRegistryPostProcessor,开发者可以非常灵活地控制 Spring 应用上下文中的 Bean 定义,这对于需要动态调整 Spring 配置的复杂应用或基础设施组件特别有用。

属性编辑器

自定义PropertyEditor,这可以在Spring将配置文件中的字符串转换为复杂对象时使用。

在Spring框架中,PropertyEditor 是用于实现属性类型转换的机制。这个机制允许开发者定义如何将字符串转换成复杂的对象或者从一种类型转换成另一种类型。这在处理Spring配置文件(如 XML 或注解配置)中的属性时非常有用,因为它允许开发者自定义如何解析和注入属性值。

使用场景

PropertyEditor 主要用于以下场景:

  • 自定义类型转换:当你需要将配置文件中的字符串转换为自定义的复杂类型时。
  • 数据格式化:比如日期、时间格式的解析和转换。
  • 兼容性处理:允许旧版系统通过特定方式解析配置数据。

实现自定义 PropertyEditor

实现自定义 PropertyEditor 通常涉及以下步骤:

  1. 继承 java.beans.PropertyEditorSupport:这个类提供了 PropertyEditor 接口的默认实现,你可以覆盖必要的方法来实现自定义逻辑。
  2. 重写 setAsText(String text) 方法:这是最关键的方法,用于将输入的字符串转换成相应的对象。
  3. 重写 getAsText() 方法:这个方法应返回一个可以表示内部属性值的字符串,通常用于UI显示或调试。

示例代码

假设你有一个自定义类 Coordinates,它包含纬度和经度信息,你需要从一个格式为 "纬度,经度" 的字符串中解析它:

 
import java.beans.PropertyEditorSupport;

public class CoordinatesEditor extends PropertyEditorSupport {

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        if (text == null || text.isEmpty()) {
            setValue(null);
        } else {
            String[] parts = text.split(",");
            if (parts.length != 2) {
                throw new IllegalArgumentException("Invalid format for Coordinates. Correct format is 'latitude,longitude'.");
            }
            try {
                double latitude = Double.parseDouble(parts[0]);
                double longitude = Double.parseDouble(parts[1]);
                Coordinates value = new Coordinates(latitude, longitude);
                setValue(value);
            } catch (NumberFormatException e) {
                throw new IllegalArgumentException("Coordinates must be valid numbers.", e);
            }
        }
    }

    @Override
    public String getAsText() {
        Coordinates coordinates = (Coordinates) getValue();
        return coordinates != null ? coordinates.getLatitude() + "," + coordinates.getLongitude() : "";
    }
}

在Spring中注册自定义 PropertyEditor

在Spring配置中注册自定义 PropertyEditor 可以通过几种方式实现,最常见的是在配置类中使用 CustomEditorConfigurer

 
import org.springframework.beans.factory.config.CustomEditorConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean
    public CustomEditorConfigurer editorConfigurer() {
        CustomEditorConfigurer configurer = new CustomEditorConfigurer();
        Map<Class<?>, Class<? extends PropertyEditor>> customEditors = new HashMap<>();
        customEditors.put(Coordinates.class, CoordinatesEditor.class);
        configurer.setCustomEditors(customEditors);
        return configurer;
    }
}

通过上述方式,当Spring框架遇到需要将字符串转换为 Coordinates 类型的属性时,会使用你定义的 CoordinatesEditor 来进行转换。这样,你就可以灵活地控制属性的解析和转换过程。

自定义事件

Spring支持使用应用事件来进行组件之间的通信。开发者可以创建和发布自定义事件,这些事件可以被任何实现了ApplicationListener接口的Bean捕获。

Spring框架的事件发布机制是一种基于观察者模式的功能,允许各个组件之间进行松散耦合的通信。通过这种机制,一个组件可以发布事件,而其他组件可以监听并响应这些事件。这对于处理应用程序中的各种状态变化或者重要的业务事件非常有用。

创建自定义事件

要创建一个自定义事件,你需要继承 ApplicationEvent 类。这个类存储事件的状态并提供了获取事件源的方法。

 
import org.springframework.context.ApplicationEvent;

public class CustomEvent extends ApplicationEvent {
    private String message;

    public CustomEvent(Object source, String message) {
        super(source);
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

在上述例子中,CustomEvent 类包含一个字符串消息,它继承了 ApplicationEvent 类并通过构造函数传递事件的源对象和消息内容。

发布事件

要发布事件,你需要在组件中注入 ApplicationEventPublisher 接口,并使用它来发布事件。这个接口由 Spring 容器提供,可以通过自动装配来注入。

 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

@Component
public class EventPublisherBean {
    @Autowired
    private ApplicationEventPublisher publisher;

    public void publish(String message) {
        CustomEvent customEvent = new CustomEvent(this, message);
        publisher.publishEvent(customEvent);
    }
}

在这个例子中,EventPublisherBean 类使用 ApplicationEventPublisher 发布 CustomEvent。任何时候调用 publish 方法都会触发一个新的事件。

监听事件

要监听这些事件,你可以实现 ApplicationListener 接口或者使用 @EventListener 注解。这两种方法都可以根据你的偏好和具体情况来选择使用。

使用 ApplicationListener 接口
 
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class CustomEventListener implements ApplicationListener<CustomEvent> {
    @Override
    public void onApplicationEvent(CustomEvent event) {
        System.out.println("Received custom event - " + event.getMessage());
    }
}

使用 @EventListener 注解

如果你更喜欢使用注解而非实现接口的方式,可以使用 @EventListener,这使得代码更简洁。

 
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class AnnotatedEventListener {

    @EventListener
    public void handleCustomEvent(CustomEvent event) {
        System.out.println("Received custom event - " + event.getMessage());
    }
}

Spring的事件机制提供了一种强大的方式来促进应用组件之间的交流,而无需组件之间有直接的依赖关系。这不仅有助于降低组件间的耦合度,还能够使事件的处理逻辑更加清晰。通过自定义事件、事件发布者和事件监听者,开发者可以灵活地处理应用中的多种业务场景。

自定义注解

Spring允许开发者创建自定义注解,并通过实现BeanPostProcessor或其他机制来处理这些注解,从而添加特定的逻辑。

在Spring框架中,自定义注解是一个非常强大的特性,允许开发者定义新的注解并在应用程序的不同层次和组件上应用它们,从而增强应用程序的功能和灵活性。通过结合 BeanPostProcessor 或其他Spring提供的扩展点,你可以在运行时处理这些注解,执行特定的业务逻辑。

创建自定义注解

自定义注解的创建涉及定义注解的本身,它的保留策略(RetentionPolicy),以及它的作用目标(ElementType)。

 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD) // 可以指定注解用在方法上
@Retention(RetentionPolicy.RUNTIME) // 注解在运行时可用,以便反射机制读取
public @interface MyCustomAnnotation {
    String value() default "default value"; // 为注解定义属性
}

在上述代码中,MyCustomAnnotation 注解被定义为可用于方法,并在运行时可用。value 属性允许在使用注解时指定一个字符串。

使用 BeanPostProcessor 处理注解

BeanPostProcessor 提供了一种方式来在Spring容器的bean生命周期的特定点进行操作。这可以用来检查bean的注解并执行相应的逻辑。

 
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;

@Component
public class MyAnnotationBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Method[] methods = bean.getClass().getDeclaredMethods();
        for (Method method : methods) {
            MyCustomAnnotation annotation = method.getAnnotation(MyCustomAnnotation.class);
            if (annotation != null) {
                // 对有MyCustomAnnotation注解的方法执行某些操作
                System.out.println("Method " + method.getName() + " has annotation with value: " + annotation.value());
            }
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

在这个例子中,MyAnnotationBeanPostProcessor 类实现了 BeanPostProcessor 接口。它在初始化前检查每个bean的方法,看是否有方法被 MyCustomAnnotation 注解标记,并执行一些逻辑。

整合和应用

你可以将这个注解应用到任何Spring管理的bean的方法上:

 
import org.springframework.stereotype.Component;

@Component
public class SomeBean {

    @MyCustomAnnotation(value = "特定值")
    public void someMethod() {
        // 方法实现
    }
}

通过自定义注解和相应的处理器,你可以为Spring应用添加灵活且强大的行为定制,使得你可以在不修改现有代码结构的情况下,注入新的逻辑或行为。这种方法非常适用于那些需要高度可配置且可扩展性强的应用程序。

AOP(面向切面编程)

Spring的AOP功能允许开发者定义方法拦截器(advice)和切点(pointcuts),用于执行横切关注点(如日志、事务管理等)。

在Spring框架中,面向切面编程(AOP)是一种编程范式,它允许开发者将某些功能模块化,并将这些模块(通常是跨多个点的关注点或横切关注点,例如日志记录、事务管理等)应用于程序的其他部分,而不需要修改这些部分的代码。AOP通过定义切点和通知(advice),可以在不触碰主业务逻辑的情况下,增强功能。

核心概念

  1. 切点(Pointcut):定义了何处执行切面的“位置”,即在哪些类或方法上应用切面的规则。
  2. 通知(Advice):定义了切面的“什么”和“何时”执行,即在目标方法执行的哪个阶段(如方法调用前、调用后、方法抛出异常后等)执行何种逻辑。
  3. 连接点(Join Point):在程序执行过程中可以插入切面的点,如方法调用或属性访问。在Spring AOP中,连接点总是表示方法的执行。
  4. 切面(Aspect):通常是一个类,定义了切点和通知的组合。

AOP的通知类型

  • 前置通知(Before advice):在方法执行之前运行的通知。
  • 后置通知(After returning advice):在方法成功执行后运行的通知。
  • 异常通知(After throwing advice):在方法抛出异常退出时执行的通知。
  • 最终通知(After (finally) advice):无论方法通过何种方式结束,都将执行的通知。
  • 环绕通知(Around advice):在方法执行前后都可以执行的通知,能在方法调用前后完成自定义的行为,甚至可以决定是否执行方法。

使用Spring AOP的示例

以下是一个简单的Spring AOP示例,展示了如何实现一个日志切面:

 
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

@Aspect
public class LoggingAspect {

    // 定义切点表达式
    @Pointcut("execution(* com.example.service.*.*(..))")
    private void selectAllMethods() {}

    // 前置通知
    @Before("selectAllMethods()")
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println("Before method:" + joinPoint.getSignature().getName());
    }

    // 环绕通知
    @Around("selectAllMethods()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Before method execution");
        Object returnValue = joinPoint.proceed();
        System.out.println("After method execution");
        return returnValue;
    }
}

在上面的示例中,我们定义了一个切面 LoggingAspect,它包含一个切点和两个通知(一个前置通知和一个环绕通知)。切点表达式指定了切面将应用于 com.example.service 包下所有类的所有方法。

配置

在Spring Boot应用中,你需要启用AOP支持,这通常通过添加 @EnableAspectJAutoProxy 注解来实现:

 
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}

通过使用Spring AOP,开发者可以将应用程序中的关注点分离,使得代码更加模块化,易于管理和维护。这对于日志记录、事务管理、安全性控制等功能尤为重要,因为这些功能通常是横切多个点的。

自定义作用域

虽然Spring默认支持单例(singleton)和原型(prototype)等作用域,但开发者也可以通过实现Scope接口来定义新的作用域。

在Spring框架中,作用域(Scope)定义了Spring容器如何创建和管理Bean的生命周期。Spring默认提供了几种作用域,如单例(singleton)、原型(prototype)、请求(request)、会话(session)和全局会话(global session)。这些默认的作用域覆盖了大多数常见的使用场景。然而,有时候开发者可能需要更具体的控制或者根据特定条件来管理Bean的生命周期,这时就可以通过实现自定义作用域来实现。

实现 Scope 接口

要创建一个自定义的作用域,你需要实现Spring的 Scope 接口。这个接口包含几个方法,每个方法都是管理Bean生命周期的关键部分:

  • Object get(String name, ObjectFactory<?> objectFactory):返回给定名称的当前作用域的对象。如果对象不存在,则使用提供的 ObjectFactory 创建这个对象。
  • Object remove(String name):从当前作用域中移除给定名称的对象,并返回它。如果找不到对象,返回 null
  • void registerDestructionCallback(String name, Runnable callback):注册一个回调,当给定名称的对象从作用域中移除时执行。
  • Object resolveContextualObject(String key):解析在当前作用域中特定的上下文数据。例如,可以用于解析与当前请求相关的数据。
  • String getConversationId():返回当前作用域的对话ID,这个ID可以是任何字符串,标识作用域的当前状态,通常用于区分不同的会话或请求。

示例:实现一个简单的自定义作用域

以下是一个自定义作用域的示例,这个作用域在应用程序的生命周期内只创建一个Bean的实例,但可以在需要时重置这个实例(例如,基于某个特定条件)。

 
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;

public class SimpleCustomScope implements Scope {

    private Object instance;
    private String name = "simpleCustom";

    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        if (instance == null) {
            instance = objectFactory.getObject();
        }
        return instance;
    }

    @Override
    public Object remove(String name) {
        Object oldInstance = instance;
        instance = null;
        return oldInstance;
    }

    @Override
    public void registerDestructionCallback(String name, Runnable callback) {
        // 对于简单实现,可以忽略销毁回调
    }

    @Override
    public Object resolveContextualObject(String key) {
        return null;
    }

    @Override
    public String getConversationId() {
        return name;
    }
}

在Spring配置中注册自定义作用域

创建完自定义作用域后,需要在Spring配置中注册这个作用域,以便Spring容器能够识别和使用它。

 
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Autowired
    ConfigurableBeanFactory beanFactory;

    @PostConstruct
    public void registerCustomScope() {
        beanFactory.registerScope("simpleCustom", new SimpleCustomScope());
    }
}

在这个配置类中,registerCustomScope 方法在Bean工厂中注册了自定义作用域。这样,你就可以在Spring管理的Bean上使用新的作用域:

 
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope("simpleCustom")
public class ScopedBean {
    // Bean的实现
}

通过实现 Scope 接口,开发者可以创建高度定制的作用域,以满足特定的业务需求。这为Spring应用提供了极大的灵活性和控制力,可以根据不同的应用场景动态管理Bean的生命周期。

这些扩展点让Spring非常灵活,可以适用于各种不同的开发需求。通过使用这些扩展点,开发者可以高度定制Spring的行为,以适应特定的项目需求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/593887.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于TL431基准电压源的可调恒压恒流源的Multisim电路仿真设计

1、线性电源的工作原理 在我们日常应用里&#xff0c;直流电是从市电或电网中的交流电获取的。例如15V直流电压源、24V直流电压源等等。交流电变为直流电的过程大概分为一下几步&#xff1a; 首先&#xff0c;交流电通过变压器降低其电压幅值。接着&#xff0c;经过整流电路进…

八、Linux进程检测与控制

章节目标 了解进程和程序的关系了解进程的特点能够使用top动态查看进程信息能够使用ps静态查看进程信息能够使用kill命令给进程发送信号能够调整进程的优先级&#xff08;扩展&#xff09; 引言 在运维的日常工作中&#xff0c;监视系统的运行状况是每天例行的工作&#xff…

Spring IoCDI (1)

目录 一、IoC & DI入门 1、Spring是什么 &#xff08;1&#xff09;什么是容器&#xff1f; &#xff08;2&#xff09;什么是IoC&#xff1f; 二、IoC介绍 1、传统程序开发 2、解决方案 3、IoC程序开发 4、IoC优势 三、DI介绍 通过前面的学习&#xff0c;我们知…

分布式与一致性协议之ZAB协议(二)

ZAB协议 ZAB协议是如何实现操作地顺序性的&#xff1f; 如果用一句话解释ZAB协议到底是什么&#xff0c;我觉得它是能保证操作顺序性的、基于主备模式的原子广播协议。 接下来&#xff0c;还是以指令X、Y为例具体演示一下&#xff0c;帮助你更好地理解为什么ZAB协议能实现操作…

力扣每日一题113:路径总和||

题目 中等 给你二叉树的根节点 root 和一个整数目标和 targetSum &#xff0c;找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。 叶子节点 是指没有子节点的节点。 示例 1&#xff1a; 输入&#xff1a;root [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSu…

【R语言数据分析】函数

目录 自定义函数 apply函数 分类汇总函数aggregate 自定义函数 R语言中的自定义函数更像是在自定义一种运算规则。 自定义函数的语法是 函数名 函数体 } 比如 表示定义了一个名为BMI_function的函数&#xff0c;这个函数代表了一种运算规则&#xff0c;就是把传入的x和…

stm32开发之netxduo网口通讯,网线热插拔处理

前言 在使用netxduo组件时&#xff0c;如果在上电过程中&#xff0c;未插入网线&#xff0c;eth驱动使能过程中未正常初始化本次使用以下几种方式进行设置 问题原因 使用定时器事件回调方式 网络组件中进行调整 /** Copyright (c) 2024-2024&#xff0c;shchl** SPDX-Licen…

openGL

open Graphics Library 核心是一个c库&#xff0c;同时支持多语言的派生。 可编程管线&#xff0c; 状态机&#xff08;State Machine&#xff09;是一种数学模型&#xff0c;用于描述对象在不同状态下的行为及状态之间的转换关系。状态机由一组状态&#xff08;States&#…

2010NOIP普及组真题 2. 接水问题

线上OJ&#xff1a; 一本通&#xff1a;http://ybt.ssoier.cn:8088/problem_show.php?pid1950 解法一、朴素模拟 核心思想&#xff1a; 朴素模拟&#xff1a; 1、先给每个b[i]水龙头分配一个人a[i]&#xff0c;b[i] 表示水龙头的剩余时间。同时标记该水龙头为 used 使用中 2…

深入解析:匹配网络(Matching Networks)的原理和应用

匹配网络&#xff08;Matching Networks&#xff09; 深入解析&#xff1a;匹配网络&#xff08;Matching Networks&#xff09;的原理和应用匹配网络的核心原理工作原理算法流程 匹配网络的实现应用示例结论 深入解析&#xff1a;匹配网络&#xff08;Matching Networks&#…

01_SpringBoot简单搭建入门程序

目录 1、先创建一个java项目2、导入依赖3、将Java项目修改为SpringBoot项目4、编写一个测试的Controller5、测试(创建一个*.http的文件)方式1&#xff1a;方式2&#xff1a;可以直接在浏览器访问该地址方式3&#xff1a;使用postman也可以 1、先创建一个java项目 我的项目结构…

FlinkSql使用ES sink并指定主键,为什么数据还是会被覆盖?

FlinkSql使用ES sink并指定主键&#xff0c;为什么数据还是会被覆盖&#xff1f; 1. 问题描述 根据ES connector文档中的描述&#xff0c;创建ES表并指定主键后将采用upsert模式。 但是在实际的使用过程中却发现部分数据仍然存在被直接覆盖的问题。 举个例子&#xff0c;假如…

NumPy库与PyTorch库的异同点

目录 1.单位的创建和操作 1.创建 2.形状变换 2.数学和统计操作 1.矩阵乘法 2.广播 3.统计计算 3.GPU支持 4.在深度学习中的作用 5.应用范围 NumPy库为数组服务&#xff0c;PyTorch库为张量服务&#xff0c;这是最本质的区别。 1.单位的创建和操作 1.创建 NumPy:使…

【busybox记录】【shell指令】md5sum

目录 内容来源&#xff1a; 【GUN】【md5sum】指令介绍 【busybox】【md5sum】指令介绍 【linux】【md5sum】指令介绍 使用示例&#xff1a; 128位MD5 - 默认输出 128位MD5 - 将每个文件当做二进制处理 128位MD5 - 从文件中读取MD5值并做检查 128位MD5 - 创建一个BSD风…

浅谈OpenCV 粗略计算工件轮廓面积和外接圆直径(Emgu.CV)

前言 最近领导在做库房工具管理这块的功能&#xff0c;希望能集成OpenCV 粗略的计算出工具的长度&#xff0c;以方便用户再归还工具的时候&#xff0c;提示用户该放在那种尺寸的盒子里面&#xff0c;这便是这篇文章的由来。 我们的系统是基于.net开发的&#xff0c;所以采用的是…

项目管理-项目采购管理1/2

项目管理&#xff1a;每天进步一点点~ 活到老&#xff0c;学到老 ヾ(◍∇◍)&#xff89;&#xff9e; 何时学习都不晚&#xff0c;加油 1.项目采购管理-主要内容 项目采购管理过程--重点&#xff1a; ①ITTO 输入&#xff0c;输出工具和技术。 ②问题和解决方案。 ③论文…

【白话机器学习系列】白话特征向量

白话特征向量 一个方阵 A A A 与列向量 v v v 的乘积会生成一个新的列向量。这个新向量通常与原向量有着不同的方向&#xff0c;矩阵在这里代表一个线性变换。然而&#xff0c;某些向量会保持其原始方向。我们称这种向量为矩阵 A A A 的特征向量&#xff08;eigenvector&…

python数据分析——业务指标分析

业务指标分析 前言一、业务指标分析的定义二、业务问题构建问题构建的要求 三、业务问题的识别在识别问题的阶段对于企业内部收益者的补充&#xff1a; 四、竞争者分析标题竞争者分析的内容&#xff1a;标题竞争者分析目的&#xff1a;案例&#xff1a; 黑莓公司为什么会消亡&a…

dynamic_cast 静态转换

dynamic_cast 静态转换 const_cast 常量转换 重新解释转换(reinterpret_cast) 最不安全

RocketMq详解:一、RocketMQ 介绍及基本概念

文章目录 前言1.RocketMQ简介2.RocketMQ 特点3.核心特性4.应用场景5.RocketMQ 优势6.RocketMQ 四大核心组件6.1 NameServer1.NameServer作用2.NameServer被设计为无状态的原因3.和NameServer和Zookeeper的区别4.NameServer的高可用保障 6.2 Broker1.Broker部署方式2.高可用与负…
最新文章