文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

横刀跃马,关于Bean对象作用域以及FactoryBean的实现和使用

2024-12-03 03:14

关注

 目录

一、前言

老司机,你的砖怎么搬的那么快?

是有劲?是技巧?是后门?总之,那个老司机的代码总是可以很快的完成产品每次新增的需求,就像他俩是一家似的!而你就不一样了,不只产品经理还有运营、测试的小姐姐,都得给你买吃的,求着你赶紧把Bug修修,否则都来不及上线了。

那为啥别人的代码总是可以很快的扩展新功能,而你的代码从来不能被重构只能被重写,小需求小改、大需求大改,没需求呢?没需求自己跑着跑着也能崩溃,半夜被运维薅起来:“你这怎么又有数据库慢查询,把别人业务都拖拉胯了!”

有人说30岁的人都,还和刚毕业的做一样的活,是没进步的! 这太扯淡了,同样是同样的活,但做出来的结果可不一定是一样的,有人能用ifelse把产品功能凑出来,也有人可以把需求拆解成各个功能模块,定义接口、抽象类、实现和继承,运用设计模式构建出一套新增需求时候能快速实现,出现问题能准确定位的代码逻辑。这就像有人问:“树上有十只鸟,一枪打死一只,还有几只?”,你会想到什么?枪声大吗、鸟笼了吗、鸟被绑树上了吗、有鸟残疾的吗、鸟被打死了吗、打鸟的人眼睛好使吗、算肚子里怀孕的鸟吗、打鸟犯法吗、边上树还有其他鸟吗等等,这些都是一个职业技术人在一个行业磨练出来的经验,不是1天2天看几本书,刷几个洗脑文章能吸收的。

二、目标

交给 Spring 管理的 Bean 对象,一定就是我们用类创建出来的 Bean 吗?创建出来的 Bean 就永远是单例的吗,没有可能是原型模式吗?

在集合 Spring 框架下,我们使用的 MyBatis 框架中,它的核心作用是可以满足用户不需要实现 Dao 接口类,就可以通过 xml 或者注解配置的方式完成对数据库执行 CRUD 操作,那么在实现这样的 ORM 框架中,是怎么把一个数据库操作的 Bean 对象交给 Spring 管理的呢。

因为我们在使用 Spring、MyBatis 框架的时候都可以知道,并没有手动的去创建任何操作数据库的 Bean 对象,有的仅仅是一个接口定义,而这个接口定义竟然可以被注入到其他需要使用 Dao 的属性中去了,那么这一过程最核心待解决的问题,就是需要完成把复杂且以代理方式动态变化的对象,注册到 Spring 容器中。而为了满足这样的一个扩展组件开发的需求,就需要我们在现有手写的 Spring 框架中,添加这一能力。

三、方案

关于提供一个能让使用者定义复杂的 Bean 对象,功能点非常不错,意义也非常大,因为这样做了之后 Spring 的生态种子孵化箱就此提供了,谁家的框架都可以在此标准上完成自己服务的接入。

但这样的功能逻辑设计上并不复杂,因为整个 Spring 框架在开发的过程中就已经提供了各项扩展能力的接茬,你只需要在合适的位置提供一个接茬的处理接口调用和相应的功能逻辑实现即可,像这里的目标实现就是对外提供一个可以二次从 FactoryBean 的 getObject 方法中获取对象的功能即可,这样所有实现此接口的对象类,就可以扩充自己的对象功能了。MyBatis 就是实现了一个 MapperFactoryBean 类,在 getObject 方法中提供 SqlSession 对执行 CRUD 方法的操作 整体设计结构如下图:

四、实现

