百度免费校园网站建设,网站怎么企业备案信息,做网站一定要有营业执照吗,自己做网站投放广告目录 IoC容器1. Bean概述1.1. Bean命名1.2. 实例化Bean 2. 依赖关系2.1. 依赖注入2.2. 自动装配 3. Bean的范围4. 定制Bean的特性4.1. 生命周期回调4.2. Lifecycle、SmartLifecycle 以及 LifecycleProcessor4.3. Aware接口 5. 容器拓展6. 容器启动过程7. 基于注解的容器配置7.1… 目录 IoC容器1. Bean概述1.1. Bean命名1.2. 实例化Bean 2. 依赖关系2.1. 依赖注入2.2. 自动装配 3. Bean的范围4. 定制Bean的特性4.1. 生命周期回调4.2. Lifecycle、SmartLifecycle 以及 LifecycleProcessor4.3. Aware接口 5. 容器拓展6. 容器启动过程7. 基于注解的容器配置7.1. Autowired7.2. Primary7.3. Qualifier7.4. 使用泛型作为自动装配限定符7.5. Resource7.6. Value7.7. PostConstruct和PreDestroy 8. 类路径扫描和管理级组件8.1. 使用元注解和组合注解8.2. ComponentScan8.3. 在组件中定义Bean元数据8.4. 命名自动检测到的组件8.5. Scope 9. 使用JSR 330标准注解9.1. Inject和Named9.2. Named和ManagedBean9.3. 对比Spring注解和JSR-330标准注解 10. 基于Java的容器配置10.1. Bean和Configuration10.2. 使用AnnotationConfigApplicationContext实例化Spring容器10.3. 使用AnnotationConfigWebApplicationContext支持Web应用程序 11. Bean 注spring版本为v6.1.5 官方文档链接
IoC容器 简介 IoC 也称为依赖注入 (DI)。对象仅通过构造函数参数、工厂方法的参数或在构造对象实例或从工厂方法返回后在对象实例上设置的属性来定义其依赖项。然后容器在创建 bean 时注入这些依赖项。这个过程从根本上来说是 bean 本身的逆过程因此得名“控制反转”。 理解控制反转Ioc是思想依赖注入DI是实现。 BeanFactory接口提供了能够管理任何类型对象的高级配置机制。
ApplicationContext是 BeanFactory的子接口。BeanFactory提供了配置框架和基本功能而ApplicationContext添加了更多企业特定的功能。
ApplicationContext是一个高级工厂接口能够维护不同 bean 及其依赖项的注册表。
1. Bean概述
Spring IoC 容器管理一个或多个 bean。
在容器本身内这些 bean 定义表示为BeanDefinition 对象其中包含以下元数据以及其他信息
包限定的类名通常是所定义的 bean 的实际实现类Bean 行为配置元素说明 Bean 在容器中的行为方式范围、生命周期回调等。对 Bean 完成其工作所需的其他 Bean 的引用。这些引用也称为协作者或依赖项。在新创建的对象中设置的其他配置设置 — 例如池的大小限制或管理连接池的 bean 中使用的连接数。
ApplicationContext还允许注册在容器外部由用户创建的现有对象。
bean definition
PropertyExplainedClass实例化的BeanNameBean的名称ScopeBean的范围Constructor arguments构造函数参数Properties属性值Autowiring mode自动装配模式Lazy initialization mode懒加载模式Initialization method初始化回调Destruction method销毁回调
1.1. Bean命名
每个 bean 都有一个或多个标识符。这些标识符在托管 bean 的容器中必须是唯一的。一个 bean 通常只有一个标识符但是可以拥有多个别名。 Bean 命名约定bean 名称以小写字母开头并采用驼峰式。例如accountManager、 accountService、userDao、loginController等。 psSpring对于未命名的Bean会默认采用类名并将首字母转为小写。但是在特殊情况下当有多个字符并且第一个和第二个字符均为大写时原始大小写将被保留。 1.2. 实例化Bean
使用构造函数实例化使用静态工厂方法实例化使用实例工厂方法实例化从容器中调用现有 bean 的非静态方法来创建新 bean将工厂bean也注入Ioc容器中管理
2. 依赖关系
典型的企业应用程序不包含单个对象或 Spring 术语中的 bean。即使是最简单的应用程序也有一些对象一起工作来呈现最终用户所认为的连贯的应用程序。
2.1. 依赖注入
概述依赖注入 (DI) 是一个过程对象仅通过构造函数参数、工厂方法的参数或对象实例构造后设置的属性来定义其依赖项。容器在创建 bean 时注入这些依赖项。
依赖注入的两种方式
基于构造函数的依赖注入基于构造函数的 DI 是通过容器调用带有多个参数的构造函数来完成的每个参数代表一个依赖项。基于 Setter 的依赖注入基于 Setter 的 DI 是通过容器在调用无参构造函数或无参static工厂方法来实例化 bean 后调用 bean 上的 setter 方法来完成的。
最好的经验法则是使用构造函数来实现强制依赖项并使用 setter 方法或配置方法来实现可选依赖项。
依赖解析过程
ApplicationContext带着描述bean的配置元素据创建和初始化。配置元数据可以通过 XML、Java 代码或注解来指定。对于每个 bean其依赖项以属性、构造函数参数或静态工厂方法的参数如果您使用它而不是普通构造函数的形式表示。这些依赖关系是在实际创建 bean 时提供给 bean 的。每个属性或构造函数参数都是要设置的值的实际定义或对容器中另一个 bean 的引用。作为值的每个属性或构造函数参数都会从其指定格式转换为该属性或构造函数参数的实际类型。默认情况下Spring 可以将以字符串格式提供的值转换为所有内置类型例如int、 long、String、boolean等。
延迟初始化Bean 默认情况下ApplicationContext 会在初始化过程中创建和配置所有的单例Bean。如果不需要可以通过Bean定义标记为延迟初始化来防止预实例化单例Bean。延迟初始化的 bean 告诉 IoC 容器在第一次请求时而不是在启动时创建一个 bean 实例。
2.2. 自动装配
Spring容器可以自动装配bean协作者之间的关系。 区别于依赖注入自动装配是以一种特殊的依赖注入方式。依赖注入只有构造器注入和setter注入两种方式而自动装配则是通过spring自行注入不需要繁琐的去定义构造函数和setter方法。 自动装配的优点
自动装配可以显着减少指定属性或构造函数参数的需要。自动装配可以随着对象的发展而更新配置。例如如果您需要向类添加依赖项则可以自动满足该依赖项而无需修改配置。
自动装配的局限性和缺点
显式依赖项构造函数设置、直接设置值等始终会覆盖自动装配。自动装配无法装配简单属性例如基本数据元素、Strings、Classes等。自动装配不如显式装配精确。因为自动装配可能会出现同名的要尽量避免让Spring去猜测以防出现歧义从Spring容器生成文档的工具可能无法获得装配信息。容器内可能会有多个Bean与自动装配的方法或构造函数匹配。对于Arrays、Collections或Map来说这不是问题。但是对于单个值的依赖项这种歧义是不能解决的。如果没有可用的唯一 bean 定义则会引发异常。
3. Bean的范围
ScopeDescriptionsingleton默认将单个 bean 定义范围限定为每个 Spring IoC 容器的单个对象实例。prototype将单个 bean 定义的范围限定为任意数量的对象实例。request将单个 bean 定义的范围限定为单个 HTTP 请求的生命周期。也就是说每个 HTTP 请求都有自己的 Bean 实例该实例是根据单个 Bean 定义创建的。仅在支持 Web 的 Spring 上下文中有效ApplicationContext。session将单个 bean 定义的范围限定为 HTTP 的生命周期Session。仅在支持 Web 的 Spring 上下文中有效ApplicationContext。application将单个 bean 定义的范围限定为ServletContext.仅在支持 Web 的 Spring 上下文中有效ApplicationContext。websocket将单个 bean 定义的范围限定为WebSocket.仅在支持 Web 的 Spring 上下文中有效ApplicationContext。
4. 定制Bean的特性
Spring 框架提供了许多可用于自定义 bean 性质的接口。
4.1. 生命周期回调
要与容器对 bean 生命周期的管理进行交互可以通过实现 Spring的InitializingBean和DisposableBean接口。容器要求 afterPropertiesSet()和destroy()让 Bean 在初始化和销毁 Bean 时执行某些操作。
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;public class ExampleBean implements InitializingBean, DisposableBean {Overridepublic void afterPropertiesSet() throws Exception {System.out.println(do something after init...);}Overridepublic void destroy() throws Exception {System.out.println(do something before destroy...);}
}现在通常可以使用PostConstruct和PreDestroy代替。
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;public class ExampleBean{PostConstructpublic void init(){System.out.println(ExampleBean init...);}PreDestroypublic void destroy(){System.out.println(ExampleBean destroy...);}
}4.2. Lifecycle、SmartLifecycle 以及 LifecycleProcessor
Lifecycle接口Lifecycle接口定义了基本的启动start()和停止stop()方法允许Spring容器管理那些具有生命周期的对象比如数据库连接池、监听器、定时任务等。当Spring应用上下文ApplicationContext启动和关闭时它会遍历所有实现了Lifecycle接口的bean并调用对应的方法。通常需要显示的去调用。SmartLifecycle接口martLifecycle 是 Lifecycle 接口的扩展提供了更高级别的生命周期管理功能。除了start()和stop()之外还引入了以下特性 int getPhase()返回一个整数表示启动和停止时的执行顺序。数字越小优先级越高在启动时会先被启动在停止时会后被停止。boolean isAutoStartup()标识该组件是否在Spring容器启动时自动启动。void stop(Runnable callback)提供了一个可选的回调函数在停止时可以异步执行其他操作。 LifecycleProcessor接口SmartLifecycleProcessor 是Spring内部用来管理和协调所有实现了SmartLifecycle接口的bean的处理器。它负责根据getPhase()方法返回的阶段值有序地启动和停止这些bean。默认的实现是DefaultLifecycleProcessor。
4.3. Aware接口
Spring提供了广泛的Aware回调接口让 bean 向容器表明它们需要某种基础设施依赖。
很多时候我们可以使用Spring的依赖注入DI机制如Autowired注解直接注入需要的资源如ApplicationContext、BeanFactory等。这样做可以使代码更加清晰和简洁同时也降低了组件与Spring容器的耦合度。然而在早期版本的Spring框架中尤其是注解驱动编程还未广泛采用之前实现Aware接口是一种常用的方式去获取Spring容器的服务。这是因为
在Spring较早的版本中注解驱动编程尚未成熟实现Aware接口是当时主流的做法。尽管现在推荐使用注解但为了保持向后兼容和满足老项目的需求Spring仍然保留了这些接口。在某些特殊场景下可能无法直接通过Autowired注入例如在非Spring管理的类中需要获取Spring容器的一些上下文信息这时实现Aware接口就成为了一种可行的选择。虽然注解注入方便且直观但在某些复杂的应用场景中实现Aware接口可以提供更多的灵活性允许开发者在Bean初始化阶段做更多的定制化处理。
总之目前推荐尽可能使用注解而非实现Aware接口来注入依赖但如果遇到特殊情况或者有特殊需求时实现Aware接口仍不失为一种有效的手段。同时随着Spring框架的发展许多Aware接口的功能已经被其他的编程模型所取代比如Autowired配合ApplicationContextAware可以由 Autowiredprivate ApplicationContext context;代替。 其他Aware接口
5. 容器拓展
通常应用程序开发人员不需要创建ApplicationContext实现类的子类。Spring IoC 容器可以通过插入特殊集成接口的实现来扩展。
1、BeanPostProcessor针对Bean 实现该接口可以在Spring容器完成实例化、配置和初始化Bean前后如 InitializingBean 的afterPropertiesSet或自定义 init 方法实现一些自定义逻辑。 BeanPostProcessor实例的范围是每个容器的不同容器的BeanPostProcessor实例不会互相影响。
2、BeanFactoryPostProcessor针对Bean的元数据 BeanFactoryPostProcessor对 bean 配置元数据进行操作。也就是说Spring IoC容器允许BeanFactoryPostProcessor读取配置元数据并可能在容器实例化除BeanFactoryPostProcessor实例之外的任何bean之前更改它。BeanFactoryPostProcessor主要是用于修改BeanDefinition。
ps对于BeanFactoryPostProcessor和BeanPostProcessor无论是否启用全局懒加载模式都会在Spring容器初始化早期阶段被积极地实例化和执行这是由其在整个IoC容器生命周期中的作用决定的。因为它们的作用是在Spring容器初始化过程中对其他Bean的定义或实例进行处理的关键组件。
3、自定义FactoryBean实例化逻辑 可以为本身就是工厂的对象实现org.springframework.beans.factory.FactoryBean接口。 FactoryBean接口提供了三种方法
T getObject()返回该工厂创建的对象的实例。该实例可能会被共享具体取决于该工厂是否返回单例或原型。boolean isSingleton()如果FactoryBean返回单例则返回true否则返回false。该方法的默认实现返回true。Class? getObjectType()返回对象的getObject()方法返回的类型或者null不知道类型。
当需要向容器请求实际的FactoryBean实例本身而不是它生成的bean时请在调用ApplicationContext的getBean()方法时在bean的id前面加上符号。 在容器上调用getBean(“myBean”)将返回FactoryBean生产的对象而调用getBean(“myBean”)将返回FactoryBean实例本身。
ApplicationContext的GetBean()方法总结
对于普通的BeangetBean(beanName) 返回的是普通Bean的实例。对于实现了 FactoryBean 的BeangetBean(factoryBeanName) 通常返回的是由该工厂Bean生成的产品Bean实例。若要获得 FactoryBean 类型的Bean自身的实例应该使用 getBean(factoryBeanName)。
6. 容器启动过程
Spring容器的启动过程涉及多个关键步骤确保应用程序能够正确运行。以下是该过程的关键步骤
加载配置文件Spring容器会读取XML配置文件、注解配置或Java配置等将配置信息加载到内存中。创建BeanDefinition解析配置文件将每个Bean的信息封装成BeanDefinition对象包括类名、作用域、依赖关系等信息。实例化Bean根据BeanDefinition中的信息通过反射机制实例化Bean并将其放入Bean容器中。设置Bean属性依赖注入自动装配Bean的属性将Bean所依赖的其他Bean注入到当前Bean中。调用Bean的初始化方法如果Bean实现了InitializingBean接口或在配置文件中定义了init-method方法Spring容器会在实例化Bean后调用其初始化方法。Bean的使用将所有初始化完成的Bean放入Bean容器中供其他Bean或程序使用。
注意区分Bean的实例化和初始化
Bean实例化是指创建Bean对象的过程。这包括选择合适的构造函数来创建对象实例以及处理Bean中的占位符和值注入。Bean初始化是一个更为复杂的过程它涉及到对Bean进行定制化的设置如设置属性的值、执行某些方法等。在Spring中可以通过实现InitializingBean接口、使用PostConstruct注解或在XML配置中定义init-method来指定初始化方法。
BeanFactoryPostProcessor在Spring容器实例化Bean之前执行而BeanPostProcessor在Bean初始化阶段即init方法前后执行。
Spring框架中的BeanFactoryPostProcessor和BeanPostProcessor是两个关键的接口它们在Bean的生命周期中扮演着不同的角色。具体来说
BeanFactoryPostProcessor它主要用于在Spring容器实例化Bean之前对Bean的定义进行修改。这个接口允许用户自定义如何修改Bean的定义信息包括添加、更新或删除Bean定义。这有助于在Bean实例化之前对配置信息进行调整。BeanPostProcessor它用于在Bean对象的初始化过程中进行一些自定义的处理。这个接口包含两个方法postProcessBeforeInitialization和postProcessAfterInitialization分别在Bean的init方法前后被调用。通过实现这个接口可以对Bean进行一些额外的处理如代理包装或者属性值的修改等。
7. 基于注解的容器配置
基于注解的配置提供了XML设置的另一种选择它依赖于字节码元数据而不是XML声明来连接组件。开发人员不使用XML来描述bean连接而是通过在相关的类、方法或字段声明上使用注解将配置移动到组件类本身。
7.1. Autowired
该注解可以实现自动装配的功能
将Autowired注释应用于构造函数
public class MovieRecommender {private final CustomerPreferenceDao customerPreferenceDao;Autowiredpublic MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {this.customerPreferenceDao customerPreferenceDao;}// ...
}将Autowired注释应用于传统的setter 方法
public class SimpleMovieLister {private MovieFinder movieFinder;Autowiredpublic void setMovieFinder(MovieFinder movieFinder) {this.movieFinder movieFinder;}// ...
}Autowired也可以应用于字段甚至可以将其与构造函数混合使用
public class MovieRecommender {private final CustomerPreferenceDao customerPreferenceDao;Autowiredprivate MovieCatalog movieCatalog;Autowiredpublic MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {this.customerPreferenceDao customerPreferenceDao;}// ...
}Autowired也可以应用于数组或集合 数组
public class MovieRecommender {Autowiredprivate MovieCatalog[] movieCatalogs;// ...
}Set
public class MovieRecommender {private SetMovieCatalog movieCatalogs;Autowiredpublic void setMovieCatalogs(SetMovieCatalog movieCatalogs) {this.movieCatalogs movieCatalogs;}// ...
}Map
public class MovieRecommender {private MapString, MovieCatalog movieCatalogs;Autowiredpublic void setMovieCatalogs(MapString, MovieCatalog movieCatalogs) {this.movieCatalogs movieCatalogs;}// ...
}如果希望数组或集合按某一个顺序去注入bean则目标bean可以实现org.springframework.core.Ordered接口或者使用Order或标准的Priority注解。否则它们的顺序遵循容器中相应目标bean定义的注册顺序。 你可以在目标类级别和Bean方法上声明Order注解这适用于单个bean定义(在多个定义使用同一个bean类的情况下)。 Order注解主要用于影响Bean在某些场景下的注入顺序而不直接影响Bean的初始化启动顺序。而Bean的初始化顺序更多地是由Bean间自然的依赖关系及DependsOn注解显式指定的关系所决定的。
如果IoC容器找不到对应的Bean去注入则会报错。若注入的Bean是非必须的可以声明Autowired(required false)就不会报错了。也可以使用Nullable注解或者Java8的Optional类
// 1
public class SimpleMovieLister {Autowired(required false)public void setMovieFinder(MovieFinder movieFinder) {...}
}
// 2
public class SimpleMovieLister {Autowiredpublic void setMovieFinder(Nullable MovieFinder movieFinder) {...}
}
// 3
public class SimpleMovieLister {Autowiredpublic void setMovieFinder(OptionalMovieFinder movieFinder) {...}
}你也可以对那些众所周知的可解析依赖的接口使用Autowired。例如BeanFactory、ApplicationContext、Environment、ResourceLoader、ApplicationEventPublisher和MessageSource以及这些接口和它们的扩展接口如ConfigurableApplicationContext或ResourcePatternResolver。他们是自动解析的不需要特殊的设置。
注Autowired、Inject、Value和Resource注解是由Spring的BeanPostProcessor实现处理的。这意味着您不能在自己的BeanPostProcessor或BeanFactoryPostProcessor类型(如果有的话)中应用这些注释。这些类型必须通过使用XML或Spring Bean方法显式地“连接”起来。
7.2. Primary
如果按类型自动装配可能会出现很多候选者实例使用Spring的Primary可以指定优先使用哪一个实例进行注入。
Configuration
public class MovieConfiguration {BeanPrimarypublic MovieCatalog firstMovieCatalog() { ... }Beanpublic MovieCatalog secondMovieCatalog() { ... }// ...
}public class MovieRecommender {Autowiredprivate MovieCatalog movieCatalog;// ...
}这里会使用firstMovieCatalog作为Bean注入
7.3. Qualifier
Primary当可以确定一个主要候选者时是在多个实例中使用按类型自动装配的有效方法。当需要对选择过程进行更多控制时可以使用 Spring 的Qualifier。一般与Autowired一起使用。Qualifier主要作用是给bean绑定限定符值让bean具有某种特征值。特征值可以不唯一。
public class MovieRecommender {AutowiredQualifier(secondMovieCatalog)private MovieCatalog movieCatalog;// ...
}Configuration
public class MovieConfiguration {Beanpublic MovieCatalog firstMovieCatalog() { ... }Beanpublic MovieCatalog secondMovieCatalog() { ... }// ...
}这里会使用secondMovieCatalog作为Bean注入
可以通过Qualifier对bean进行一个分组注入
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;Configuration
public class AppConfig {Bean(name man)Qualifier(human)public Man man(){return new Man();}Bean(name women)Qualifier(human)public Woman women(){return new Woman();}Bean(name dog)Qualifier(animal)public Dog dog(){return new Dog();}Bean(name cat)Qualifier(animal)public Cat cat(){return new Cat();}}Service
public class TestServiceImpl{AutowiredQualifier(animal)private ListAnimal animals;AutowiredQualifier(human)private ListAnimal humans;...
}cat和dog会被注入animals集合man和women会被注入humans集合
可以创建自己的自定义限定符注解
Target({ElementType.FIELD, ElementType.PARAMETER})
Retention(RetentionPolicy.RUNTIME)
Qualifier
public interface CustomQualifier{
}7.4. 使用泛型作为自动装配限定符
假设前面的 beans 实现了通用接口即Store和 Store
Configuration
public class MyConfiguration {Beanpublic StringStore stringStore() {return new StringStore();}Beanpublic IntegerStore integerStore() {return new IntegerStore();}
}Autowired
private StoreString s1; // String qualifier, injects the stringStore beanAutowired
private StoreInteger s2; // Integer qualifier, injects the integerStore bean我个人理解泛型匹配实际上是一种特殊的按类型匹配
7.5. Resource
当指定了name属性时默认按名称查找并注入Bean。
当未显示的指定注解的name属性时则类似于Autowired注解。它会尝试按以下顺序进行查找
先按类型查找按名称查找如果找不到对于普通Bean此时会抛异常对于BeanFactory、 ApplicationContext、ResourceLoader、ApplicationEventPublisher和MessageSource 这些特殊的beanSpring保证一定能正确找到默认的实现类。
public class Example{// 1Resourceprivate Animal dog;// 2Resource(namecat) private Animal catttt;// 3Resourceprivate Animal dogggg;// 4Resourceprivate ApplicationContext context; // ...
}假设有Dog 和 Cat两个类继承自Animal类且有两个beandog和cat 1、2、4都能成功注入3这种会报错
7.6. Value
Value通常用于注入外部化属性比如application.properties。 形如Value(${animal.name:dog})dog是默认值用:分隔
Component
public class MovieRecommender {private final String catalog;public MovieRecommender(Value(${catalog.name}) String catalog) {this.catalog catalog;}
}Configuration
PropertySource(classpath:application.properties)
public class AppConfig { }application.properties文件配置
catalog.nameMovieCatalog在这种情况下catalog参数和字段将等于该MovieCatalog值。 Spring 提供了默认的宽松嵌入值解析器。它将尝试解析属性值如果无法解析则将属性名称例如 ${catalog.name}作为值注入。如果要对不存在的值保持严格控制则应该声明一个PropertySourcesPlaceholderConfigurerBean如以下示例所示 Configuration
public class AppConfig {Beanpublic static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {return new PropertySourcesPlaceholderConfigurer();}
}使用 JavaConfig配置时PropertySourcesPlaceholderConfigurer Bean方法必须是static。 这里是因为包含Configuration的类将在很早之前实例化并且负责解析诸如ValueAutowired等注解的BeanPostProcessors无法对其执行操作而PropertySourcesPlaceholderConfigurer则实现了BeanPostProcessors。
${} 如果无法解析任何占位符使用上述配置可确保 Spring 初始化失败。也可以使用setPlaceholderPrefix、setPlaceholderSuffix或setValueSeparator等方法来定制占位符。
Spring Boot默认配置了PropertySourcesPlaceholderConfigurer获取属性的Beanapplication.properties和application.yml文件。
Spring BeanPostProcessor在后台使用ConversionService来处理将Value中的String值转换为目标类型的过程。如果你想为你自己的自定义类型提供转换支持你可以提供你自己的converonservice bean实例如下面的例子所示:
Configuration
public class AppConfig {Beanpublic ConversionService conversionService() {DefaultFormattingConversionService conversionService new DefaultFormattingConversionService();conversionService.addConverter(new MyCustomConverter());return conversionService;}
}Value注解同时也支持使用SpEL表达式
Component
public class MovieRecommender {private final String catalog;public MovieRecommender(Value(#{systemProperties[user.catalog] Catalog }) String catalog) {this.catalog catalog;}
}7.7. PostConstruct和PreDestroy
PostConstruct初始化前回调 PreDestroy销毁前回调
public class CachingMovieLister {PostConstructpublic void populateMovieCache() {// populates the movie cache upon initialization...}PreDestroypublic void clearMovieCache() {// clears the movie cache upon destruction...}
}8. 类路径扫描和管理级组件
Component是Spring中一个管理级的组件Spring可以自动检测被其标识的类并注册相应的bean。Spring中除了Component外还有许多其他管理级的注解例如Service、Controller、Repository。可以使用这些注解来管理不同特性的Bean做不同的处理。查看Configuration、Bean、Import和DependsOn注解了解如何使用这些注解管理Bean这里不做过多解释。
8.1. 使用元注解和组合注解
Spring提供的许多注解都可以在自己的代码中用作元注解。元注解是一种可以应用于其他注释的注解。例如前面提到的Service注释是用Component做元注释的。
Target(ElementType.TYPE)
Retention(RetentionPolicy.RUNTIME)
Documented
Component
public interface Service {// ...
}组合注解可以重新声明元注解中的属性允许自定义。例如Spring的SessionScope注释将作用域名称硬编码到session但仍然允许自定义proxyMode。
Target({ElementType.TYPE, ElementType.METHOD})
Retention(RetentionPolicy.RUNTIME)
Documented
Scope(WebApplicationContext.SCOPE_SESSION)
public interface SessionScope {/*** Alias for {link Scope#proxyMode}.* pDefaults to {link ScopedProxyMode#TARGET_CLASS}.*/AliasFor(annotation Scope.class)ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;}在使用SessionScope注解时任可以自定义proxyMode
Service
SessionScope(proxyMode ScopedProxyMode.INTERFACES)
public class SessionScopedUserService implements UserService {// ...
}8.2. ComponentScan
Spring可以使用ComponentScan自动检测原型类并向ApplicationContext注册相应的BeanDefinition实例。 以下是两个支持自动检测的类
Service
public class SimpleMovieLister {private MovieFinder movieFinder;public SimpleMovieLister(MovieFinder movieFinder) {this.movieFinder movieFinder;}
}Repository
public class JpaMovieFinder implements MovieFinder {// implementation elided for clarity
}为了自动检测这些类并注册相应的bean可以将ComponentScan添加到Configuration类中其中的basePackages属性是这两个类的公共父包。
Configuration
ComponentScan(basePackages org.example)
public class AppConfig {// ...
}使用ComponentScan的过滤器属性自定义扫描 默认情况下带有Component、Repository、Service、Controller、Configuration注解的类或者带有Component注解的自定义注解是Spring可以自动检测到的组件。但是我们也可以通过应用自定义过滤器来修改和扩展此行为。将被标识的类添加为ComponentScan注释的includeFilters或excludeFilters属性
FilterType类型
类型例子描述annotation (default)org.example.SomeAnnotation在目标组件的类上存在或元存在的注解。assignable_typeorg.example.SomeClass指定的类aspectjorg.example…*Service目标组件要匹配的AspectJ类型表达式regexorg.example.Default.*与目标组件的类名匹配的正则表达式customorg.example.MyTypeFilterorg.springframework.core.type.TypeFilter接口的自定义实现
Configuration
ComponentScan(basePackages org.example,includeFilters Filter(type FilterType.REGEX, pattern .*Stub.*Repository),excludeFilters Filter(Repository.class))
public class AppConfig {// ...
}8.3. 在组件中定义Bean元数据
Spring组件还可以向容器提供bean定义元数据。 例如通过Qualifier注解标识限定符值
Component
public class FactoryMethodComponent {BeanQualifier(public)public TestBean publicInstance() {return new TestBean(publicInstance);}public void doWork() {// Component method implementation omitted}
}其他可以指定的方法级注解是Scope、Lazy和自定义限定符注解。
Bean方法在普通Component类与Configuration类中的处理方式存在差异
Spring不会对普通的Component类使用CGLIB代理进行增强。CGLIB是一个强大的代码生成库它可以动态生成字节码来实现代理功能。 在Configuration类中通过CGLIB代理当调用Bean方法内的方法或字段时Spring会创建与其协作对象相关的Bean元数据引用。这意味着这些方法并不是按照常规Java语义被直接调用而是通过Spring容器进行调用从而提供Spring Bean的常规生命周期管理以及代理功能。即使在方法内部通过编程方式调用其他的Bean方法来引用其他Bean也会受到容器的管理和代理。 相反在一个普通的Component类中的Bean方法内调用方法或访问字段时其行为遵循标准的Java语义不会有CGLIB处理或其他特别约束。这意味着当在一个普通组件类的Bean方法内部调用其他方法或字段时它们的调用是直接的不会经过Spring容器进行额外的生命周期管理或代理处理。
Configuration
public class AppConfig {Beanpublic Service service() {return new DefaultService();}Beanpublic Client client(Service service) { // 这里通过方法参数注入了service Beanreturn new Client(service);}
}在上述例子中AppConfig是一个Configuration类其中定义了两个Bean方法——service()和client()。当client()方法被调用时并不是简单地创建一个新的Client实例并将new DefaultService()作为参数传递给它。相反Spring IoC容器会拦截这个调用确保service()方法也被正确调用并返回一个代理后的Service实例然后将这个代理实例注入到Client中。这样做的好处是可以确保Service实例也符合Spring的生命周期管理例如如果Service有初始化方法PostConstruct或者依赖其他bean这些都会得到正确的处理。
Component
public class ComponentConfig {Beanpublic Service service() {return new DefaultService();}public Client getClient() { Service service this.service(); // 直接调用了service()方法return new Client(service);}
}在这个例子中ComponentConfig是一个普通的Component类同样包含了一个Bean方法service()和一个非Bean方法getClient()。当getClient()方法被执行时它直接调用了service()方法来获取Service实例。此时service()方法的调用遵循标准的Java语义没有CGLIB代理介入也就是说DefaultService实例会被立即创建并注入到Client中但这个Service实例并没有享受到Spring容器的生命周期管理比如如果有PostConstruct注解的方法则可能不会被自动调用。同时后续对该Service实例的任何修改不会反映到由Spring容器管理的其他地方共享的Bean上。并且每次调用getClient方法都会创建一个新的service。
使用 Bean方法 注意事项
在定义后处理器bean(例如类型为BeanFactoryPostProcessor或BeanPostProcessor)时Bean方法声明为静态方法。因为此类bean在容器生命周期的早期被初始化并且应该避免在那时触发配置的其他部分。对静态Bean方法的调用永远不会被容器截获即静态Bean方法获取的实例不会被IoC容器管理甚至在Configuration类中也不会被截获因为CGLIB子类化只能覆盖非静态方法。Configuration类中的常规Bean方法需要是可重写的——也就是说它们不能被声明为私有或final。也可以不被重写一般是可重写Bean方法不仅可以直接在配置类Configuration类中定义还可以在基类或者接口中定义并且能够被子类或实现类继承和使用从而提供更加灵活的配置方式。单个类可以为同一个bean保存多个Bean方法。即有多个实现类的情况在构造时选择具有最多可满足依赖项的实例。
8.4. 命名自动检测到的组件
如果没有在注解中显示命名Bean名称则默认 Bean 名称生成器将返回非大写的非限定类名称。例如 以下两个Bean名称分别为myMovieLister和movieFinderImpl
Service(myMovieLister)
public class SimpleMovieLister {// ...
}Repository
public class MovieFinderImpl implements MovieFinder {// ...
}如果不想依赖默认的 bean 命名策略可以提供自定义的 bean 命名策略。首先实现 BeanNameGenerator 接口并确保包含默认的无参数构造函数。然后在配置ComponentScan扫描器时提供完全限定的类名
Configuration
ComponentScan(basePackages org.example, nameGenerator MyNameGenerator.class)
public class AppConfig {// ...
}8.5. Scope
与spring管理的组件一样自动检测组件的默认和最常见的作用域是singleton(单例)的。然而有时您需要一个可以通过Scope注释指定的不同作用域。你可以在注释中提供作用域的名称如下例所示:
Scope(prototype)
Repository
public class MovieFinderImpl implements MovieFinder {// ...
}要提供范围解析的自定义策略而不是依赖基于注解的方法可以实现该 ScopeMetadataResolver 接口。然后可以在配置ComponentScan扫描器时提供完全限定的类名
Configuration
ComponentScan(basePackages org.example, scopeResolver MyScopeResolver.class)
public class AppConfig {// ...
}9. 使用JSR 330标准注解
Spring提供了对JSR-330标准注解(依赖注入)的支持。以与Spring注释相同的方式扫描这些注释。要使用它们需要引入依赖
dependencygroupIdjakarta.inject/groupIdartifactIdjakarta.inject-api/artifactIdversion2.0.0/version
/dependency9.1. Inject和Named
你可以用Inject代替Autowired使用Named对注入的依赖项使用名称
public class SimpleMovieLister {private MovieFinder movieFinder;Injectpublic void setMovieFinder(Named(main) MovieFinder movieFinder) {this.movieFinder movieFinder;}// ...
}9.2. Named和ManagedBean
使用Named和ManagedBean等价于使用Component
Named(movieListener) // ManagedBean(movieListener) could be used as well
public class SimpleMovieLister {private MovieFinder movieFinder;Injectpublic void setMovieFinder(MovieFinder movieFinder) {this.movieFinder movieFinder;}// ...
}当使用Named或ManagedBean时也可以以使用ComponentScan去扫描组件。 与Component相反Named和ManagedBean注解都是不可组合的不能用他们来构造自定义的注解。
9.3. 对比Spring注解和JSR-330标准注解
springjakarta.inject.*对比AutowiredInjectInject没有’required’属性。ComponentNamed / ManagedBeanJSR-330不可组合注解Scope(“singleton”)SingletonJSR-330的默认作用域类似于Spring的原型。但是为了使其与Spring的一般默认值保持一致在Spring容器中声明的JSR-330 bean默认情况下是单例的。为了使用单例以外的作用域你应该使用Spring的Scope注释。jakarta.inject还提供了一个jakarta.inject.Scope注释:但是这个注释仅用于创建自定义注释。QualifierQualifier / Namedjakarta.inject.Qualifier只是一个用于构建自定义限定符的元注释。具体的字符串限定符(比如Spring带值的Qualifier)可以通过jakarta.inject.Named来关联。Value--Lazy--ObjectFactoryProviderprovider是Spring的ObjectFactory的直接替代品只是get()方法名更短。它还可以与Spring的Autowired或无注释的构造函数和setter方法结合使用。
10. 基于Java的容器配置
这里主要介绍如何在 Java 代码中使用注解来配置 Spring 容器。
10.1. Bean和Configuration
Spring的Java配置支持中的核心是带Configuration注释的类和带Bean注释的方法。Bean注释用于指示方法实例化、配置和初始化要由Spring IoC容器管理的新对象。被Configuration注解标记的类表明它的主要目的是作为bean定义的来源。 此外Configuration标记的类允许通过调用同一类中的其他Bean方法来定义bean间依赖关系。
Configuration
public class AppConfig {Beanpublic MyServiceImpl myService() {return new MyServiceImpl();}
}Full Configuration vs “lite” Bean mode 当Bean方法在没有使用Configuration注释的类中声明时它们被称为以“精简”模式处理。这里的“精简模式”意味着这类Bean方法所在的类不被视为专门用来定义Bean配置的类而是该类有自己的主要功能比如它可能是某个服务组件的实现类。 在这种情况下Bean方法就像是该类的一个附加功能允许类通过这些方法向Spring IoC容器公开额外的Bean实例。换句话说这些类不仅可以提供其原有的业务逻辑还可以充当Bean工厂通过Bean注解的方法创建和管理其他Bean。这对于服务组件或者其他实体类而言是一种扩展其功能的方式使它们能够参与到Spring容器的依赖注入和管理中从而实现更灵活的组件组装和协同工作。 10.2. 使用AnnotationConfigApplicationContext实例化Spring容器
Configuration
Configuration类主要用于定义Spring应用的配置和Bean的创建逻辑。这些类通常包含一系列Bean方法每个方法对应一个Bean的定义Spring容器会调用这些方法来实例化和初始化Bean。Configuration类可以互相引用通过Import注解导入其他配置类或者通过Bean方法间的相互调用来建立Bean间的依赖关系。Configuration类本身也会被注册为一个Bean可以拥有自己的依赖注入。
Component及其衍生注解如Service、Repository、Controller和JSR-330注解类
这些注解用于标识一个类为Spring容器管理的组件通常这些类代表了应用中的业务逻辑、数据访问层或控制器等。Spring容器会自动扫描含有这些注解的类并将其注册为Bean定义。依赖注入通常通过注解如Autowired、Inject等来完成Spring容器会根据类型、名称或者构造器参数自动注入Bean的依赖。这些类一般不直接定义如何创建其他Bean而是专注于自身的业务逻辑。
总结起来Configuration类更偏向于系统配置和Bean工厂的角色而Component和JSR-330注解类则主要扮演着业务逻辑组件的角色。前者更侧重于整体框架和Bean的组装后者则专注于业务的具体实现。两者共同构成了Spring IoC容器中的Bean生态系统。
AnnotationConfigApplicationContext实例化方式
在实例化一个AnnotationConfigApplicationContext时可以使用Configuration类作为输入。
public static void main(String[] args) {ApplicationContext ctx new AnnotationConfigApplicationContext(AppConfig.class);MyService myService ctx.getBean(MyService.class);myService.doStuff();
}AnnotationConfigApplicationContext并不局限于只使用Configuration类。任何Component或JSR-330注释类都可以作为构造函数的输入
public static void main(String[] args) {ApplicationContext ctx new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);MyService myService ctx.getBean(MyService.class);myService.doStuff();
}使用一个无参数的构造函数实例化一个AnnotationConfigApplicationContext然后使用register()方法配置它。
public static void main(String[] args) {AnnotationConfigApplicationContext ctx new AnnotationConfigApplicationContext();ctx.register(AppConfig.class, OtherConfig.class);ctx.register(AdditionalConfig.class);ctx.refresh();MyService myService ctx.getBean(MyService.class);myService.doStuff();
}使用一个无参数的构造函数并使用组件扫描的方式。AnnotationConfigApplicationContext 的 scan方法
public static void main(String[] args) {AnnotationConfigApplicationContext ctx new AnnotationConfigApplicationContext();ctx.scan(com.acme);ctx.refresh();MyService myService ctx.getBean(MyService.class);
}10.3. 使用AnnotationConfigWebApplicationContext支持Web应用程序
AnnotationConfigWebApplicationContext是AnnotationConfigApplicationContext的扩展专为Web应用设计适用于Servlet环境。它除了包含AnnotationConfigApplicationContext的所有功能外还增加了对Web应用特有的特性的支持比如处理Servlet监听器、过滤器、Spring MVC的DispatcherServlet以及其他与Web容器交互的功能。
11. Bean
Bean是一个方法级注释是XML 元素bean/的直接类比。 声明一个 Bean
Configuration
public class AppConfig {Beanpublic TransferServiceImpl transferService() {return new TransferServiceImpl();}
}前面的配置与以下 Spring XML 完全相同
beansbean idtransferService classcom.acme.TransferServiceImpl/
/beansBean还可以声明接口或基类的类型来作为方法的返回
Configuration
public class AppConfig {Beanpublic TransferService transferService() {return new TransferServiceImpl();}
}推荐返回更具体的类型