前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >以后面试别再说你只会用@Autowired实例化Bean了

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

作者头像
灬沙师弟
发布2023-03-07 09:31:50
2100
发布2023-03-07 09:31:50
举报
文章被收录于专栏:Java面试教程

闲谈

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

代码语言:javascript
复制
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);

    }
代码语言:javascript
复制
<?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

代码语言:javascript
复制
<?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

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
<?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

代码语言:javascript
复制
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类

代码语言:javascript
复制
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方法即可。

代码语言:javascript
复制
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

代码语言:javascript
复制
<bean id="user-by-factory-bean"  class="org.example.factory.UserFactoryBean" />

这个时候xml只需要加这么一句即可,不是直接定义User对象,而是直接会定一个FactoryBean。

之后只需要通过id来获取这个bean就可以了

代码语言:javascript
复制
//获取bean
User beanFactoryBean = beanFactory.getBean("user-by-factory-bean", User.class);
//打印
System.out.println(beanFactoryBean.toString());

五、通过ServiceLoaderFactoryBean实现Bean

点开ServiceLoaderFactoryBean源码,我们会发现这么一个类ServiceLoader,ServiceLoader是java1.6之后就有的。

代码语言:javascript
复制
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/一个路径

代码语言:javascript
复制
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

代码语言:javascript
复制
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配置里来

代码语言:javascript
复制
<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对象。

代码语言:javascript
复制
 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

代码语言:javascript
复制
 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。

代码语言:javascript
复制
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了,但是身为程序员,若想在这条路上越走越远,那么底层的实现方法就有去了解的必要,否则一旦脱离了框架,自身的价值便一文不值了。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-12-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java面试教程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 闲谈
  • 一、通过构造器实例化Bean
  • 二、通过静态方法实例化Bean
  • 三、通过实例工厂方法实例化Bean
  • 四、通过FactoryBean实例化Bean
  • 五、通过ServiceLoaderFactoryBean实现Bean
  • 六、通过AutowireCapableBeanFactory实例化Bean
  • 七、通过BeanDefinitionRegistry实例化Bean
  • 结束语
相关产品与服务
腾讯会议
腾讯会议(Tencent Meeting)为企业打造专属的会议能力,卓越的音视频性能,丰富的会议协作能力,坚实的会议安全保障,提升协作效率,满足大中小会议全场景需求。您可以使用腾讯会议进行远程音视频会议、在线协作、会管会控、会议录制、指定邀请、布局管理、同声传译等。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档