资料来源:https://www.docs4dev.com/docs/zh/spring-framework/4.3.21.RELEASE/reference/beans.html
目录
Spring IoC 容器和 beans 简介
容器概述
配置元数据
实例化容器
使用容器
Bean 概述
命名 beans
实例化 beans
依赖关系
依赖注入
详细信息的依赖关系和 configuration
使用 depends-on
Lazy-initialized beans
自动装配
方法注入
Bean 范围
singleton 单例范围(默认值)
prototype 原型范围
Request,session,global session,application 和 WebSocket 范围
Bean 定义继承
基于注解的容器配置
@Required
@Autowired
@Resource
@Bean
@PostConstruct 和 @PreDestroy
Classpath 扫描和托管组件
@Component
@ComponentScan
在组件中定义 bean 元数据
命名自动检测的组件
为自动检测的组件提供范围
使用 JSR 330 标准 Annotations
Java-based container configuration
@Bean 和 @Configuration
使用 AnnotationConfigApplicationContext 实例化 Spring 容器
使用 @Configuration 注释
org.springframework.beans和org.springframework.context包是 Spring Framework 的 IoC 容器的基础。
BeanFactory提供配置框架和基本功能,ApplicationContext添加更多的功能。 ApplicationContext是BeanFactory的高级bean工厂。
在 Spring 中,构成 application 主要由 Spring IoC 容器管理的对象称为 beans。bean 是一个 object,它由 Spring IoC 容器实例化,组装和管理。否则,bean 只是 application 中许多 object 之一。 Beans 及其之间的依赖关系反映在容器使用的 configuration 元数据中(BeanDefinition)。
ApplicationContext表示 Spring IoC 容器,负责实例化,配置和组装上述 beans。容器通过读取 configuration 元数据获取有关 objects 实例化,配置和汇编的指令。 configuration 元数据以 XML,Java annotations 或 Java code 表示。它允许您表达组成您的 application 的 objects 以及这些 objects 之间丰富的相互依赖关系。
Spring提供了ApplicationContext接口的几个实现类,在最早先的程序中中,创建ClassPathXmlApplicationContext或FileSystemXmlApplicationContext 来的实现,虽然 XML 是定义 configuration 元数据的传统格式,但您可以通过提供少量 XML 配置来声明性地启用对这些其他元数据格式的支持,从而指示容器使用 Java 注解方式 或 code(Groovy Bean 定义 DSL) 作为元数据格式。
元数据传统上以简单直观的 XML 格式提供。XML-based configuration 元数据的基本结构:
容器从各种外部资源(如本地文件系统,Java CLASSPATH
等)加载 configuration 元数据。
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
或者,使用一个或多个
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
application.xml
方法:public T getBean(String name, Class
// 容器实例化
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
// 从容器中获取对象
PetStoreService service = context.getBean("petStore", PetStoreService.class);
Spring IoC 容器管理一个或多个 beans。这些 beans 是使用您提供给容器的 configuration 元数据创建的,例如,以 XML
定义的形式。
在容器本身中,这些 bean 定义表示为BeanDefinition
对象,其中包含(以及其他信息)以下元数据:
类的全限定名:通常是 bean 的实际实现类来定义,id通常为接口或超类。
Bean 行为配置元素,如何在容器中运行(范围,生命周期回调等)。
引用 bean 所需要的其他 beans;这些 references 也称为协作者或依赖关系。
要在新创建的对象中设置的其他配置设置,比如,管理连接池的bean 中使用的连接数,或池的大小限制。
此元数据转换为构成每个 bean 定义的一组 properties。
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
//设置beanClassName,用于Class.forName(beanClassName)
void setBeanClassName(@Nullable String var1);
String getBeanClassName();
//生命周期范围,默认singleton
void setScope(@Nullable String var1);
String getScope();
//bean在容器创建时是否惰性初始化,默认为false,容器创建后立即初始化其中的bean对象
void setLazyInit(boolean var1);
boolean isLazyInit();
//略
}
每个 bean 都有一个或多个标识符。这些标识符在承载 bean 的容器中必须是唯一的。 bean 通常只有一个标识符(id),但如果它需要多个标识符,则额外的标识符(name)可以被视为别名。
bean 名称以小写字母开头,命名规范同变量命名。
在@Component扫描中,对于类一般将类名的第一个字母转为小写来命名,如果类名有多个字符且第一个和第二个字符都是大写字母时,命名与类名一致,比如类名为USer,在ApplicationContext中命名为“USer”,而非“uSer"。
自定义bean名称,此时仅有一个标识符
@Component("user")
public class USer
@Bean("beanName)使用在方法上
XML中可定义多组别名
在容器初始化时实例化beans,如果bean的属性boolean isLazyInit();为true时,则延迟在第一次getBean方法时实例化。
在Beandefinition中,有String getBeanClassName()方法,获取beanClassName之后,利用java的反射构建Class对象Class.forName(beanClassName)。
如果在com.example
包中有一个名为Foo
的 class,并且Foo
class 有一个static
嵌套 class(静态内部类),名为Bar,
那么 该bean的class定义为"com.example.Foo$Bar
"
注意在 name 中使用$
字符将嵌套的 class name 与外部 class name 分开。
无参构造
//xml方式
//注解方式
@Component
public class ExampleBean{
}
有参构造
//xml方式
public class ExampleBean {
private String name;
public ExampleBean(String name) {
this.name = name;
}
}
//注解方式
@Component
public class ExampleBean {
private String name;
//@ConstructorProperties("张三")
public ExampleBean(@Value("张三") String name) {
this.name = name;
}
}
定义使用静态工厂方法创建的 bean 时,可以使用class
属性指定包含static
工厂方法的 class 和名为factory-method
的属性
public class ClientService {
private static ClientService clientService= new ClientService();
private ClientService() {}
//静态工厂方法
public static ClientService createInstance() {
return clientService;
}
}
使用普通工厂方法,要先实例化普通工厂类,然后再将class
属性保留为空,并在factory-bean
属性中,指定为工厂类的bean,使用factory-method
属性设置工厂方法。
//普通工厂类,可包含多个工厂方法
public class DefaultServiceFactory {
private static ClientService clientService = new ClientServiceImpl();
private static AccountService accountService = new AccountServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
public AccountService createAccountServiceInstance() {
return accountService;
}
}
//工厂类实例化bean
//使用factory-bean指向工厂类实例化bean,然后指定方法
依赖是类与类之间的依赖,依赖形成类之间的协作关系。
依赖注入(DI)是一个过程,其中bean定义它们的依赖关系,即它们使用的其他bean,仅通过构造函数,工厂方法,或 setter方法 。然后容器在创建 bean 时注入这些依赖项。这个流程基本上是反向的,因此 name 反转控制(IoC), bean 本身通过使用 classes 的直接构造或服务定位器匹配来控制其依赖项的实例化或位置。
依赖关系尽量在接口或抽象类上。
package x.y
public class Foo {
// 注入构造器
//Bar,Baz为接口或者接象类
public Foo(Bar bar, Baz baz) {
// ...
}
}
//注解方式其他依赖bean
@Component
public class ExampleBean {
private USer user;
//构造器注入可省略@Autowired
//@Autowired
public ExampleBean(USer user) {
this.user = user;
}
}
//注解方式注入常量
public class ExampleBean {
@ConstructorProperties({"years", "ultimateAnswer"})
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
//注解方式注入常量2
public class ExampleBean {
public ExampleBean(@Value("2020")int years, @Value("anser")String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
//XML方式
//注解方式
@Component
public class User {
private int uid;
@Value("张三")
private String uname;
private String upwd;
@Autowired
private School school;
//注解方式不用显式申明setter方法
//public void setSchool(School school) {
// this.school = school;
//}
//public void setUname(String uname) {
// this.uname = uname;
//}
}
关于依赖注入是使用构造器还是setter方法注入,spring团队提倡采用构造器注入方式,因为它允许将应用组件实现为不可变对象并确保所需的依赖项不是null
。
通过初始化容器时通过构造器注入了其他bean,如果是setter方法,初始化的bean是无参构造的对象,因此依赖的属性会为null,在第一次getBean时会查找其依赖并注入,如果没有查找到会报错,因此我们采用构造器注入可以在初始化容器时就发现错误,而不是将错误延迟到一段时间或者很久之后使用bean的时候才会发现错误。
这也是为什么springIOC默认Lazyinit参数为false的原因。
如果您主要使用构造函数注入,则无法解析循环依赖关系,Spring IoC 容器会在运行时检测到此循环依赖,并抛出BeanCurrentlyInCreationException
。
尽管不推荐使用setter注入,但您可以使用 setter 注入循环依赖项,当然在后期的getBean方法时会产生异常。
//注解方式
@Value("张三")
private String name;
使用P空间 命名,属性setter注入
使用C空间命名 ,构造器注入
用于将容器中另一个 bean 的 id当然value传递给当前属性,主要在于容器在部署时可以验证该值引用的bean是否存在,解决了value的拼写错误。
//相当于
ref元素
//相当于
//注解方式,byTpye方式,实际找到的是targetBean
@Autowired
private TargetBean TargetBean
或
元素中的
元素定义在内部
内部 bean 定义不需要定义的 id 或 name;定义了也没用:内部 bean 始终是匿名的,它们始终使用外部 bean 创建。不可能将内部 bean 注入其他bean中,也不能独立访问。内部bean共享外部bean的生命周期。
另外还有
,
,、
@Component
public class Student {
private String name;
@Value("1,3,4,5")
private int[] arr;
@Value("a,b,c,d,e")
private List list;
}
集合合并
子类bean中
@Value("")
private String name;
@Nullable
private String name;
foo bean 有一个fred property,它有一个bob property,它有sammy property,最后sammy property 被设置为 value 123。为了使其工作,的fred property 和fred的bob property 在构造 bean 之后不能是null,否则抛出NullPointerException。
在使用此元素的 bean 初始化之前,depends-on属性可以显式强制初始化一个或多个 beans。depends-on无法覆盖依赖的bean的layzy-init=true属性,其lazy-init属性随当前bean。
@Component
@DependsOn({"user"})
public class Student {
@Autowired
private User user;
}
可以通过将 bean 定义标记为 lazy-initialized 来阻止 singleton bean 的初始实例化。 lazy-initialized bean 告诉 IoC 容器在第一次请求时创建 bean 实例,而不是在启动时创建 bean 实例。
//注解方式,Lazy默认为true
@Component
@Lazy
public class Student {}
Spring 容器可以自动适配beans 之间的关系,允许 Spring 自动为您的 bean 解析依赖 beans(其他 beans)。自动装配具有以下优点:
1 自动装配可以显着减少指定 properties 或构造函数 arguments 的需要。
2 随着 objects 的扩展,自动装配可以更新 configuration。
基于XML配置:autowire
autowire:
no | (默认)无自动装配。 Bean references 必须通过ref 元素定义。不建议对较大的部署更改默认设置,因为明确指定协作者可以提供更好的控制和清晰度。在某种程度上,它记录了系统的结构。 |
byName | 按变量名自动装配。一般在XML配置中 |
byType | 按类型名(第一个字母转为小写)自动装配。一般在注解配置中,如果存在多个接口的实现类,则抛出致命的异常。 |
constructor | 类似于 byType,但适用于构造函数 arguments。如果容器中没有构造函数参数类型的一个 bean,则会引发致命错误。 |
在注解@Autowired中,默认是先按照byType查找相关bean,如果其对应的bean不是唯一的,则继续按byName来查找,@Autowired是一个必须找到bean的注解,如果不确认其bean是否存在,可以配置@Autowird(required=false)。
从自动装配中排除 bean:
autowire-candidate属性旨在仅影响byType的自动装配。它不会影响byName的显式 references,即使指定的 bean 未标记为 autowire 候选,它也会得到解析。因此,如果 name 匹配,就会byName自动装配。
优先权:消除歧义
1.@Primary 定义在类上,当 byType发生冲突时,此注解的bean优先注入;
2.@Qualifier("beanName") 标注在需要注入的属性上,其value值为优先的beanName,此优先级大于@Primary。
@Service
public class Us1 implements UserService
@Service
@Primary
public class Us2 implements UserService
public Class TestClass {
@Qualifier("us1")
UserService userService = null;
public TestClass (UserService userService) {
this.userService = userService;
}
}
当在整个项目中一致地使用自动装配时,自动装配效果最佳。
在大多数 application 场景中,容器中的大多数 beans 都是单例的。当单例bean 需要与另一个单例bean 协作,或者非单例bean 需要与另一个非单例bean 协作时,通常通过将一个 bean 定义为另一个 property 来处理依赖关系。当 bean 生命周期不同时会出现问题。
假如在单例A中依赖使用原型B,由于容器中只创建一次A,容器也就不能为原型B每一次获取新的实例。
查找方法注入是容器覆盖容器托管 beans 上的方法的能力,以便 return 容器中另一个名为 bean 的查找结果。查找通常涉及原型 bean。 Spring Framework 通过使用 CGLIB library 中的字节码生成来实现此方法注入,以动态生成覆盖该方法的子类。
public abstract class CommandManager {
public Object process(Object commandState) {
//创建新的实例
Command command = createCommand();
//为实例设置状态
command.setState(commandState);
return command.execute();
}
//创建新的实例,由代理子类实现
protected abstract Command createCommand();
}
//注解方式
public abstract class CommandManager {
public Object process(Object commandState) {
//创建新的实例
Command command = createCommand();
//为实例设置状态
command.setState(commandState);
return command.execute();
}
//创建新的实例,由代理子类实现
@Lookup("myCommand")
protected abstract Command createCommand();
//@Lookup默认按类型查找
//protected abstract MyCommand createCommand();
}
当定义 bean 并将其范围限定为 singleton 时,Spring IoC 容器只创建该 bean 定义的 object 的一个实例。此单例实例存储在此类 singleton beans 的缓存中,并且所有后续请求和 references 都指向 bean return 缓存的 object。
bean 部署的原型范围导致每次创建一个新的 bean 实例,并对该特定 bean 发出请求getBean。Spring 不管理原型 bean 的完整生命周期,交由应用程序来管理(包括创建和销毁)
只有在使用 web-aware Spring ApplicationContext implementation(例如XmlWebApplicationContext)时,request,session,globalSession,application和websocket范围才可用。如果将这些范围与常规的 Spring IoC 容器(例如ClassPathXmlApplicationContext)一起使用,则会抛出IllegalStateException抱怨未知的 bean 范围。
当singleton中使用web范围时,当对范围prototype的 bean 声明
使用abstract属性显式地将 parent bean 定义标记为 abstract。如果 parent 定义未指定 class,则需要将 parent bean 定义明确标记为abstract。
parent bean 无法单独实例化,因为它不完整,并且也明确标记为abstract。当定义是abstract时,它只能用作纯模板 bean 定义,作为 child 定义的 parent 定义。
annotations 在其声明中提供了大量的 context,从而导致更简洁,更简洁的 configuration;XML 擅长在不触及 source code 或重新编译它们的情况下连接组件,无论选择如何,Spring 都可以兼顾两种风格,甚至可以将它们混合在一起。
在XML中启用annotation
适用于 bean property setter 方法,强制填充,属性非null,不过此注解在5.1之后标记为过时方法,我们现在一般采用构造器注入来确保初始化中形成依赖。
适用于属性(字段),构造器,setter方法,构造器注入时可省略@Autowired。
@Autowired默认是byType,如果查找不到再继续byName,如果继续查找不到抛出异常,如果有基于接口的两个实例情况下,按接口类型注入则会抛出异常,此时可使用优先权注解@Primary和@Qualifiers("beanName")来注明优先级。
此注解属于java配置包的,默认基于byName注入
当要把第三方类实例化bean注入容器时,由于第三方类我们不能修改,因此不能使用@Component注解,此注解只作用于类上,因此我们使用@Bean,此注解作用于方法上,且可和@Configuration和@Component协作
//标明此类是一个配置类,或者使用@Component来协作
@Configuration
public class MyConfiguration {
//向IOC中注入第三方类实例bean
@Bean
public StringStore stringStore() {
return new StringStore();
}
@Bean
public IntegerStore integerStore() {
return new IntegerStore();
}
}
public class CachingMovieLister {
@PostConstruct
public void populateMovieCache() {
// 初始回调
}
@PreDestroy
public void clearMovieCache() {
// 销毁回调
}
}
使用 XML 来指定Configuration 元数据,该元数据在 Spring 容器中生成每个BeanDefinition。
@Component是作用于类和注解(元注解),其他几个注解(@Repository,@Service和@Controller,主要是明确此类的职责)中隐式包含了此元注解。
组件扫描器,添加到@Configuration中标注的类上,basesPackages为扫描的包名,默认为扫描该类所在的包。
@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig {
...
}
//基于注解配置的容器构建
ApplicationContext applicationContext =
new AnnotationConfigApplicationContext(AppConfig.class);
XML替代方法
@ComponentScan的过滤器:
excludeFilters可以排除某些组件的扫描,includeFilters指定组件的扫描。
如在@SpringbootApplication注解中的元注解@ComponentScan
//扫描io.example包下,同时排除@Controller注解的类
@ComponentScan(value = "io.example",
excludeFilters = {@Filter(type = FilterType.ANNOTATION,
value = {Controller.class})})
public class BeanConfig {
}
//扫描io.example包下的包含Controller注解的类,同时隐式扫描包含@Component的其他注解类
//可使用useDefaultFilters = false排除,默认其值为true
@ComponentScan(value = "io.example", includeFilters = {@Filter(type = FilterType.ANNOTATION, classes = {Controller.class})})
public class BeanConfig {
}
多扫描器:
//jdk8和之后
@ComponentScan(value = "io.mieux.controller")
@ComponentScan(value = "io.mieux.service")
@Configuration
public class BeanConfig {}
//jdk8之前
@ComponentScans(value =
{@ComponentScan(value = "io.mieux.controller"),
@ComponentScan(value = "io.mieux.service")})
@Configuration
public class BeanConfig {}
@Component
public class FactoryMethodComponent {
@Bean
//@Scope("prototype") 可定义原型注入
@Qualifier("public")
public TestBean publicInstance() {
return new TestBean("publicInstance");
}
public void doWork() {
// Component method implementation omitted
}
}
一般为类名的首字线小写。
@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {}
需要引入第三方jar
javax.inject
javax.inject
1
@Inject等价@Autowired
@Named/@ManagedBean 等价 @Component
@Singleton等价@Scope(“singleton”)
@Qualifier/@Named等价@Qualifier
基于java的容器配置
使用@Configuration注释 class 表示其主要目的是作为 bean 定义的源。
允许通过简单地调用同一 class 中的其他@Bean方法来定义 inter-bean 依赖项。
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
不限于@Configuration注解的类,任何含@Component注解的类和JSR-330注解的类均可以构建容器。
ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);
可以使用 no-arg 构造函数实例化AnnotationConfigApplicationContext,然后使用register()方法或scan()方法进行配置。
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();
}
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.scan("com.example");//包扫描方式
ctx.refresh();//重点方法
MyService myService = ctx.getBean(MyService.class);
}
@Configuration是类级别的annotation,表示的类是 bean 定义的来源(BeanDefintion)。
@Configuration
public class AppConfig {
@Bean
public Foo foo() {
return new Foo(bar());
}
@Bean
public Bar bar() {
return new Bar();
}
}
@Import annotation
将几个@Configuration的配置类合并在一个主配置类中,加载主配置类即可
@Configuration
public class ConfigA {
@Bean
public A a() {
return new A();
}
}
@Configuration
@Import(ConfigA.class)
public class ConfigB {
@Bean
public B b() {
return new B();
}
}
ApplicationContext ctx =
new AnnotationConfigApplicationContext(ConfigB.class)
使用@ImportResource导入XML的配置属性文件,或者使用@PropertyResource直接导入配置文件
@Configuration
@ImportResource("classpath:properties-config.xml")
public class AppConfig {
@Value("${name}")
private String uname;
}
//properties-config.xml配置文件
//application.properties配置文件
name = 李四
@Component("user")
@PropertySource("classpath:application.properties")
public class USer {
private int uid;
@Value("${name}")
private String uname;
}