IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> spring的bean实例生成疑问 -> 正文阅读

[Java知识库]spring的bean实例生成疑问

背景

RedisFactoryBean作为一个FactoryBean,用来生成访问redis用的client,可以是主从模式的,也可以是集群模式的。

疑问

如果一个项目里同时配置了redisClient 和redisClusterClient, 那么它们的工厂类RedisFactoryBean 实例会一样吗?
bean的xml配置如下:
   <!-- 测试主从 -->
    <bean id="redisClient" class="com.common.redis.RedisFactoryBean">
        <property name="hostAndPort" value="0.0.0.2:6602"/>
        <property name="password" value="password"/>
        <property name="isCluster" value="false"/>
        <property name="testOnBorrow" value="true"/>
        <property name="testWhileIdle" value="true"/>
        <property name="poolMinEvictableIdleTimeMillis" value="60000"/>
        <property name="poolTimeBetweenEvictionRunsMillis" value="30000"/>
        <property name="poolMinIdle" value="1"/>
        <property name="poolMaxIdle" value="20"/>
        <property name="poolMaxTotal" value="100"/>
        <property name="soTimeout" value="30000" />
    </bean>
 
    <!-- 测试集群 -->
    <bean id="redisClusterClient" class="com.common.redis.RedisFactoryBean">
        <property name="hostAndPort" value="0.0.0.1:6602"/>
        <property name="password" value="password"/>
        <property name="poolMinIdle" value="5"/>
        <property name="poolMaxIdle" value="20"/>
        <property name="poolMaxTotal" value="100"/>
        <property name="soTimeout" value="1000" />
        <property name="maxRedirects" value="5" />
        <property name="testWhileIdle" value="true"/>
        <property name="poolMinEvictableIdleTimeMillis" value="60000"/>
        <property name="poolTimeBetweenEvictionRunsMillis" value="30000"/>
        <property name="isCluster" value="true" />
    </bean>

源码解析

Bean配置信息中, BeanDefinitionParserDelegate类负责解析<bean>元素

// 处理<bean>元素的id、name 和别名属性
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
   // id属性值
   String id = ele.getAttribute(ID_ATTRIBUTE);
   // name属性值
   String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
   // alias属性值
   List<String> aliases = new ArrayList<String>();
   // 将所有name属性值放到别名中
   if (StringUtils.hasLength(nameAttr)) {
      String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
      aliases.addAll(Arrays.asList(nameArr));
   }
 
   String beanName = id;
   if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
      beanName = aliases.remove(0);
      if (logger.isDebugEnabled()) {
         logger.debug("No XML 'id' specified - using '" + beanName +
               "' as bean name and " + aliases + " as aliases");
      }
   }
   // 检查<bean> 元素所配置的id或者name的唯一性
   // containingBean 标识<bean>元素中是否包含子<bean>元素
   if (containingBean == null) {
      checkNameUniqueness(beanName, aliases, ele);
   }
    
   // 配置的bean定义进行解析
   AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
   if (beanDefinition != null) {
      if (!StringUtils.hasText(beanName)) {
         try {
            if (containingBean != null) {
               // 如果<bean>元素中没有配置id、别名或者name, 且没有包含子元素
               // <bean>元素,则为解析的bean生成一个唯一beanName并注册
               beanName = BeanDefinitionReaderUtils.generateBeanName(
                     beanDefinition, this.readerContext.getRegistry(), true);
            }
            else {
               // 如果<bean>元素中没有配置id、别名或者name, 且包含子元素
               // <bean>元素,则为解析的bean使用别名向IOC容器注册
               beanName = this.readerContext.generateBeanName(beanDefinition);
               // 为了解析的Bean使用别名注册时,为了向后兼容
               String beanClassName = beanDefinition.getBeanClassName();
               if (beanClassName != null &&
                     beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                     !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                  aliases.add(beanClassName);
               }
            }
            if (logger.isDebugEnabled()) {
               logger.debug("Neither XML 'id' nor 'name' specified - " +
                     "using generated bean name [" + beanName + "]");
            }
         }
         catch (Exception ex) {
            error(ex.getMessage(), ele);
            return null;
         }
      }
      String[] aliasesArray = StringUtils.toStringArray(aliases);
      return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
   }
 
   return null;
}
BeanDefinitionReaderUtils 向spring IoC容器注册解析的bean
// 将解析的BeanDefinitionHold注册到Spring IOC容器中
public static void registerBeanDefinition(
      BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
      throws BeanDefinitionStoreException {
 
   String beanName = definitionHolder.getBeanName();
   // 向IOC容器注册beanDefinition
   registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
 
   String[] aliases = definitionHolder.getAliases();
   if (aliases != null) {
      for (String alias : aliases) {
         // 向IOC容器注册别名
         registry.registerAlias(beanName, alias);
      }
   }
}

结论

所以,因为它们设置的id 不同, beanName也不同,在ioc容器存储的BeanDefinition也是不同的。依赖注入时,根据beanName从ioc容器获取到不同的BeanDefinition,?那么实例化时redisClient的RedisFactoryBean? 和redisClusterClient的也会是不同的实例。

延伸

那同一个<bean>配置时,RedisFactoryBean什么情况实例会相同,什么情况实例会不同呢?
bean的scope配置默认为单例模式, 其中两种模式区别如下:
单例模式: 提供了具有特定名称的全局共享实例对象,可以在查询时对其进行检索;
原型模式:确定每次检索都会创建单独的实例对象。
那么我们可以分别配置scope为singleton 和 prototype, 测试RedisFactoryBean的生成情况。

当scope="prototype" 时,测试代码如下:

<bean id="redisClient"?scope="prototype"?class="com.common.redis.RedisFactoryBean">
?????<property name="hostAndPort" value="0.0.0.1:6602"/>
?????......

</bean>
@Test
    public void testRedisBeanFactory() throws Exception{
        ApplicationContext ac = new FileSystemXmlApplicationContext("classpath:applicationContext_test.xml");
        RedisFactoryBean redisFactoryBean = (RedisFactoryBean) ac.getBean("&redisClient");
        RedisFactoryBean redisFactoryBean2 = (RedisFactoryBean) ac.getBean("&redisClient");
        Assert.assertTrue(redisFactoryBean == redisFactoryBean2);
    }

测试结果:

java.lang.AssertionError

????at org.junit.Assert.fail(Assert.java:86)

????at org.junit.Assert.assertTrue(Assert.java:41)

????at org.junit.Assert.assertTrue(Assert.java:52)

????at com.RedisTest.testRedisBeanFactory(RedisTest.java:55)

????at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

????at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

????at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

????at java.lang.reflect.Method.invoke(Method.java:498)

????at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)

????at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)

????at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)

????at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)

????at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)

????at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)

????at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)

????at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)

????at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)

????at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)

????at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)

????at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)

????at org.junit.runners.ParentRunner.run(ParentRunner.java:309)

????at org.junit.runner.JUnitCore.run(JUnitCore.java:160)

????at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)

????at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)

????at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)

????at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)

因此,当scope="prototype"?时,?同个beanName, 从ioc容器检索得到的两个实例是不同的。
同样的,当scope="singleton"?时,同个beanName,?从ioc容器检索得到的两个实例是相同的。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-05-03 09:21:59  更:2022-05-03 09:22:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 1:50:37-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码