定义Spring Bean
对应代码中的 BeanDefinition ,是 Spring Framework 中定义 Bean 的配置元信息接口,主要包含:
- Bean 的类名,全限定名称,具体实现类
- Bean 配置元信息:作用域、自动绑定(Auto Wiring)、生命周期回调……
- Bean 的引用:合作者(Collaborators)或者依赖(Dependencies),
- 配置的设置:Bean 的属性(比如连接池的大小)
BeanDefinition 元信息
概览
属性 | 说明 | 备注 |
---|
Class | Bean 全类名,具体的类,不能是抽象类或接口 | | Name | Bean 的名称或者 ID | | Scope | Bean 的作用域(singleton、prototype、……) | | Constructor arguments | Bean 构造器参数(用于依赖注入) | | Properties | Bean 的属性设置(用于依赖注入) | | Autowiring mode | Bean 自动绑定模式(比如:通过名称 byName……) | | Lazy initialization mode | Bean 延迟初始化(延迟「需要时再初始化,缩短时间」、非延迟「默认」) | | Initialization method | Bean 初始化回调方法名称 | | Destruction method | Bean 销毁回调方法名称 | |
构建
通过 BeanDefinitionBuilder
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
beanDefinitionBuilder.addPropertyValue("name","Steve");
beanDefinitionBuilder.addPropertyValue("id",30);
BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
通过 AbstractBeanDefinition 以及派生类
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(User.class);
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.addPropertyValue("name","Steve");
propertyValues.addPropertyValue("id",31);
genericBeanDefinition.setPropertyValues(propertyValues);
命名Spring Bean
Bean 名称
推荐阅读:spring学习之bean的命名-segmentfault
每个 Bean 都有一个或者多个标识符,标识符要在 Bean 的所在容器唯一。通常情况下,一个 Bean 有一个标识符,如果需要额外的标识符则使用别名扩充。 ?
基于 XML 的情况下,可以用 id 或者 name 属性规定 Bean 的标识符。如果想使用别名,在 name 属性使用「,」或者「;」分开。 ?
Bean 的 id 或者 name 可以留空,留空后,容器会为 Bean 自动生成唯一的名称。推荐使用驼峰,符合 Java 命名规范。 ?
Spring 中可查看BeanNameGenerator 的两个实现。
注解实现
具体看:AnnotationBeanNameGenerator
比如用到的 @component 、@Repository 、@Service 、@Controller 等,其实后三者只是简单包了一层第一个
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
@AliasFor(annotation = Component.class)
String value() default "";
}
默认实现
具体看:DefaultBeanNameGenerator
Spring Bean 的别名
好处
- 可以复用现有的 BeanDefinitio
- 能够通过命名看出来使用场景(比如同一个 Bean 在 A 系统里叫 beanInA,在 B 系统里可以叫 beanInB)
注册 Spring Bean
基础
XML 配置元信息
Java 注解
@Bean 、@Component 、@Import
Java API 配置元信息
- 命名方式:
BeanDefinitionRegistry#registerBeanDefinition(String beanName, BeanDefinition beanDefinition) - 非命名方式:
BeanDifinitionReaderUtils#registerWithGeneratedName(AbstractBeanDefinition definition, BeanDefinitionRegistry registry) - 配置类方式:
AnnotatedBeanDefinitionReader#register(Class?<?>... componentClasses)
代码
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.genericBeanDefinition;
@Import(AnnotationBeanDefinitionDemo.Config.class)
public class AnnotationBeanDefinitionDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(AnnotationBeanDefinitionDemo.class);
registerUserBeanDefinition(applicationContext,"mindartisan-user");
registerUserBeanDefinition(applicationContext);
applicationContext.refresh();
System.out.println("Config 类型的 Bean:" + applicationContext.getBeansOfType(Config.class));
System.out.println("User 类型的 Bean:" + applicationContext.getBeansOfType(User.class));
applicationContext.close();
}
public static void registerUserBeanDefinition(BeanDefinitionRegistry beanDefinitionRegistry, String beanName) {
BeanDefinitionBuilder beanDefinitionBuilder = genericBeanDefinition(User.class);
beanDefinitionBuilder
.addPropertyValue("id", 1L)
.addPropertyValue("name", "Steve");
if (StringUtils.hasText(beanName)) {
beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition());
} else {
BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinitionBuilder.getBeanDefinition(), beanDefinitionRegistry);
}
}
public static void registerUserBeanDefinition(BeanDefinitionRegistry beanDefinitionRegistry) {
registerUserBeanDefinition(beanDefinitionRegistry, null);
}
@Component
public static class Config {
@Bean(name = {"user", "steve-user"})
public User user() {
User user = new User();
user.setId(1L);
user.setName("Steve");
return user;
}
}
}
扩展
通过外部单体对象注册,主要用到的是 SingletonBeanRegistry#registerSingleton(String beanName, Object singletonObject); 方法 SingtonBeanRegistrationDemo.class:
public class SingtonBeanRegistrationDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
UserFactory userFactory = new DefaultUserFactory();
ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
applicationContext.refresh();
beanFactory.registerSingleton("userFactory", userFactory);
UserFactory userFactoryByLookup = beanFactory.getBean("userFactory", UserFactory.class);
System.out.println("userFactory == userFactoryByLookup:" + (userFactory == userFactoryByLookup));
applicationContext.close();
}
}
实例化 Spring Bean
常规
通过构造器(配置元信息:XML、Java 注解、Java API)
通过静态工厂方法(配置元信息:XML、Java API)
通过 Bean 工厂方法(配置元信息:XML、Java API)
通过 FactoryBean 方式(配置元信息:XML、Java 注解、Java API)
代码
具体方式看 xml 中注释,和 main 方法中的命名
User.class:
public class User {
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public static User createUser(){
User user = new User();
user.setId(1L);
user.setName("Steve");
return user;
}
}
bean-instantiation-context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<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="user-by-static-method" class="com.mindartisan.spring.geek.ioc.overview.domain.User"
factory-method="createUser"/>
<bean id="user-by-instance-method" factory-bean="userFactory" factory-method="createUser"/>
<bean id="userFactory" class="com.mindartisan.spring.geek.bean.factory.DefaultUserFactory"/>
<bean id="user-by-factory-bean" class="com.mindartisan.spring.geek.bean.factory.UserFactoryBean"/>
</beans>
BeanInstantinationDemo.class:
public class BeanInstantinationDemo {
public static void main(String[] args) {
BeanFactory beanFactory = new ClassPathXmlApplicationContext("META-INF/bean-instantiation-context.xml");
User userByStaticMethod = beanFactory.getBean("user-by-static-method", User.class);
System.out.println(userByStaticMethod);
User userByInstanceMethod = beanFactory.getBean("user-by-instance-method", User.class);
System.out.println(userByInstanceMethod);
System.out.println(userByStaticMethod == userByInstanceMethod);
User userByFactoryBean = beanFactory.getBean("user-by-factory-bean", User.class);
System.out.println(userByFactoryBean);
System.out.println(userByStaticMethod == userByFactoryBean);
System.out.println(userByFactoryBean == userByInstanceMethod);
}
}
UserFactory.class:
public interface UserFactory {
default User createUser(){
return User.createUser();
}
}
DefaultUserFactory.class:
public class DefaultUserFactory implements UserFactory {
}
UserFactoryBean.class:
public class UserFactoryBean implements FactoryBean {
@Override
public Object getObject() throws Exception {
return User.createUser();
}
@Override
public Class<?> getObjectType() {
return User.class;
}
}
特殊
通过 ServiceLoaderFactoryBean(配置元信息:XML、Java 注解、Java API)
通过 AutowireCapableBeanFactory#createBean(Java.lang.Class,int,boolean)
通过 BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)
参考:注册 Spring Bean -> Java API 配置元信息
代码
SpecialBeanInstantinationDemo.class:
package com.mindartisan.spring.geek.bean.definition;
import com.mindartisan.spring.geek.bean.factory.DefaultUserFactory;
import com.mindartisan.spring.geek.bean.factory.UserFactory;
import com.mindartisan.spring.geek.ioc.overview.domain.User;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Iterator;
import java.util.ServiceLoader;
public class SpecialBeanInstantinationDemo {
public static void main(String[] args) {
BeanFactory beanFactory = new ClassPathXmlApplicationContext("META-INF/special-bean-instantiation-context.xml");
ServiceLoader<UserFactory> userFactoryServiceLoader = beanFactory.getBean("userFactoryServiceLoader", ServiceLoader.class);
displayServiceLoader(userFactoryServiceLoader);
System.out.println("---------------");
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("META-INF/special-bean-instantiation-context.xml");
AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
UserFactory userFactory = autowireCapableBeanFactory.createBean(DefaultUserFactory.class);
User user = userFactory.createUser();
System.out.println(user);
}
public static void demoServiceLoader() {
ServiceLoader<UserFactory> userFactoryServiceLoader = ServiceLoader.load(UserFactory.class,
Thread.currentThread().getContextClassLoader());
displayServiceLoader(userFactoryServiceLoader);
}
public static void displayServiceLoader(ServiceLoader<UserFactory> userFactoryServiceLoader) {
Iterator<UserFactory> userFactoryIterator = userFactoryServiceLoader.iterator();
while (userFactoryIterator.hasNext()) {
UserFactory userFactory = userFactoryIterator.next();
System.out.println(userFactory.createUser());
}
}
}
special-bean-instantiation-context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<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="userFactoryServiceLoader"
class="org.springframework.beans.factory.serviceloader.ServiceLoaderFactoryBean">
<property name="serviceType" value="com.mindartisan.spring.geek.bean.factory.UserFactory"/>
</bean>
</beans>
SecondUserFactory.class:
public class SecondUserFactory implements UserFactory {
}
META-INF/services/com.mindartisan.spring.geek.bean.factory.UserFactory
与 ServiceLoader 有关
com.mindartisan.spring.geek.bean.factory.DefaultUserFactory
com.mindartisan.spring.geek.bean.factory.SecondUserFactory
上述内容指定了com.mindartisan.spring.geek.bean.factory.UserFactory 的实现类
扩展:ServiceLoader
体现了 SPI 机制
JDK 的 ServiceLoader
在 main 方法中,使用了 JDK 自带的ServiceLoader 。上述的_META-INF/services/com.mindartisan.spring.geek.bean.factory.UserFactory_ _之所以在 _META/INF/services 这个路径下是因为 ServiceLoader 中下述代码:
private static final String PREFIX = "META-INF/services/";
Spring 中的 「ServiceLoader」
在 Spring 中,也提供了适配 JDK 中ServiceLoader 的实现:ServiceLoaderFactoryBean.class ,其父类AbstractServiceLoaderBasedFactoryBean.class 有三个子类,如下: 三个子类中对 AbstractServiceLoaderBasedFactoryBean#getObjectToExpose(ServiceLoader<?> serviceLoader) 有三种不同的实现:
- ServiceFactoryBean:返回一个
- ServiceLoaderFactoryBean:
- ServiceListFactoryBean:返回所有
在使用时,还要注意设置 serviceType ,xml 的配置方式如下:
<bean id="userFactoryServiceLoader" class="org.springframework.beans.factory.serviceloader.ServiceLoaderFactoryBean">
<property name="serviceType" value="com.mindartisan.spring.geek.bean.factory.UserFactory"/>
</bean>
初始化 Spring Bean
实现
- @PostConstruct 标注方法
- 实现 InitializingBean 接口的 afterPropertiesSet() 方法
- 自定义初始化方法
- XML 配置:<bean init-method = “init” …/>
- Java 注解:@Bean(initMethod=“init”)
- Java API:AbstractBeanDefinition#setInitMethodName(String)
代码
BeanInitializationDemo.class
@Configuration
public class BeanInitializationDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(BeanInitializationDemo.class);
applicationContext.refresh();
UserFactory userFactory = applicationContext.getBean(UserFactory.class);
applicationContext.close();
}
@Bean(initMethod = "initUserFactory")
public UserFactory userFactory() {
return new DefaultUserFactory();
}
}
DefaultUserFactory.class
public class DefaultUserFactory implements UserFactory, InitializingBean {
@PostConstruct
public void init() {
System.out.println("@PostConstruct:UserFactory 初始化中");
}
public void initUserFactory() {
System.out.println("自定义初始化方法 initUserFactory():UserFactory 初始化中");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet:UserFactory 初始化中");
}
}
顺序
参考:Spring official
- 使用注解
- 继承接口,重写方法
- 手动配置的方法
延迟初始化 Spring Bean
实现
- XML 配置<bean lazy-init = “true” …/>
- Java 注解:@Lazy
代码:
BeanInitializationDemo.class
@Configuration
public class BeanInitializationDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(BeanInitializationDemo.class);
applicationContext.refresh();
System.out.println("Spring 应用上下文已经启动");
UserFactory userFactory = applicationContext.getBean(UserFactory.class);
System.out.println("使用了 @Lazy 注解「当前为 true」:"+userFactory);
applicationContext.close();
}
@Bean(initMethod = "initUserFactory")
@Lazy(value = true)
public UserFactory userFactory() {
return new DefaultUserFactory();
}
}
当 @Lazy(value = true) 时,输出以下内容:
Spring 应用上下文已经启动 @PostConstruct:UserFactory 初始化中 afterPropertiesSet:UserFactory 初始化中 自定义初始化方法 initUserFactory():UserFactory 初始化中 使用了 @Lazy 注解「当前为 true」:com.mindartisan.spring.geek.bean.factory.DefaultUserFactory@197d671
BeanInitializationDemo.class
@Configuration
public class BeanInitializationDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(BeanInitializationDemo.class);
applicationContext.refresh();
System.out.println("Spring 应用上下文已经启动");
UserFactory userFactory = applicationContext.getBean(UserFactory.class);
System.out.println("使用了 @Lazy 注解「当前为 false」:"+userFactory);
applicationContext.close();
}
@Bean(initMethod = "initUserFactory")
@Lazy(value = false)
public UserFactory userFactory() {
return new DefaultUserFactory();
}
}
当 @Lazy(value = fale) 时,输出以下内容:
@PostConstruct:UserFactory 初始化中 afterPropertiesSet:UserFactory 初始化中 自定义初始化方法 initUserFactory():UserFactory 初始化中 Spring 应用上下文已经启动 使用了 @Lazy 注解「当前为 false」:com.mindartisan.spring.geek.bean.factory.DefaultUserFactory@6b4a4e18
延迟初始化和非延迟初始化的主要区别是:延迟初始化在 Spring 应用上下文之后,非延迟在之前。
销毁Spring Bean
方式
@PreDestory 标注方法- 实现
DisposableBean#destory() 方法 - 自定义销毁方法
- XML 配置:
<bean destroy = "xxx" … /> - Java 注解:
@Bean(destroy = "xxx") - Java API:
AbstractBeanDefinition#setDestroyMethodName(String)
代码
BeanInitializationDemo.class
@Configuration
public class BeanInitializationDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(BeanInitializationDemo.class);
applicationContext.refresh();
System.out.println("Spring 应用上下文已经启动");
UserFactory userFactory = applicationContext.getBean(UserFactory.class);
System.out.println("使用了 @Lazy 注解「当前为 false」:"+userFactory);
System.out.println("Spring 应用上下文准备关闭");
applicationContext.close();
System.out.println("Spring 应用上下文已经关闭");
}
@Bean(initMethod = "initUserFactory",destroyMethod = "doDestroy")
@Lazy(value = false)
public UserFactory userFactory() {
return new DefaultUserFactory();
}
}
DefaultUserFactory.class:
public class DefaultUserFactory implements UserFactory, InitializingBean, DisposableBean {
@PostConstruct
public void init() {
System.out.println("@PostConstruct:UserFactory 初始化中");
}
public void initUserFactory() {
System.out.println("自定义初始化方法 initUserFactory():UserFactory 初始化中");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet:UserFactory 初始化中");
}
@PreDestroy
public void preDestroy() {
System.out.println("@PreDestroy:UserFactory 销毁中");
}
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean#destroy():UserFactory 销毁中");
}
public void doDestroy() {
System.out.println("自定义销毁方法 doDestroy():UserFactory 销毁中");
}
}
当 @Lazy(value = fale) 时,输出以下内容:
@PostConstruct:UserFactory 初始化中 afterPropertiesSet:UserFactory 初始化中 自定义初始化方法 initUserFactory():UserFactory 初始化中 Spring 应用上下文已经启动 使用了 @Lazy 注解「当前为 false」:com.mindartisan.spring.geek.bean.factory.DefaultUserFactory@197d671 Spring 应用上下文准备关闭 @PreDestroy:UserFactory 销毁中 DisposableBean#destroy():UserFactory 销毁中 自定义销毁方法 doDestroy():UserFactory 销毁中 Spring 应用上下文已经关闭
顺序
参考:Spring official
- 使用注解
- 继承接口,重写方法
- 手动配置的方法
源码分析
@PreDestory 标注方法
比较简单,直接 IDEA 搜 @PreDestory 的 usage:
- CommonAnnotationBeanPostProcessor.CommonAnnotationBeanPostProcessor();
- InitDestroyAnnotationBeanPostProcessor.setDestroyAnnotationType()
再在当前类下搜destroyAnnotationType ,会发现buildLifecycleMetadata() 方法中使用反射去找标注了注解的方法,有的话,就添加到销毁方法的集合中currDestroyMethods.add(new LifecycleElement(method));
实现 DisposableBean#destory()方法
首先看上文的输出,确定好是在 applicationContext.close() 将 Bean 销毁:
- AbstractApplicationContext.close();
- AbstractApplicationContext.doClose();
- AbstractApplicationContext.destroyBeans();
- ConfigurableBeanFactory.destroySingletons();
- DefaultListableBeanFactory.destroySingletons();
- DefaultSingletonBeanRegistry.destroySingletons();「这里通过遍历循环调用下一步」
- DefaultSingletonBeanRegistry.destroySingleton();
- DefaultSingletonBeanRegistry.destroyBean(String beanName, @Nullable DisposableBean bean);
- DisposableBean.destroy();
垃圾回收 Spring Bean
|