Spring源代码分析(9)---FactoryBean(我是谁,谁是我,谁是谁)

2024-03-28 08:30

本文主要是介绍Spring源代码分析(9)---FactoryBean(我是谁,谁是我,谁是谁),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本节,我们最分析ioc的最后一个核心点,那就是FactoryBean;
在ioc中,存在着这样的一种bean,他的引用并不是只想他自身,而是通过折射指向了别的bean,就因为他的存在,使得他支持了jdbc,jndi等多种j2ee技术,他维持了spring的80%的功能的实现,那么,就让我们来详细的分析一些这个神奇的bean,就好像武林外传里面的秀才杀死姬无命一样的,谁到底是谁,我们看似在取得factoryBean,却拿到了另外的一个类,失之东隅,收之桑榆;

我们以MethodInvokingFactoryBean为例子,这是一个很奇妙的bean,如下配置:

  1. <bean name="methodInvoke" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
  2. <property name="staticMethod">
  3.   <value>org.corey.demo.Demo.staticMethod</value>
  4. </property>
  5. </bean>
  6.    
我们在代码:
  1. ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
  2. ac.getBean("methodInvoke");

  1. ac.getBean("methodInvoke");返回的是org.corey.demo.Demo类的staticMethod方法的返回值;
那么,我们来看一下这些都是怎么实现的:



我们具体来看下几个类的代码:

  1. public interface FactoryBean {
  2.     
  3.     Object getObject() throws Exception;
  4.     Class getObjectType();
  5.     
  6.     boolean isSingleton();
  7. }

InitializingBean接口的话,我们已经不用介绍了;

MethodInvoker类主要含有这几个类:

  1.     private Class targetClass;
  2.     private Object targetObject;
  3.     private String targetMethod;
  4.     private Object[] arguments;
  5.     // the method we will call
  6.     private Method methodObject;

MethodInvoker类正式实现了MethodInvokingFactoryBean方法转嫁的核心功能,我们从入口来分析一下这个类:

  1.     public void afterPropertiesSet() throws Exception {
  2.         prepare();
  3.         if (this.singleton) {
  4.             Object obj = doInvoke();
  5.             this.singletonObject = (obj != null ? obj : MethodInvoker.VOID);
  6.         }
  7.     }
这是MethodInvokingFactoryBean的初始化方法:
  1. ublic void prepare() throws ClassNotFoundException, NoSuchMethodException {
  2.         if (this.targetClass == null) {
  3.             throw new IllegalArgumentException("Either targetClass or targetObject is required");
  4.         }
  5.         if (this.targetMethod == null) {
  6.             throw new IllegalArgumentException("targetMethod is required");
  7.         }
  8.         if (this.arguments == null) {
  9.             this.arguments = new Object[0];
  10.         }
  11.         Class[] argTypes = new Class[this.arguments.length];
  12.         for (int i = 0; i < this.arguments.length; ++i) {
  13.             argTypes[i] = (this.arguments[i] != null ? this.arguments[i].getClass() : Object.class);
  14.         }
  15.         // Try to get the exact method first.
  16.         try {
  17.             this.methodObject = this.targetClass.getMethod(this.targetMethod, argTypes);
  18.         }
  19.         catch (NoSuchMethodException ex) {
  20.             // Just rethrow exception if we can't get any match.
  21.             this.methodObject = findMatchingMethod();
  22.             if (this.methodObject == null) {
  23.                 throw ex;
  24.             }
  25.         }
  26.         if (this.targetObject == null && !Modifier.isStatic(this.methodObject.getModifiers())) {
  27.             throw new IllegalArgumentException("Target method must not be non-static without a target");
  28.         }
  29.     }
将你在xml文件中配置的MethodInvokingFactoryBean的属性(字符串)转化成为元数据,为等下的调用做准备;

  1. private Object doInvoke() throws Exception {
  2.         try {
  3.             return invoke();
  4.         }
  5.         catch (InvocationTargetException ex) {
  6.             if (ex.getTargetException() instanceof Exception) {
  7.                 throw (Exception) ex.getTargetException();
  8.             }
  9.             if (ex.getTargetException() instanceof Error) {
  10.                 throw (Error) ex.getTargetException();
  11.             }
  12.             throw ex;
  13.         }
  14.     }
  1.     public Object invoke() throws InvocationTargetException, IllegalAccessException {
  2.         if (this.methodObject == null) {
  3.             throw new IllegalStateException("prepare() must be called prior to invoke() on MethodInvoker");
  4.         }
  5.         // In the static case, target will just be <code>null</code>.
  6.         return this.methodObject.invoke(this.targetObject, this.arguments);
  7.     }

在这里分别调用了开始准备的目标类和目标方法;得到结果并且把他存储在singletonObject中,

在factoryBean接口中,实现了这个方法:

  1. public Object getObject() throws Exception {
  2.         if (this.singleton) {
  3.             // Singleton: return shared object.
  4.             return this.singletonObject;
  5.         }
  6.         else {
  7.             // Prototype: new object on each call.
  8.             Object retVal = doInvoke();
  9.             return (retVal != null ? retVal : MethodInvoker.VOID);
  10.         }
  11.     }