1. 工程结构

  1. small-spring-step-09 
  2. └── src 
  3.     ├── main 
  4.     │   └── java 
  5.     │       └── cn.bugstack.springframework 
  6.     │           ├── beans 
  7.     │           │   ├── factory 
  8.     │           │   │   ├── config 
  9.     │           │   │   │   ├── AutowireCapableBeanFactory.java 
  10.     │           │   │   │   ├── BeanDefinition.java 
  11.     │           │   │   │   ├── BeanFactoryPostProcessor.java 
  12.     │           │   │   │   ├── BeanPostProcessor.java 
  13.     │           │   │   │   ├── BeanReference.java 
  14.     │           │   │   │   ├── ConfigurableBeanFactory.java 
  15.     │           │   │   │   └── SingletonBeanRegistry.java 
  16.     │           │   │   ├── support 
  17.     │           │   │   │   ├── AbstractAutowireCapableBeanFactory.java 
  18.     │           │   │   │   ├── AbstractBeanDefinitionReader.java 
  19.     │           │   │   │   ├── AbstractBeanFactory.java 
  20.     │           │   │   │   ├── BeanDefinitionReader.java 
  21.     │           │   │   │   ├── BeanDefinitionRegistry.java 
  22.     │           │   │   │   ├── CglibSubclassingInstantiationStrategy.java 
  23.     │           │   │   │   ├── DefaultListableBeanFactory.java 
  24.     │           │   │   │   ├── DefaultSingletonBeanRegistry.java 
  25.     │           │   │   │   ├── DisposableBeanAdapter.java 
  26.     │           │   │   │   ├── FactoryBeanRegistrySupport.java 
  27.     │           │   │   │   ├── InstantiationStrategy.java 
  28.     │           │   │   │   └── SimpleInstantiationStrategy.java   
  29.     │           │   │   ├── support 
  30.     │           │   │   │   └── XmlBeanDefinitionReader.java 
  31.     │           │   │   ├── Aware.java 
  32.     │           │   │   ├── BeanClassLoaderAware.java 
  33.     │           │   │   ├── BeanFactory.java 
  34.     │           │   │   ├── BeanFactoryAware.java 
  35.     │           │   │   ├── BeanNameAware.java 
  36.     │           │   │   ├── ConfigurableListableBeanFactory.java 
  37.     │           │   │   ├── DisposableBean.java 
  38.     │           │   │   ├── FactoryBean.java 
  39.     │           │   │   ├── HierarchicalBeanFactory.java 
  40.     │           │   │   ├── InitializingBean.java 
  41.     │           │   │   └── ListableBeanFactory.java 
  42.     │           │   ├── BeansException.java 
  43.     │           │   ├── PropertyValue.java 
  44.     │           │   └── PropertyValues.java  
  45.     │           ├── context 
  46.     │           │   ├── support 
  47.     │           │   │   ├── AbstractApplicationContext.java  
  48.     │           │   │   ├── AbstractRefreshableApplicationContext.java  
  49.     │           │   │   ├── AbstractXmlApplicationContext.java  
  50.     │           │   │   ├── ApplicationContextAwareProcessor.java  
  51.     │           │   │   └── ClassPathXmlApplicationContext.java  
  52.     │           │   ├── ApplicationContext.java  
  53.     │           │   ├── ApplicationContextAware.java  
  54.     │           │   └── ConfigurableApplicationContext.java 
  55.     │           ├── core.io 
  56.     │           │   ├── ClassPathResource.java  
  57.     │           │   ├── DefaultResourceLoader.java  
  58.     │           │   ├── FileSystemResource.java  
  59.     │           │   ├── Resource.java  
  60.     │           │   ├── ResourceLoader.java  
  61.     │           │   └── UrlResource.java 
  62.     │           └── utils 
  63.     │               └── ClassUtils.java 
  64.     └── test 
  65.         └── java 
  66.             └── cn.bugstack.springframework.test 
  67.                 ├── bean 
  68.                 │   ├── UserDao.java 
  69.                 │   └── UserService.java 
  70.                 └── ApiTest.java 

Spring 单例、原型以及 FactoryBean 功能实现类关系,如图 10-2

图 10-2

