spring源码-FactoryBean基本使用

spring源码-FactoryBean入门

参考网址:

https://mp.weixin.qq.com/s/Q_dvZEM1FjRnLt1avy8BFw

方法介绍

共有3个方法需要实现:

(1)getObject():从工厂中获取bean

(2)getObjectType():获取Bean工厂创建的对象的类型,该方法返回的类型是在ioc容器中getbean所匹配的类型。

(3)isSingleton():Bean工厂创建的对象是否是单例模式。

FactoryBean.java

package org.springframework.beans.factory;

import org.springframework.lang.Nullable;

public interface FactoryBean<T> {
    String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";

    @Nullable
    T getObject() throws Exception;

    @Nullable
    Class<?> getObjectType();

    default boolean isSingleton() {
        return true;
    }
}

FactoryBean作用

一般情况下,Spring通过反射机制利用的class属性指定实现类实例化Bean。至于为什么会有FactoryBean?原因有两个:

(1)在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。

FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。

(2)由于第三方库不能直接注册到spring容器,于是可以实现org.springframework.bean.factory.FactoryBean接口,然后给出自己对象的实例化代码即可。

FactoryBean的应用

Mybatis中的SqlSessionFactoryBean。阿里开源的分布式服务框架 Dubbo中的Consumer 也使用到了FactoryBean

示意图

FactoryBean举例-SqlSessionFactoryBean

FactoryBean的基本使用

新建springboot项目

引入相关依赖

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>springboot-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.0</version>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

创建一个普通的实体类

User.java

package com.example;


public class User {
    private Integer id;
    private String userName;

    public User() {
        System.out.println("com.example.User 的 构造方法");
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }
}

构建一个FactoryBean

对于 FactoryBean 的使用很简单,只需要实现接口 UserFactoryBean:

UserFacotryBean.java

package com.example;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;

@Component("user")
public class UserFactoryBean implements FactoryBean<User> {

    @Override
    public User getObject() throws Exception {
        User user = new User();
        user.setId(1);
        user.setUserName("自定义 userName");
        return user;
    }

    @Override
    public Class<?> getObjectType() {
        return User.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

测试例子

在启动类上测试

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.ApplicationContext;


@SpringBootApplication
public class SpringbootDemoApplication {

    public static void main(String[] args) {
        ApplicationContext ac = SpringApplication.run(SpringbootDemoApplication.class, args);
        User user1 = ac.getBean(User.class);
        User user2 = ac.getBean(User.class);
        System.out.println(user1);
        System.out.println(user2);
        System.out.println("use是否是单例 :  "+ (user1==user2));

        UserFactoryBean userFactoryBean = ac.getBean("&user", UserFactoryBean.class);
        System.out.println(userFactoryBean);
    }
    
 }

控制台打印

com.example.User 的 构造方法
com.example.User@4d7e7435
com.example.User@4d7e7435
use是否是单例 : true
com.example.UserFactoryBean@4a1e3ac1

说明:

(1)如果不加&返回的是实例,加了返回的是工厂bean本身。

(2)如果不执行获取实例的方法,也就是不执行ctx.getBean(Computer.class)获取实例方法,是不会构建Computer实例的,也就不会执行构造方法的。获取工厂Bean的话,也是不会实例的创建。

来看下通过beanName获取FactoryBean创建的对象:

(1)移除name变量的&前缀,用一个beanName变量接收(这次没有&前缀,name和beanName是一样的)然后从容器的缓存中取(FactoryBean本身),取到后走getObjectForBeanInstance,该方法需要将从容器中拿到的FactoryBean本身、原始参数name和beanName都要传过去。

(2)判断name是不是以&为前缀,不是的话,创建一个Object对象,用来接收FactoryBean创建出来的Bean对象。

(3)从工厂的factoryBeanObjectCached(FactoryBean创建Bean的缓存)缓存中取,有的话直接返回(第一次获取肯定没有),没有通过FactoryBean#getObject方法进行创建流程。

anInstance,该方法需要将从容器中拿到的FactoryBean本身、原始参数name和beanName都要传过去。

(2)判断name是不是以&为前缀,不是的话,创建一个Object对象,用来接收FactoryBean创建出来的Bean对象。

(3)从工厂的factoryBeanObjectCached(FactoryBean创建Bean的缓存)缓存中取,有的话直接返回(第一次获取肯定没有),没有通过FactoryBean#getObject方法进行创建流程。

(4)判断该FactoryBean#isSingleton是单例还是原型对象。然后通过FactoryBean#getObject创建一个Bean出来,如果是单例对象,放入factoryBeanObjectCached缓存中,如果是原型,不存缓存中。最后直接返回。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值