以后面试别再说你只会用@Autowired实例化Bean了

简介: 以后面试别再说你只会用@Autowired实例化Bean了

闲谈

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)方法
  • 通过调用BeanDefinitionRegistryregisterBeanDefinition(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了,但是身为程序员,若想在这条路上越走越远,那么底层的实现方法就有去了解的必要,否则一旦脱离了框架,自身的价值便一文不值了。

相关文章
|
6月前
|
消息中间件 安全 Java
面试官:单例Bean一定不安全吗?实际工作中如何处理此问题?
面试官:单例Bean一定不安全吗?实际工作中如何处理此问题?
102 5
|
29天前
|
架构师 Java 开发者
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?
在40岁老架构师尼恩的读者交流群中,近期多位读者成功获得了知名互联网企业的面试机会,如得物、阿里、滴滴等。然而,面对“Spring Boot自动装配机制”等核心面试题,部分读者因准备不足而未能顺利通过。为此,尼恩团队将系统化梳理和总结这一主题,帮助大家全面提升技术水平,让面试官“爱到不能自已”。
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?
|
Java 程序员 Spring
Spring中bean类的生命周期|面试必问如何回答
Spring中bean类的生命周期|面试必问如何回答
|
6月前
|
XML Java 数据格式
【springboot原理篇】Bean的加载方式,面试必看
【springboot原理篇】Bean的加载方式,面试必看
211 0
|
6月前
|
缓存 前端开发 Java
面试题:说说你对Bean作用域的理解和使用?
面试题:说说你对Bean作用域的理解和使用?
51 0
|
6月前
|
Java Spring 容器
面试题:在spring框架下面,Bean的属性lazy-init有什么作用,默认值是多少
面试题:在spring框架下面,Bean的属性lazy-init有什么作用,默认值是多少
41 0
|
6月前
|
缓存 Java Spring
面试官猛的一问:Spring的Bean注入如何解决循环依赖的?
面试官猛的一问:Spring的Bean注入如何解决循环依赖的?
192 0
|
存储 缓存 Java
面试官:Spring Bean的生命周期,你知道吗?
面试官:Spring Bean的生命周期,你知道吗?
46 0
|
Java 程序员 API
大厂高频面试题Spring Bean生命周期最详解
Spring作为当前Java最流行、最强大的轻量级框架。Spring Bean的生命周期也是面试高频题,了解Spring Bean周期也能更好地帮助我们解决日常开发中的问题。程序员应该都知道Spring的基础容器是ApplicationContext。应很多粉丝的强烈建议,本文我来分析分析 ApplicationContext中Bean的生命周期。ApplicationContext是顶层容器接口BeanFactory的实现类,因此,我们了解了ApplicationContext的生命周期逻辑,也基本上了解了其他类型容器的生命周期逻辑。
112 0
|
Java Spring
面试了才知道初始化Bean不仅只有new那么简单
面试了才知道初始化Bean不仅只有new那么简单
56 0