2. Bean的作用范围定义和xml解析

  1. public class BeanDefinition { 
  2.  
  3.     String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON; 
  4.  
  5.     String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE; 
  6.  
  7.     private Class beanClass; 
  8.  
  9.     private PropertyValues propertyValues; 
  10.  
  11.     private String initMethodName; 
  12.  
  13.     private String destroyMethodName; 
  14.  
  15.     private String scope = SCOPE_SINGLETON; 
  16.  
  17.     private boolean singleton = true
  18.  
  19.     private boolean prototype = false
  20.      
  21.     // ...get/set 

cn.bugstack.springframework.beans.factory.xml.XmlBeanDefinitionReader

  1. public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { 
  2.  
  3.     protected void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException { 
  4.        
  5.         for (int i = 0; i < childNodes.getLength(); i++) { 
  6.             // 判断元素 
  7.             if (!(childNodes.item(i) instanceof Element)) continue
  8.             // 判断对象 
  9.             if (!"bean".equals(childNodes.item(i).getNodeName())) continue
  10.  
  11.             // 解析标签 
  12.             Element bean = (Element) childNodes.item(i); 
  13.             String id = bean.getAttribute("id"); 
  14.             String name = bean.getAttribute("name"); 
  15.             String className = bean.getAttribute("class"); 
  16.             String initMethod = bean.getAttribute("init-method"); 
  17.             String destroyMethodName = bean.getAttribute("destroy-method"); 
  18.             String beanScope = bean.getAttribute("scope"); 
  19.  
  20.             // 获取 Class,方便获取类中的名称 
  21.             Class clazz = Class.forName(className); 
  22.             // 优先级 id > name 
  23.             String beanName = StrUtil.isNotEmpty(id) ? id : name
  24.             if (StrUtil.isEmpty(beanName)) { 
  25.                 beanName = StrUtil.lowerFirst(clazz.getSimpleName()); 
  26.             } 
  27.  
  28.             // 定义Bean 
  29.             BeanDefinition beanDefinition = new BeanDefinition(clazz); 
  30.             beanDefinition.setInitMethodName(initMethod); 
  31.             beanDefinition.setDestroyMethodName(destroyMethodName); 
  32.  
  33.             if (StrUtil.isNotEmpty(beanScope)) { 
  34.                 beanDefinition.setScope(beanScope); 
  35.             } 
  36.              
  37.             // ... 
  38.              
  39.             // 注册 BeanDefinition 
  40.             getRegistry().registerBeanDefinition(beanName, beanDefinition); 
  41.         } 
  42.     } 
  43.  

3. 创建和修改对象时候判断单例和原型模式

cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory

  1. public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { 
  2.  
  3.     private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy(); 
  4.  
  5.     @Override 
  6.     protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException { 
  7.         Object bean = null
  8.         try { 
  9.             bean = createBeanInstance(beanDefinition, beanName, args); 
  10.             // 给 Bean 填充属性 
  11.             applyPropertyValues(beanName, bean, beanDefinition); 
  12.             // 执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法 
  13.             bean = initializeBean(beanName, bean, beanDefinition); 
  14.         } catch (Exception e) { 
  15.             throw new BeansException("Instantiation of bean failed", e); 
  16.         } 
  17.  
  18.         // 注册实现了 DisposableBean 接口的 Bean 对象 
  19.         registerDisposableBeanIfNecessary(beanName, bean, beanDefinition); 
  20.  
  21.         // 判断 SCOPE_SINGLETON、SCOPE_PROTOTYPE 
  22.         if (beanDefinition.isSingleton()) { 
  23.             addSingleton(beanName, bean); 
  24.         } 
  25.         return bean; 
  26.     } 
  27.  
  28.     protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) { 
  29.         // 非 Singleton 类型的 Bean 不执行销毁方法 
  30.         if (!beanDefinition.isSingleton()) return
  31.  
  32.         if (bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())) { 
  33.             registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition)); 
  34.         } 
  35.     } 
  36.      
  37.     // ... 其他功能 

4. 定义 FactoryBean 接口

cn.bugstack.springframework.beans.factory.FactoryBean

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

FactoryBean 中需要提供3个方法,获取对象、对象类型,以及是否是单例对象,如果是单例对象依然会被放到内存中。

5. 实现一个 FactoryBean 注册服务

