闲谈
Hello,大家好,我是Java小面,今天我主管通过腾讯会议远程面试别人,小面是一边旁听一边学习,其中有一位的面试让我最印象深刻,因为他简历写对源码有所理解,所以主管问他知道几种实例化Bean的方式,他说只会用@Autowired。只懂得依赖于IOC的便利,对于底层是怎么实例化Bean是一无所知,听着屏幕对方支支吾吾的回答,真是让旁人看着都着急啊。
一般面试可以回答以下的几种Bean实例化(Instantiation)方式
- 常规方式
- 通过构造器(配置元信息:使用XML设置、通过Java注解设置和 调用Java API配置)
- 通过静态工厂方法(配置元信息:使用XML设置和 Java API配置)
- 通过Bean工厂方法(配置元信息:使用XML设置
- 通过FactoryBean (配置元信息:使用XML设置、通过Java注解设置和 Java API配置)
- 特殊方式
- 通过继承ServiceLoaderFactoryBean
- 通过调用AutowireCapableBeanFactory的createBean(Class<?> beanClass, int autowireMode, boolean dependencyCheck)方法
- 通过调用BeanDefinitionRegistry的registerBeanDefinition(String beanName, BeanDefinition beanDefinition)方法
接下来详细讲一下这几种实例化方法
一、通过构造器实例化Bean
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
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
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。
静态方法和实例方法其实没有什么太大的区别,通常很少有人会这么去实现。
四、通过FactoryBean实例化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实现Bean
点开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()); } }
六、通过AutowireCapableBeanFactory实例化Bean
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异常
七、通过BeanDefinitionRegistry实例化Bean
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了,但是身为程序员,若想在这条路上越走越远,那么底层的实现方法就有去了解的必要,否则一旦脱离了框架,自身的价值便一文不值了。