Java 学习笔记(四)
@Scope
@Scope 是 Spring 框架中的一个注解,用于指定一个 bean 的作用域。在 Spring 应用程序中,bean 的作用域决定了它的生命周期和可见性。通过 @Scope 注解,你可以精确地控制 bean 的行为,以确保它在需要时存在,并在不再需要时被正确地清理。
@Scope 注解可以应用于类级别或方法级别(在使用 @Bean 注解的方法上)。它通常与 @Component、@Service、@Repository 或 @Bean 等注解一起使用,以指示 Spring 容器如何管理该 bean 的实例。
@Scope 的参数
@Scope 注解有一个主要的参数 scopeName,它指定了 bean 的作用域。Spring 提供了几种预定义的作用域,同时也允许你通过实现 Scope 接口来定义自定义作用域。
singleton(默认):每个 Spring IoC 容器中仅有一个 bean 实例,该实例被所有对该 bean 的引用共享。
prototype:每次请求 bean 时都会创建一个新的实例。
request:在 web 应用中,bean 定义在请求的范围内。即每个 HTTP 请求都会创建一个新的 bean 实例,且该实例仅在当前请求中有效。
session:在 web 应用中,bean 定义在会话的范围内。即每个 HTTP 会话都会创建一个新的 bean 实例,且该实例在该会话中共享。
application:在 web 应用中,bean 定义在 ServletContext 的范围内。即每个 web 应用仅有一个 bean 实例,该实例被所有用户共享。
websocket:在 web 应用中,bean 定义在 WebSocket 的范围内。这是 Spring 4.0 中引入的,用于支持 WebSocket 协议的 bean 作用域。
使用示例
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("prototype")
public class MyPrototypeBean {
// 类定义
}
// 或者在配置类中定义
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
public class AppConfig {
@Bean
@Scope("request")
public MyRequestBean myRequestBean() {
return new MyRequestBean();
}
}
在上述示例中,MyPrototypeBean 被声明为原型作用域,这意味着每次从 Spring 容器中请求它时,都会获得一个新的实例。而 myRequestBean 方法定义的 bean 被声明为请求作用域,仅在当前 HTTP 请求期间有效。
自定义作用域
如果你需要定义一个自定义的作用域,你需要实现 Scope 接口并注册一个 CustomScopeConfigurer bean 来告知 Spring 关于你的新作用域。这通常涉及更复杂的配置,适用于特定的用例,如集群环境中的会话管理等。
@ConditionalOnProperty(prefix = “spring.aop”, name = “auto”, havingValue = “true”, matchIfMissing = true)
@ConditionalOnProperty 是 Spring Boot 提供的一个条件注解,用于根据应用程序中的配置属性(即 application.properties 或 application.yml 文件中的配置)来决定某个配置类、Bean 或自动配置类是否应该被包含进最终的 Spring 应用上下文中。这对于条件化地激活或禁用自动配置特别有用。
下面是 @ConditionalOnProperty 注解的详细参数解释,包括你提到的那个具体示例:
prefix: 指定配置属性的前缀。在你的例子中,prefix = "spring.aop" 表示我们将关注 spring.aop 开头的配置属性。
name: 指定配置属性的名称。在你的例子中,name = "auto" 表示我们将特别关注 spring.aop.auto 这个配置属性。
havingValue: 指定期望的配置属性值。在你的例子中,havingValue = "true" 表示只有当 spring.aop.auto 的值等于 true 时,
条件才成立。
matchIfMissing: 当指定的配置属性不存在时,是否认为条件成立。在你的例子中,
matchIfMissing = true 表示如果 spring.aop.auto 配置没有指定(即不存在这个配置项),则条件也被视为成立。
这常用于提供一个默认值或行为,如果配置未被明确指定。
综合上述参数,@ConditionalOnProperty(prefix = “spring.aop”, name = “auto”, havingValue = “true”, matchIfMissing = true) 注解的含义是:如果 spring.aop.auto 的配置值为 true,或者该配置项完全不存在(因为 matchIfMissing = true),则条件成立,标注了该注解的类、方法或Bean将被包含进Spring应用上下文中。
这种机制对于创建可选功能特别有用,尤其是当你想让用户通过简单的配置来启用或禁用某个功能时。通过使用 @ConditionalOnProperty,你可以非常灵活地控制哪些配置和Bean被包含在你的应用中,使得你的应用更加模块化和可配置。
SPI 机制
SPI(Service Provider Interface)机制是Java平台的一种服务提供者机制,它允许第三方为Java核心库或框架提供自定义的实现。这种机制通过基于接口的编程、策略模式以及配置文件的方式,实现了动态加载和替换组件的功能,从而增强了系统的可扩展性和灵活性。下面将详细介绍SPI机制在Java和SpringBoot中的使用,并给出示例代码。
SPI机制概述
SPI机制的核心思想在于解耦服务提供者和使用者,通过接口定义服务规范,而具体的实现则由不同的服务提供者提供。Java平台通过java.util.ServiceLoader类来加载和发现这些服务提供者。服务提供者需要在其jar包的META-INF/services目录下创建一个以接口全限定名命名的文件,文件内容为实现该接口的具体类的全限定名。
SPI在Java中的使用
在Java中,SPI机制主要用于扩展Java平台的能力,比如JDBC(Java Database Connectivity)通过SPI机制加载不同数据库的驱动,SLF4J(Simple Logging Facade for Java)通过SPI机制加载不同日志框架的实现等。
示例代码
以下是一个简单的Java SPI使用示例:
定义服务接口
public interface Log {
void log(String message);
}
创建服务实现类
public class ConsoleLogger implements Log {
@Override
public void log(String message) {
System.out.println("Console Logger: " + message);
}
}
public class FileLogger implements Log {
@Override
public void log(String message) {
System.out.println("File Logger: " + message);
}
}
创建配置文件
在src/main/resources/META-INF/services目录下创建名为com.example.Log的文件(假设Log接口的全限定名为com.example.Log),文件内容如下:
com.example.ConsoleLogger
com.example.FileLogger
加载服务实现类
import java.util.ServiceLoader;
public class App {
public static void main(String[] args) {
ServiceLoader<Log> serviceLoader = ServiceLoader.load(Log.class);
for (Log log : serviceLoader) {
log.log("Hello SPI");
}
}
}
这段代码会输出:
Console Logger: Hello SPI
File Logger: Hello SPI
SPI在SpringBoot中的使用
在SpringBoot中,SPI机制同样被广泛应用,主要用于自动注册各种组件,如自动注册插件、自动注册事件监听器、自动注册过滤器等。SpringBoot通过spring.factories文件(位于META-INF目录下)来配置SPI机制,该文件包含了SpringBoot运行时需要读取的类信息。
示例(概念性说明)
虽然SpringBoot中的SPI使用更多是通过spring.factories文件配置的,而不是直接通过ServiceLoader类加载,但背后的思想是一致的:通过接口定义服务,并通过配置文件指定实现类。SpringBoot在启动时会自动扫描spring.factories文件,并加载其中指定的类。
例如,在spring.factories文件中配置一个自定义的ApplicationListener:
org.springframework.context.ApplicationListener=com.example.MyApplicationListener
这样,SpringBoot在启动时就会自动加载并注册com.example.MyApplicationListener作为ApplicationListener的实现。
总结
SPI机制是Java平台提供的一种强大的服务提供者机制,它通过接口定义服务规范,允许第三方提供具体的实现,并通过配置文件和ServiceLoader类实现动态加载和替换。在Java和SpringBoot中,SPI机制都得到了广泛的应用,为系统的扩展性和灵活性提供了有力的支持。