cn.bugstack.springframework.beans.factory.support.FactoryBeanRegistrySupport

  1. public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry { 
  2.  
  3.      
  4.     private final Map factoryBeanObjectCache = new ConcurrentHashMap(); 
  5.  
  6.     protected Object getCachedObjectForFactoryBean(String beanName) { 
  7.         Object object = this.factoryBeanObjectCache.get(beanName); 
  8.         return (object != NULL_OBJECT ? object : null); 
  9.     } 
  10.  
  11.     protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName) { 
  12.         if (factory.isSingleton()) { 
  13.             Object object = this.factoryBeanObjectCache.get(beanName); 
  14.             if (object == null) { 
  15.                 object = doGetObjectFromFactoryBean(factory, beanName); 
  16.                 this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT)); 
  17.             } 
  18.             return (object != NULL_OBJECT ? object : null); 
  19.         } else { 
  20.             return doGetObjectFromFactoryBean(factory, beanName); 
  21.         } 
  22.     } 
  23.  
  24.     private Object doGetObjectFromFactoryBean(final FactoryBean factory, final String beanName){ 
  25.         try { 
  26.             return factory.getObject(); 
  27.         } catch (Exception e) { 
  28.             throw new BeansException("FactoryBean threw exception on object[" + beanName + "] creation", e); 
  29.         } 
  30.     } 
  31.  

6. 扩展 AbstractBeanFactory 创建对象逻辑