用getObject把真正的bean返回出去了,那么,现在我们的疑问来了,我们在getBean的时候,什么时候调用了这个getObject()方法呢???

我们来看一下getBean的实现:
在AbstractBeanFactory中,
public Object getBean(String name, Class requiredType, Object[] args) throws BeansException
是一个典型的模板模式的应用,他的createBean();在AbstractAutowireCapableBeanFactory实现,他负责从BeanDefinition构造出一个Objct,而getBean在拿到这个Object后,就会调用第三步:

bean = getObjectForSharedInstance(name, sharedInstance);

从这个Object类中拿到一个真正我们要返回出去的bean,而这里主要就是我们的factoryBean的处理;
  1. protected Object getObjectForSharedInstance(String name, Object beanInstance) throws BeansException {
  2.         String beanName = transformedBeanName(name);
  3.         // Don't let calling code try to dereference the
  4.         // bean factory if the bean isn't a factory.
  5.         if (isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
  6.             throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
  7.         }
  8.         // Now we have the bean instance, which may be a normal bean or a FactoryBean.
  9.         // If it's a FactoryBean, we use it to create a bean instance, unless the
  10.         // caller actually wants a reference to the factory.
  11.         if (beanInstance instanceof FactoryBean) {
  12.             if (!isFactoryDereference(name)) {
  13.                 // Return bean instance from factory.
  14.                 FactoryBean factory = (FactoryBean) beanInstance;
  15.                 if (logger.isDebugEnabled()) {
  16.                     logger.debug("Bean with name '" + beanName + "' is a factory bean");
  17.                 }
  18.                 try {
  19.                     beanInstance = factory.getObject();
  20.                 }
  21.                 catch (Exception ex) {
  22.                     throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
  23.                 }
  24.                 if (beanInstance == null) {
  25.                     throw new FactoryBeanNotInitializedException(
  26.                         beanName, "FactoryBean returned null object: " +
  27.                             "probably not fully initialized (maybe due to circular bean reference)");
  28.                 }
  29.             }
  30.             else {
  31.                 // The user wants the factory itself.
  32.                 if (logger.isDebugEnabled()) {
  33.                     logger.debug("Calling code asked for FactoryBean instance for name '" + beanName + "'");
  34.                 }
  35.             }
  36.         }
  37.         return beanInstance;
  38.     }
transformedBeanName去掉了beanName的&符号;
isFactoryDereference(name)判断一个类名是不是以&开头,如果这个类是FactoryBean并且beanName以&开头,那么则返回这个FactoryBean的本身,如果不是以&开始,则返回他所要转换的类;
beanInstance = factory.getObject();

就这样,我们在不知不觉的就完成这次南水北调的转换! *_*

最后,我们来总结一下Spring代码的此功能实现,在整个实现中,spring的代码都很有调理,很好的体现了面向接口编程,几乎每个具体的类(工具类除外)都是从接口开始着手,并且一层一层就想洋葱一样展现在我们的眼前,比如,首先,抽象出来一个顶层的factoryBean接口,提供了bean转换的统一接口,为我们组合型模板方式提供了可能,我们只要在beanFactory中调用他的getObject,而不必管这个类是从jndi还是从别的类的方法中得到的,从而把BeanFactory与具体的FactoryBean实现解耦开来,而且在MethodInvokingFacrotyBean中,我们把转换方法的实现用继承的方式委托给了MethodInvoker,功能复用的方式有两种,一种如这个一样的继承,坏处就是这是一种编译器的复用,无法实现策略模式一样的转换算法的功能,但是他好的就是我们不用显示的把委托代码重新写一次,而组合复用的好处是我们可以更换不同的MethodInvoker的实现,如果这里的targetClass和targetMethod有另外的处理方式,即MethodInvoker有另外的实现方式,我们可以考虑使用,如果没有的话,我们选择更方便的继承方式;
在代码中,大量的使用了工具类,这是单一职责原则的体现,为了避免意外的情况发生,或者说用户的盲目扩展,很多公开地方做了防御性的判断,并且在意外情况发生,输入参数非法的地方,抛出了明确的异常,这正式代码大全里所说的防御性编程的良好的体现,往往是这么一些微小的细节,更加的体现了大牛们代码的质量;设计的合理;这些都是值得我们深入去研究的地方;

因为我也初出茅庐,这些文章也只是我看spring源代码的一些小小的心得,在分享给有需要的朋友的同时,其实更多的是给自己招一个随处可得的笔记本,希望大家能够多多交流,在前面的getBean方法中也许分析得有些模糊,但是通过前几节对反射的重新复习,cglib的应用和asm的了解,和对ioc的代码一些全局的了解,现在我们再去看getBean代码,基本上不存在着什么疑问,至少我是这样的;所以,我想ioc的分析,应该就差不多到这里了,而spring的微核就是这个ioc容器,搞清楚这些对我们将来分析外围打下一个良好的基础;

这篇关于Spring源代码分析(9)---FactoryBean(我是谁,谁是我,谁是谁)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/855045

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听