从源码上详细学习FactoryBean概念与其作用

从源码上详细学习FactoryBean概念与其作用,第1张

源码上详细学习FactoryBean概念与其作用

文章目录
  • 1. 概念
  • 2. FactoryBean的作用
  • 3. 通过代码查看FactoryBean的作用
  • 4. 根据源码上解析
    • 1. 先说结论
    • 2. 源码分析
  • 5. FactoryBean运用在什么场景下
  • 6. @Bean跟FactoryBean的区别
  • 7. FactoryBean中的创建Bean对象相当于懒加载?
  • 8. BeanFactory与FactoryBean的区别
  • 9. 如何拿到实现FactoryBean的类的bean对象,而不是getObject的bean对象

1. 概念
  1. FactoryBean 本质上是一个接口,具体如下:

    public interface FactoryBean {
    	// 获取一个对象
    	@Nullable
    	T getObject() throws Exception;
    
    	// 获取对象的类型
    	@Nullable
    	Class getObjectType();
    
    	// 判断是否为单例,默认返回true 即是单例
    	default boolean isSingleton() {
    		return true;
    	}
    }
    
2. FactoryBean的作用
  1. 实现它的bean类实际上是一个特殊的bean,允许程序员自定义一个对象通过FactoryBean间接的放到Spring容器中成为一个Bean
3. 通过代码查看FactoryBean的作用
  1. 通过下面测试代码,看到FactoryBean的本质:

    定义一个bean类,并getBean

    @Component
    public class UserService {
    }
    
    public class demo {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext applicationContext
                    = new AnnotationConfigApplicationContext("com.king.learning");
            System.out.println(applicationContext.getBean("userService"));
        }
    }
    
    


    上面是我们常用的方式,确实得到的是UserService对象。
    现在将UserService 实现 FactoryBean接口,并重新运行getBean。

    public class User {
    	// 注意 User 上没有任何注解
    }
    
    @Component
    public class UserService implements FactoryBean{
    	
        @Override
        public Object getObject() throws Exception {
        	// 创建user对象
            return new User();
        }
    
        @Override
        public Class getObjectType() {
        	// 返回 User.class
            return User.class;
        }
    }
    
    // 测试代码不变
    public class demo {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext applicationContext
                    = new AnnotationConfigApplicationContext("com.king.learning");
         // 留意:我仍然是getBean("userService")
            System.out.println(applicationContext.getBean("userService"));
        }
    }
    


    通过上面结论:

    1. 即使我们getBean(“userService”)但是返回的居然是User对象。
    2. FactoryBean允许程序员自定义一个对象通过FactoryBean间接的放到Spring容器中成为一个Bean
4. 根据源码上解析 1. 先说结论
  1. spring容器在启动的时候,会去扫描带注解的类,生成对应的BeanDefinition,并生成对应的Bean对象,然后放入单例池中,注意:这里生成的bean 是非懒加载的单例bean。
  2. 在getBean的时候,会去判断 这个bean对象是否实现了FactoryBean接口?
  3. 如果实现则不返回这个bean对象,而是去判断isSingleton是否为单例?
  4. 如果不是单例的,直接调用getObject方法生成新的对象并返回。
  5. 如果是单例的,则取缓存factoryBeanObjectCache (factorybean 产生的对象 的缓存),
  6. 如果是单例的且不在缓存中,则会去调用factoryBean的getObject方法,并将生成的对象放入在缓存中,并返回。
  7. 注意:getObject方法只会被调用一次,因为第一次创建之后,都会存在缓存factoryBeanObjectCache 中,不会在getObejct了
2. 源码分析

由于spring的getBean方法里面做了很多事情,因此这篇文章只看跟factoryBean有关系的源码




到这里基本上FactoryBean的大体有一定的理解,也知道怎么调用的,但是我们似乎只是理解,好像还差点什么东西,别急,精彩还在下面。

5. FactoryBean运用在什么场景下
  1. 我们知道FactoryBean的作用:允许程序员自定义一个对象通过FactoryBean间接的放到Spring容器中成为一个Bean。

  2. 举例:使用mybatis的时候,我们定义的Dao层都是接口,但是在spring容器中确实是存在对应的bean对象,就是用了FactoryBean,当然mybatis使用的是MapperFactoryBean。

  3. 简单模拟:

    // 定义dao接口
    public interface UserDao {
        String getAll();
    }
    
    // 定义FactoryBean
    @Component
    public class UserDaoFactoryBean implements FactoryBean {
    
        @Override
        public Object getObject() throws Exception {
        	// 这里使用了 动态代理
            return  Proxy.newProxyInstance(UserDaoFactoryBean.class.getClassLoader(), new Class[]{UserDao.class}, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    return null;
                }
            });
        }
    
        @Override
        public Class getObjectType() {
            return UserDao.class;
        }
    }
    
    // 定义Service,并依赖注入dao接口
    @Component
    public class UserService{
    
        @Autowired
        private UserDao userDao;
    }
    // 测试代码
    public class demo {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext applicationContext
                    = new AnnotationConfigApplicationContext("com.king.learning");
            final UserService userService = applicationContext.getBean("userService", UserService.class);
            System.out.println(userService);
        }
    }
    



    从结论可以看出,没有报错,可以注入,且注入的是个代理对象。

  4. 相关知识链接:

    1. 动态代理:https://blog.csdn.net/xueyijin/article/details/122276921
    2. mybatis的Mapper创建bean对象并放入spring容器:期待,小编努力中。
6. @Bean跟FactoryBean的区别
  1. @Bean的作用:也是创建一个对象,并放入spring容器中,似乎跟FactoryBean没有什么区别。
  2. 实际上:FactoryBean的功能是比@Bean强的,@Bean是一个注解,FactoryBean是一个接口,类去实现它,可以额外拥有很多功能。
  3. 如:
7. FactoryBean中的创建Bean对象相当于懒加载?
  1. 是的,相当于懒加载,根据查看getBean 有关于FactoryBean的源码,我们可以知道,在getBean的时候,才会去判断该对象是不是FactoryBean,如果是且第一次获取,则会先去调用 Factorybean的getObject方法,先创建对象,因此是懒加载。
8. BeanFactory与FactoryBean的区别
  1. BeanFactory是容器,是大型工厂,可以产生各种各样类型的bean,其中包括普通bean,也有FactoryBean。
  2. FactoryBean也是工厂,但是是小型工厂,可以产生同一种类型的bean。
9. 如何拿到实现FactoryBean的类的bean对象,而不是getObject的bean对象
  1. 只需要在getBean的时候,在beanName前面加上 & 符号,这是spring的规定。

  2. getBean("&userService") 加上& 则会拿到factoryBean自身的对象了,而不是getObject的bean对象。

  3. 源码解析:



欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5695610.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存