实例化Spring Bean
Bean 实例化(Instantiation)
常规方式
• 通过构造器(配置元信息:XML、Java 注解和Java API )
不演示了, 例子太多
• 通过静态工厂方法(配置元信息:XML 和Java API )
• 通过Bean 工厂方法(配置元信息:XML和Java API )
• 通过FactoryBean(配置元信息:XML、Java 注解和Java API )
特殊方式
• 通过ServiceLoaderFactoryBean(配置元信息:XML、Java 注解和Java API )
• 通过AutowireCapableBeanFactory#createBean(java.lang.Class, int,
boolean)
• 通过BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)
代码示例
spring-core项目
搜索 36 | 实例化Spring Bean
常规方式
BeanInstantiationDemo.java
UserFactory.java
UserFactoryBean.java
bean-instantiation-context.xml
BeanInstantiationDemo.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.geekbang.thinking.in.spring.bean.definition;
import org.geekbang.thinking.in.spring.ioc.overview.domain.User;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 36 | 实例化Spring Bean:Bean实例化的姿势有多少种?
*
* Bean 实例化示例, 这里实例化其实也包括初始化了
*
* • Bean 实例化(Instantiation)
* • 常规方式
* • 通过构造器(配置元信息:XML、Java 注解和Java API )
* • 通过静态工厂方法(配置元信息:XML 和Java API )
* • 通过Bean 工厂方法(配置元信息:XML和Java API ), 就是下面的 实例(Bean)方法实例化 Bean
* • 通过FactoryBean(配置元信息:XML、Java 注解和Java API )
* • 特殊方式
* • 通过ServiceLoaderFactoryBean(配置元信息:XML、Java 注解和Java API )
* • 通过AutowireCapableBeanFactory#createBean(java.lang.Class, int, boolean)
* • 通过BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition) 见 31 | 定义Bean: 什么是BeanDefinition?
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
* @since
*/
public class BeanInstantiationDemo {
public static void main(String[] args) {
// 配置 XML 配置文件, 下面bean的id都去xml文件里面去找
// 启动 Spring 应用上下文
BeanFactory beanFactory
= new ClassPathXmlApplicationContext("classpath:/META-INF/bean-instantiation-context.xml");
// 36.2 通过静态工厂方法实例化 Bean, 这里其实是一个静态工厂的模式, 可以看User类里面那个静态的工厂方法
User userByStaticMethod = beanFactory.getBean("user-by-static-method", User.class);
// 36.3 实例(Bean)方法实例化 Bean, 这里其实是一个代理工厂的模式, 看UserFactory和 DefaultUserFactory, 实现了代理工厂
// UserFactory需要在配置文件中配置
User userByInstanceMethod = beanFactory.getBean("user-by-instance-method", User.class);
// 36.4 通过FactoryBean实例化 Bean
User userByFactoryBean = beanFactory.getBean("user-by-factory-bean", User.class);
System.out.println(userByStaticMethod);
System.out.println(userByInstanceMethod);
System.out.println(userByFactoryBean);
// 相等性上应该是都不对等的, 都不是一种创建的方式
System.out.println(userByStaticMethod == userByInstanceMethod);
System.out.println(userByStaticMethod == userByFactoryBean);
}
}
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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 36 | 实例化Spring Bean:Bean实例化的姿势有多少种? -->
<!-- 36.2 静态方法实例化 Bean -->
<!-- 这里的意思就是通过一个静态方法来创建这个bean, 而这个方法定义在这个User类里面 -->
<bean id="user-by-static-method" class="org.geekbang.thinking.in.spring.ioc.overview.domain.User"
factory-method="createUser"/>
<!-- 36.3 实例(Bean)方法实例化 Bean / 通过Bean 工厂方法 -->
<!-- 这里其实是定义了一个UserFactory代理工厂类(有默认实现的接口), 然后DefaultUserFactory是实现类,
createUser则是实现类的一个实例方法, 故而是一个Bean工厂方法实现的实例化Bean -->
<bean id="user-by-instance-method" factory-bean="userFactory" factory-method="createUser"/>
<bean id="userFactory" class="org.geekbang.thinking.in.spring.bean.factory.DefaultUserFactory"/>
<!-- 36.4 FactoryBean实例化 Bean -->
<!-- 这个方式比较特殊在于并不会去定义这个bean, 而是一个factoryBean,
那么这里不仅能实例化, 其实还能进行初始化, 看实现了哪些方法了 -->
<bean id="user-by-factory-bean" class="org.geekbang.thinking.in.spring.bean.factory.UserFactoryBean"/>
</beans>
User.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.geekbang.thinking.in.spring.ioc.overview.domain;
import org.geekbang.thinking.in.spring.ioc.overview.enums.City;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.core.io.Resource;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
/**
* 用户类
* <p>
* spring-core-2-17 | 传统IoC容器实现:JavaBeans也是IoC容器吗?
* 这个类也用于JavaBeans作为IoC容器功能的演示
* <p>
* 常见叫法: Setter方法 / Getter 方法
* Java Beans叫法: 可写方法(writable) / 可读方法(Readable)
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
* @since
*/
public class User implements BeanNameAware {
// 61 | 基础类型注入 有没有思考过配置文件里的属性值是字符串, 怎么转换成这的Long?
private Long id;// 属性也称为Property
private String name;
// 61 | 基础类型注入, 枚举也可以注入
private City city;
// 62 | 集合类型注入, 数组类型注入
private City[] workCities;
// 62 | 集合类型注入, List类型注入
private List<City> lifeCities;
// 61 | 基础类型注入, spring的类型也可以注入
// 其实在调用User 的 toString() 方法时, 对configFileLocation 的展示也是调了一个 toString(),
// 可以看 org.springframework.core.io.ClassPathResource 的 父接口
// org.springframework.core.io.AbstractResource, 有 toString() 方法,
// 可以看到是使用 getDescription() 实现的, 那么再转回去:
// org.springframework.core.io.ClassPathResource.getDescription, 就是这么来的
private Resource configFileLocation;
private Company company;
private Properties context;
private String contextAsText;
/**
* 当前 Bean 的名称
*/
private transient String beanName;
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;
}
public City getCity() {
return city;
}
public void setCity(City city) {
this.city = city;
}
public Resource getConfigFileLocation() {
return configFileLocation;
}
public void setConfigFileLocation(Resource configFileLocation) {
this.configFileLocation = configFileLocation;
}
public City[] getWorkCities() {
return workCities;
}
public void setWorkCities(City[] workCities) {
this.workCities = workCities;
}
public List<City> getLifeCities() {
return lifeCities;
}
public void setLifeCities(List<City> lifeCities) {
this.lifeCities = lifeCities;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
/**
* 36.2 静态的工厂方法, 用于bean的实例化
* @return
*/
public static User createUser() {
User user = new User();
user.setId(1L);
user.setName("小马哥");
return user;
}
@PostConstruct
public void init() {
System.out.println("User Bean [" + beanName + "] 初始化...");
}
@PreDestroy
public void destroy() {
System.out.println("User Bean [" + beanName + "] 销毁中...");
}
@Override
public void setBeanName(String name) {
this.beanName = name;
}
public Properties getContext() {
return context;
}
public void setContext(Properties context) {
this.context = context;
}
public String getContextAsText() {
return contextAsText;
}
public void setContextAsText(String contextAsText) {
this.contextAsText = contextAsText;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", city=" + city +
", workCities=" + Arrays.toString(workCities) +
", lifeCities=" + lifeCities +
", configFileLocation=" + configFileLocation +
", company=" + company +
", context=" + context +
", contextAsText='" + contextAsText + '\'' +
", beanName='" + beanName + '\'' +
'}';
}
}
DefaultUserFactory.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.geekbang.thinking.in.spring.bean.factory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
/**
* 36 | 实例化Spring Bean:Bean实例化的姿势有多少种?
* 36.3 默认 {@link UserFactory} 实现
*
* 37 | 初始化Spring Bean:Bean初始化有哪些方式?
*
* 39 | 销毁Spring Bean
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
* @since
*/
public class DefaultUserFactory implements UserFactory, InitializingBean, DisposableBean {
// 37.1. 基于 @PostConstruct 注解
@PostConstruct
public void init() {
System.out.println("@PostConstruct : UserFactory 初始化中...");
}
public void initUserFactory() {
System.out.println("自定义初始化方法 initUserFactory() : UserFactory 初始化中...");
}
/**
* 37.2 实现InitializingBean 接口的afterPropertiesSet() 方法
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean#afterPropertiesSet() : UserFactory 初始化中...");
}
/**
* 39.1 @PreDestroy 标注方法
*/
@PreDestroy
public void preDestroy() {
System.out.println("@PreDestroy : UserFactory 销毁中...");
}
/**
* 39.2 实现DisposableBean 接口的destroy() 方法
* @throws Exception
*/
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean#destroy() : UserFactory 销毁中...");
}
public void doDestroy() {
System.out.println("自定义销毁方法 doDestroy() : UserFactory 销毁中...");
}
/**
* 40 Spring Bean 覆盖的finalize() 方法
* 垃圾回收时 ,finalize()会被java回调
* @throws Throwable
*/
@Override
public void finalize() throws Throwable {
System.out.println("当前 DefaultUserFactory 对象正在被垃圾回收...");
}
}
UserFactoryBean.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.geekbang.thinking.in.spring.bean.factory;
import org.geekbang.thinking.in.spring.ioc.overview.domain.User;
import org.springframework.beans.factory.FactoryBean;
/**
* 36 | 实例化Spring Bean:Bean实例化的姿势有多少种?
* 36.4 {@link User} Bean 的 {@link org.springframework.beans.factory.FactoryBean} 实现
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
* @since
*/
public class UserFactoryBean implements FactoryBean {
/**
* 相当于我的实例方法, 静态方法以及 FactoryBean的实现均采用User类的静态方法createUser()来实现
* @return
* @throws Exception
*/
@Override
public Object getObject() throws Exception {
return User.createUser();
}
@Override
public Class<?> getObjectType() {
return User.class;
}
}
特殊方式
SpecialBeanInstantiationDemo.java
special-bean-instantiation-context.xml
META-INF\services\org.geekbang.thinking.in.spring.bean.factory.UserFactory
SpecialBeanInstantiationDemo.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.geekbang.thinking.in.spring.bean.definition;
import org.geekbang.thinking.in.spring.bean.factory.DefaultUserFactory;
import org.geekbang.thinking.in.spring.bean.factory.UserFactory;
import org.geekbang.thinking.in.spring.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;
import static java.util.ServiceLoader.load;
/**
* 36 | 实例化Spring Bean:Bean实例化的姿势有多少种?
*
* 特殊的 Bean 实例化示例
* • 通过ServiceLoaderFactoryBean(配置元信息:XML、Java 注解和Java API )
* • 通过AutowireCapableBeanFactory#createBean(java.lang.Class, int, boolean)
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
* @since
*/
public class SpecialBeanInstantiationDemo {
public static void main(String[] args) {
// 配置 XML 配置文件
// 启动 Spring 应用上下文
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/special-bean-instantiation-context.xml");
// 36.6 通过 ApplicationContext 获取 AutowireCapableBeanFactory
// 这里有个意思的就是 beanFactory 就不能获取到 AutowireCapableBeanFactory, ApplicationContext是beanFactory的超类
AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();
// 创建 UserFactory 对象,通过 AutowireCapableBeanFactory
// 这里我们在创建一个类的实体的时候, 一定要用实现类, 但是不要用接口, 会报错
UserFactory userFactory = beanFactory.createBean(DefaultUserFactory.class);
System.out.println(userFactory.createUser());
// 36.5.2 serviceLoader的获取方式二, 通过spring的ServiceLoaderFactoryBean来获取 ServiceLoader, 这里有spring的参与了
// 这里是通过ServiceLoaderFactoryBean 来创建一个 ServiceLoader , 并且这个 ServiceLoader 只关注 UserFactory,
// 如果有多个对象, 与方式一相比这种方式不会逐一输出
ServiceLoader<UserFactory> serviceLoader = beanFactory.getBean("userFactoryServiceLoader", ServiceLoader.class);
displayServiceLoader(serviceLoader);
// 36.5.1 ServiceLoader的获取方式一
// 与方式二相比, 这里如果配置了多个类会逐一输出
// demoServiceLoader();
}
public static void demoServiceLoader() {
// ServiceLoader 是java1.6开始 提供的一个用于依赖注入的类, 其中
// private static final String PREFIX = "META-INF/services/";
// 我们需要这么个路径, 里面的文件名是需要实例化的类的接口的全类名
// 文件的内容则是接口实现类的全类名, 可以是一个或多个, 重复的话也会被去重, 这里就是jdk里面的反转控制/依赖查找
// serviceLoader的获取方式一, 通过ServiceLoader的静态方法 load(), 这是纯jdk的方法
ServiceLoader<UserFactory> serviceLoader = load(UserFactory.class, Thread.currentThread().getContextClassLoader());
displayServiceLoader(serviceLoader);
}
private static void displayServiceLoader(ServiceLoader<UserFactory> serviceLoader) {
// 可以看出这里允许实例化多个类
Iterator<UserFactory> iterator = serviceLoader.iterator();
while (iterator.hasNext()) {
UserFactory userFactory = iterator.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 36 | 实例化Spring Bean:Bean实例化的姿势有多少种? -->
<!-- 方式二, 通过ServiceLoaderFactoryBean, 这里通过spring来获取的ServiceLoader, 就需要用到 ServiceLoaderFactoryBean
public class ServiceLoaderFactoryBean extends AbstractServiceLoaderBasedFactoryBean implements BeanClassLoaderAware {
@Override
protected Object getObjectToExpose(ServiceLoader<?> serviceLoader) {
return serviceLoader;
}
@Override
public Class<?> getObjectType() {
return ServiceLoader.class;
}
}
这是spring提供的工厂类, 但是其中参数的注入怎么弄, 需要看下源码, 往父类找:
org.springframework.beans.factory.config.AbstractFactoryBean.getObject()
@Override
public final T getObject() throws Exception {
if (isSingleton()) {
return (this.initialized ? this.singletonInstance : getEarlySingletonInstance());
}
else {
return createInstance();
}
}
其中 createInstance() 有很多种实现, 我们看其中的基础实现:
org.springframework.beans.factory.serviceloader.AbstractServiceLoaderBasedFactoryBean.createInstance
protected Object createInstance() {
Assert.notNull(getServiceType(), "Property 'serviceType' is required");
return getObjectToExpose(ServiceLoader.load(getServiceType(), this.beanClassLoader));
}
可以看出来其实和方式一是类似的, 还是需要配置一个 serviceType , 所以我们提供一个要实例化的接口的全类名
我们再回来看 getObjectToExpose() 的实现
org.springframework.beans.factory.serviceloader.ServiceFactoryBean.getObjectToExpose
@Override
protected Object getObjectToExpose(ServiceLoader<?> serviceLoader) {
Iterator<?> it = serviceLoader.iterator();
if (!it.hasNext()) {
throw new IllegalStateException(
"ServiceLoader could not find service for type [" + getServiceType() + "]");
}
return it.next();
}
发现它有且只返回一个实现
有没有能返回多个的呢? 那是肯定的, AbstractServiceLoaderBasedFactoryBean 还有其他实现, 比如
org.springframework.beans.factory.serviceloader.ServiceListFactoryBean
public class ServiceListFactoryBean extends AbstractServiceLoaderBasedFactoryBean implements BeanClassLoaderAware {
@Override
protected Object getObjectToExpose(ServiceLoader<?> serviceLoader) {
List<Object> result = new LinkedList<>();
for (Object loaderObject : serviceLoader) {
result.add(loaderObject);
}
return result;
}
@Override
public Class<?> getObjectType() {
return List.class;
}
}
就可以看出来他就是返回一个list
-->
<bean id="userFactoryServiceLoader" class="org.springframework.beans.factory.serviceloader.ServiceLoaderFactoryBean">
<property name="serviceType" value="org.geekbang.thinking.in.spring.bean.factory.UserFactory" />
</bean>
</beans>
META-INF\services\org.geekbang.thinking.in.spring.bean.factory.UserFactory
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.geekbang.thinking.in.spring.bean.factory;
import org.geekbang.thinking.in.spring.ioc.overview.domain.User;
/**
* 36 | 实例化Spring Bean:Bean实例化的姿势有多少种?
* {@link User} 36.3 工厂类, 这里是一个代理工厂, 需要用jdk1.8的编译器, pom文件中需要修改编译级别
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
* @since
*/
public interface UserFactory {
// 1.8编译器支持的接口默认实现
default User createUser() {
return User.createUser();
}
}