cn.bugstack.springframework.beans.factory.support.AbstractBeanFactory

  1. public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory { 
  2.  
  3.     protected  T doGetBean(final String name, final Object[] args) { 
  4.         Object sharedInstance = getSingleton(name); 
  5.         if (sharedInstance != null) { 
  6.             // 如果是 FactoryBean,则需要调用 FactoryBean#getObject 
  7.             return (T) getObjectForBeanInstance(sharedInstance, name); 
  8.         } 
  9.  
  10.         BeanDefinition beanDefinition = getBeanDefinition(name); 
  11.         Object bean = createBean(name, beanDefinition, args); 
  12.         return (T) getObjectForBeanInstance(bean, name); 
  13.     }   
  14.     
  15.     private Object getObjectForBeanInstance(Object beanInstance, String beanName) { 
  16.         if (!(beanInstance instanceof FactoryBean)) { 
  17.             return beanInstance; 
  18.         } 
  19.  
  20.         Object object = getCachedObjectForFactoryBean(beanName); 
  21.  
  22.         if (object == null) { 
  23.             FactoryBean factoryBean = (FactoryBean) beanInstance; 
  24.             object = getObjectFromFactoryBean(factoryBean, beanName); 
  25.         } 
  26.  
  27.         return object; 
  28.     } 
  29.          
  30.     // ... 

五、测试

1. 事先准备

cn.bugstack.springframework.test.bean.IUserDao

  1. public interface IUserDao { 
  2.  
  3.     String queryUserName(String uId); 
  4.  

cn.bugstack.springframework.test.bean.UserService

  1. public class UserService { 
  2.  
  3.     private String uId; 
  4.     private String company; 
  5.     private String location; 
  6.     private IUserDao userDao; 
  7.  
  8.     public String queryUserInfo() { 
  9.         return userDao.queryUserName(uId) + "," + company + "," + location; 
  10.     } 
  11.  
  12.     // ...get/set 

在 UserService 新修改了一个原有 UserDao 属性为 IUserDao,后面我们会给这个属性注入代理对象。

2. 定义 FactoryBean 对象

cn.bugstack.springframework.test.bean.ProxyBeanFactory

  1. public class ProxyBeanFactory implements FactoryBean { 
  2.  
  3.     @Override 
  4.     public IUserDao getObject() throws Exception { 
  5.         InvocationHandler handler = (proxy, method, args) -> { 
  6.  
  7.             Map hashMap = new HashMap<>(); 
  8.             hashMap.put("10001""小傅哥"); 
  9.             hashMap.put("10002""八杯水"); 
  10.             hashMap.put("10003""阿毛"); 
  11.              
  12.             return "你被代理了 " + method.getName() + ":" + hashMap.get(args[0].toString()); 
  13.         }; 
  14.         return (IUserDao) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{IUserDao.class}, handler); 
  15.     } 
  16.  
  17.     @Override 
  18.     public Class getObjectType() { 
  19.         return IUserDao.class; 
  20.     } 
  21.  
  22.     @Override 
  23.     public boolean isSingleton() { 
  24.         return true
  25.     } 
  26.  

3. 配置文件

  1. "1.0" encoding="UTF-8"?> 
  2.  
  3.  
  4.     "userService" class="cn.bugstack.springframework.test.bean.UserService" scope="prototype"
  5.         name="uId" value="10001"/> 
  6.         name="company" value="腾讯"/> 
  7.         name="location" value="深圳"/> 
  8.         name="userDao" ref="proxyUserDao"/> 
  9.      
  10.  
  11.     "proxyUserDao" class="cn.bugstack.springframework.test.bean.ProxyBeanFactory"/> 
  12.  
  13.  

4. 单元测试(单例&原型)

  1. @Test 
  2. public void test_prototype() { 
  3.     // 1.初始化 BeanFactory 
  4.     ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml"); 
  5.     applicationContext.registerShutdownHook();    
  6.  
  7.     // 2. 获取Bean对象调用方法 
  8.     UserService userService01 = applicationContext.getBean("userService", UserService.class); 
  9.     UserService userService02 = applicationContext.getBean("userService", UserService.class); 
  10.      
  11.     // 3. 配置 scope="prototype/singleton" 
  12.     System.out.println(userService01); 
  13.     System.out.println(userService02);     
  14.  
  15.     // 4. 打印十六进制哈希 
  16.     System.out.println(userService01 + " 十六进制哈希:" + Integer.toHexString(userService01.hashCode())); 
  17.     System.out.println(ClassLayout.parseInstance(userService01).toPrintable()); 
  18.  

测试结果

  1. cn.bugstack.springframework.test.bean.UserService$$EnhancerByCGLIB$$4cabb984@1b0375b3 
  2. cn.bugstack.springframework.test.bean.UserService$$EnhancerByCGLIB$$4cabb984@2f7c7260 
  3. cn.bugstack.springframework.test.bean.UserService$$EnhancerByCGLIB$$4cabb984@1b0375b3 十六进制哈希:1b0375b3 
  4. cn.bugstack.springframework.test.bean.UserService$$EnhancerByCGLIB$$4cabb984 object internals: 
  5.  OFFSET  SIZE                                             TYPE DESCRIPTION                                               VALUE 
  6.       0     4                                                  (object header)                                           01 b3 75 03 (00000001 10110011 01110101 00000011) (58045185) 
  7.       4     4                                                  (object header)                                           1b 00 00 00 (00011011 00000000 00000000 00000000) (27) 
  8.       8     4                                                  (object header)                                           9f e1 01 f8 (10011111 11100001 00000001 11111000) (-134094433) 
  9.      12     4                                 java.lang.String UserService.uId                                           (object) 
  10.      16     4                                 java.lang.String UserService.company                                       (object) 
  11.      20     4                                 java.lang.String UserService.location                                      (object) 
  12.      24     4   cn.bugstack.springframework.test.bean.IUserDao UserService.userDao                                       (object) 
  13.      28     1                                          boolean UserService$$EnhancerByCGLIB$$4cabb984.CGLIB$BOUND        true 
  14.      29     3                                                  (alignment/padding gap)                                   
  15.      32     4                          net.sf.cglib.proxy.NoOp UserService$$EnhancerByCGLIB$$4cabb984.CGLIB$CALLBACK_0   (object) 
  16.      36     4                                                  (loss due to the next object alignment) 
  17. Instance size: 40 bytes 
  18. Space losses: 3 bytes internal + 4 bytes external = 7 bytes total 
  19.  
  20.  
  21. Process finished with exit code 0 

5. 单元测试(代理对象)

  1. @Test 
  2. public void test_factory_bean() { 
  3.     // 1.初始化 BeanFactory 
  4.     ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml"); 
  5.     applicationContext.registerShutdownHook();  
  6.  
  7.     // 2. 调用代理方法 
  8.     UserService userService = applicationContext.getBean("userService", UserService.class); 
  9.     System.out.println("测试结果:" + userService.queryUserInfo()); 

关于 FactoryBean 的调用并没有太多不一样,因为所有的不同都已经被 spring.xml 配置进去了。当然你可以直接调用 spring.xml 配置的对象 cn.bugstack.springframework.test.bean.ProxyBeanFactory

测试结果

测试结果:你被代理了 queryUserName:小傅哥,腾讯,深圳

Process finished with exit code 0

六、总结

 

来源:bugstack虫洞栈 内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