Hello,大家好,我是Java小面,今天我主管通过腾讯会议远程面试别人,小面是一边旁听一边学习,其中有一位的面试让我最印象深刻,因为他简历写对源码有所理解,所以主管问他知道几种实例化Bean的方式,他说只会用@Autowired。只懂得依赖于IOC的便利,对于底层是怎么实例化Bean是一无所知,听着屏幕对方支支吾吾的回答,真是让旁人看着都着急啊。
一般面试可以回答以下的几种Bean实例化(Instantiation)方式
接下来详细讲一下这几种实例化方法
public static void main(String[] args)
{
ClassPathXmlApplicationContext beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-definition-create.xml");
Object user = beanFactory.getBean("user");
System.out.println(user);
}
<?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" class="org.example.pojo.User" >
<property name="id" value="1" />
<property name="name" value="Java面试教程" />
</bean>
</beans>
通过构造器实例化是最简单的,在xml里配置个bean,设置好属性就可以通过beanFactory.getBean(String)获取出来了。
bean-definition-create.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 -->
<bean id="user-java" class="org.example.pojo.User" factory-method="createUser" />
</beans>
在xml中定义一个Bean,id叫user-java,对应的实体是User,创建的方式是使用静态方法来创建Bean对象,该方法必须是静态的且存在与User实体类中。
User.java
package org.example.pojo;
/**
* @author Java面试教程
* @date 2022-11-22 21:16
*/
@Data
public class User {
private Integer id;
private String name;
//静态方法
public static User createUser(){
return new User(1,"Java面试教程");
}
@Override
public String toString()
{
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public User(Integer id, String name)
{
this.id = id;
this.name = name;
}
}
在User中创建静态方法createUser,返回类型为User。
然后就可以初始化Bean了。
BeanDefinitionDemo.java
package org.example.bean;
import org.example.pojo.User;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author Java面试教程
* @date 2022-11-22 20:43
*/
public class BeanDefinitionDemo {
public static void main(String[] args)
{
// 配置 XML 配置文件
// 启动 Spring 应用上下文
ClassPathXmlApplicationContext beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-definition-create.xml");
//获取bean
User bean = beanFactory.getBean("user-java", User.class);
//打印
System.out.println(bean.toString());
}
}
bean-definition-create.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 -->
<bean id="user-java" class="org.example.pojo.User" factory-method="createUser" />
<!-- 实例方法实例化 Bean -->
<bean id="user-by-instance" factory-bean="userFactory" factory-method="createUser" />
<bean id="userFactory" class="org.example.factory.DefaultUserFactory" />
</beans>
在原先的基础中新增实例方法实例化。
BeanDefinitionDemo.java
package org.example.definition;
import org.example.pojo.User;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author Java面试教程
* @date 2022-11-22 20:43
*/
public class BeanDefinitionDemo {
public static void main(String[] args)
{
// 配置 XML 配置文件
// 启动 Spring 应用上下文
ClassPathXmlApplicationContext beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-definition-create.xml");
//获取bean
User bean = beanFactory.getBean("user-java", User.class);
User userByInstance = beanFactory.getBean("user-by-instance", User.class);
//打印
System.out.println(bean.toString());
System.out.println(userByInstance.toString());
//是否相等
System.out.println(bean==userByInstance);
}
}
由于在xml中,这是两个不同的id,所以它们并不相等,是不同的两个bean。
静态方法和实例方法其实没有什么太大的区别,通常很少有人会这么去实现。
创建一个class,实现FactoryBean类
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
@Nullable
T getObject() throws Exception;
@Nullable
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
}
由于Spring5之后是用Java8,所以这个时候的FactoryBean里的isSingleton方法有了default来修饰,默认是单例的,所以我们只需要重新实现getObject和getObjectType方法即可。
package org.example.factory;
import org.springframework.beans.factory.FactoryBean;
/**
* @author Java面试教程
* @date 2022-11-22 21:54
*/
public class UserFactoryBean implements FactoryBean {
@Override
public Object getObject() throws Exception
{
return User.createUser();
}
@Override
public Class<?> getObjectType()
{
return User.class;
}
}
意味着我的实例方法、静态方法以及FactoryBean的实现都是用createUser这个静态方法来实现。
bean-definition-create.xml
<bean id="user-by-factory-bean" class="org.example.factory.UserFactoryBean" />
这个时候xml只需要加这么一句即可,不是直接定义User对象,而是直接会定一个FactoryBean。
之后只需要通过id来获取这个bean就可以了
//获取bean
User beanFactoryBean = beanFactory.getBean("user-by-factory-bean", User.class);
//打印
System.out.println(beanFactoryBean.toString());
点开ServiceLoaderFactoryBean源码,我们会发现这么一个类ServiceLoader,ServiceLoader是java1.6之后就有的。
public class ServiceLoaderFactoryBean extends AbstractServiceLoaderBasedFactoryBean implements BeanClassLoaderAware {
@Override
protected Object getObjectToExpose(ServiceLoader<?> serviceLoader) {
return serviceLoader;
}
@Override
public Class<?> getObjectType() {
return ServiceLoader.class;
}
}
我们再点开ServiceLoader源码,第一眼看到的就是META-INF/services/一个路径
public final class ServiceLoader<S>
implements Iterable<S>
{
private static final String PREFIX = "META-INF/services/";
......
}
它这个是用来配置实现类的。
首先我们需要再META-INF下创建services文件夹,复制UserFactory的全路径,创建一个名为UserFactory的全路径,没有文件后缀的文件。将作为UserFactory的实现类DefalutUserFactory的全路径放到里面去。
然后我们再回过去看ServiceLoaderFactoryBean,既然是实例化就避不开create或者Instance方法,仔细找了一下,方法在继承的AbstractServiceLoaderBasedFactoryBean中,名为createInstance
public abstract class AbstractServiceLoaderBasedFactoryBean extends AbstractFactoryBean<Object>
implements BeanClassLoaderAware {
@Nullable
private Class<?> serviceType;
@Nullable
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
/**
* Specify the desired service type (typically the service's public API).
*/
public void setServiceType(@Nullable Class<?> serviceType) {
this.serviceType = serviceType;
}
/**
* Return the desired service type.
*/
@Nullable
public Class<?> getServiceType() {
return this.serviceType;
}
@Override
public void setBeanClassLoader(@Nullable ClassLoader beanClassLoader) {
this.beanClassLoader = beanClassLoader;
}
/**
* Delegates to {@link #getObjectToExpose(java.util.ServiceLoader)}.
* @return the object to expose
*/
@Override
protected Object createInstance() {
Assert.notNull(getServiceType(), "Property 'serviceType' is required");
return getObjectToExpose(ServiceLoader.load(getServiceType(), this.beanClassLoader));
}
/**
* Determine the actual object to expose for the given ServiceLoader.
* <p>Left to concrete subclasses.
* @param serviceLoader the ServiceLoader for the configured service class
* @return the object to expose
*/
protected abstract Object getObjectToExpose(ServiceLoader<?> serviceLoader);
}
getObjectToExpose(ServiceLoader.load(getServiceType(), this.beanClassLoader))中不难发现,实例化的对象来自于getServiceType,那么serviceType的值从哪里来的呢?
答案是:从xml配置里来
<bean id="userFactoryServiceLoader" class="org.springframework.beans.factory.serviceloader.ServiceLoaderFactoryBean" >
<property name="serviceType" value="org.example.factory.UserFactory" />
</bean>
ServiceLoaderFactoryBean使用的是ServiceLoader,ServiceLoader默认读取的是/META-INF/services/下的文件,而value="org.example.factory.UserFactory"刚好就是文件名,从而serviceType获取到的是文件内存储的UserFactory的所有实现类全路径,通过ServiceLoader.load(getServiceType(), this.beanClassLoader)方法加载得到了bean对象。
public static void main(String[] args)
{
// 配置 XML 配置文件
// 启动 Spring 应用上下文
ClassPathXmlApplicationContext beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-service-loader-create.xml");
//获取ServiceLoader对象
ServiceLoader serviceLoader = beanFactory.getBean("userFactoryServiceLoader", ServiceLoader.class);
Iterator<UserFactory> iterator = serviceLoader.iterator();
while (iterator.hasNext()) {
//遍历对象
UserFactory userFactory = iterator.next();
System.out.println(userFactory.createUser().toString());
}
}
public static void main(String[] args)
{
// 配置 XML 配置文件
// 启动 Spring 应用上下文
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-service-loader-create.xml");
//通过 AutowireCapableBeanFactory 创建 AutowireCapableBeanFactory 对象
AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();
// 创建 UserFactory 对象,通过 AutowireCapableBeanFactory 进行依赖注入
UserFactory bean = beanFactory.createBean(DefaultUserFactory.class);
System.out.println(bean.createUser());
}
这里要注意的是:
UserFactory bean = beanFactory.createBean(DefaultUserFactory.class);
需要直接创建的是实例对象DefaultUserFactory.class,而不是UserFactory.class接口对象,否则会报BeanInstantiationException异常
DefaultListableBeanFactory是BeanDefinitionRegistry的一个实现类,这里我们使用DefaultListableBeanFactory这个实现类来实例化Bean。
public static void main(String[] args)
{
//创建一个BeanFactory容器
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
//设置Bean中的参数
MutablePropertyValues values=new MutablePropertyValues();
values.add("id",1);
values.add("name","my name is Java面试教程 ");
//BeanDefinition定义了 定义bean所需要的基本属性
BeanDefinition definition=new RootBeanDefinition(User.class,null,values);
//把参数设置进去
factory.registerBeanDefinition("user",definition);
User user=(User)factory.getBean("user");
System.out.print(user.toString());
}
几种Bean的实例化分享就到此结束了,虽然SpringBoot的便利已经让开发不再需要自己去实例化Bean了,但是身为程序员,若想在这条路上越走越远,那么底层的实现方法就有去了解的必要,否则一旦脱离了框架,自身的价值便一文不值了。