• Spring Beans初始化


    Spring Framework框架中,各个对象都被当为Bean来进行管理,具体由IoC容器来进行初始化、存储及销毁等。

    IoC容器内部,这类Bean具体由BeanDefinition来表示,该对象定义了如下信息:

    • 全路径类名
    • Bean的行为特性(如:scope范围、生命周期回调等)
    • Bean的依赖信息
    • 其它配置信息

    1. Bean的定义

    Spring Framework框架中,每一个Bean都有一个或多个唯一的标识。可以使用传统的XML的形式配置Bean,也可以使用Java Config的形式来配置Bean

    XML的元信息配置中,可以使用idname来标识Bean。其中,name可以设置多个值,用,;或空格隔开。不指定idname时,Spring会生成一个默认的标识。

    自动生成Bean的唯一标识,遵循Java标准规则,即使用java.beans.Introspector#decapitalize方法,若前两个字母都是大写,则不会变换,否则,将首字母小写

    2. 初始化Bean

    Bean的初始化主要有以下三种方式:

    • 构造方法
    • 静态工厂方法
    • 工厂方法

    首先,来看构造器初始化的方式
    新建maven工程,并引入Spring Framework相关依赖,如下:

        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
            <springFramework.version>5.2.0.RELEASE</springFramework.version>
        </properties>
    
         <dependencies>
                <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-context</artifactId>
                    <version>${springFramework.version}</version>
                </dependency>
                <dependency>
                    <groupId>junit</groupId>
                    <artifactId>junit</artifactId>
                    <version>4.13.2</version>
                    <scope>test</scope>
                </dependency>
         </dependencies>
    

    引入spring-context模块实际会引入Spring Framework好几个模块,如:spring-corespring-beansspring-aopspring-expression等。

    2.1 构造器方式

    首先来看XML的形式,创建一个普通的PoJo对象,如下:

    package com.vielat.springframework.bean.service;
    
    /**
     * TODO
     *
     * @author Michael
     * @since 2021/12/3
     */
    public class ConstructorClientService {
    
    }
    

    src/main/resources目录下新建META-INF/applicationContext.xml文件,内容如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           https://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="service" class="com.vielat.springframework.bean.service.ConstructorClientService" />
    
    </beans>
    

    src/test/java目录下编写Junit测试类,如下:

    public class XmlApplicationDemo {
    
        @Test
        public void testConstructorInitializing(){
            ApplicationContext ac = new ClassPathXmlApplicationContext("META-INF/applicationContext.xml");
            System.out.println(ac.getBean("service"));
        }
    }
    

    运行Junit测试类后,执行结果如下:

    
    com.vielat.springframework.bean.service.ConstructorClientService@77ec78b9
    
    Process finished with exit code 0
    

    若为该PoJo添加有参的构造函数,如下:

    public class ConstructorClientService {
    
        public ConstructorClientService(String name){
    
        }
    }
    

    再次执行Junit测试类,则会抛出如下异常:

    十二月 03, 2021 7:57:00 上午 org.springframework.context.support.AbstractApplicationContext refresh
    警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'service' defined in class path resource [META-INF/applicationContext.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.vielat.springframework.bean.service.ConstructorClientService]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.vielat.springframework.bean.service.ConstructorClientService.<init>()
    
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'service' defined in class path resource [META-INF/applicationContext.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.vielat.springframework.bean.service.ConstructorClientService]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.vielat.springframework.bean.service.ConstructorClientService.<init>()
    
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1320)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1214)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
    	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
    	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:879)
    	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878)
    	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
    	at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:144)
    	at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:85)
    	at com.vielat.springframework.bean.XmlApplicationDemo.testConstructorInitializing(XmlApplicationDemo.java:27)
    	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:59)
    	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
    	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
    	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
    	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
    	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
    	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
    	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
    	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
    	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
    	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
    	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
    	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    	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)
    Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.vielat.springframework.bean.service.ConstructorClientService]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.vielat.springframework.bean.service.ConstructorClientService.<init>()
    	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:83)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1312)
    	... 38 more
    Caused by: java.lang.NoSuchMethodException: com.vielat.springframework.bean.service.ConstructorClientService.<init>()
    	at java.lang.Class.getConstructor0(Class.java:3082)
    	at java.lang.Class.getDeclaredConstructor(Class.java:2178)
    	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:78)
    	... 39 more
    

    由上述日志可以看到,在XML中的bean标签若只配置id和class属性(其中id属性可以省略),会直接使用class对应对象的无参构造器进行初始化,若该对象没有提供无参构造器,则会抛出异常。若想使用带参数的构造器进行初始化,需要在XML配置中进行相应的配置,如下:

    ...
      <bean id="service" class="com.vielat.springframework.bean.service.ConstructorClientService">
            <constructor-arg name="name" value="Michael" />
      </bean>
    

    上述配置,除了使用name字段属性,还可以使用index下标的方式进行指定。
    除此之外,在Spring Framework 3.1之后,还提供一种更加简化的命名空间的形式,如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:c="http://www.springframework.org/schema/c"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           https://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="service" class="com.vielat.springframework.bean.service.ConstructorClientService">
            <constructor-arg name="name" value="Michael" />
        </bean>
    
        <bean id="service2" class="com.vielat.springframework.bean.service.ConstructorClientService" c:name="Michael">
        </bean>
    
    </beans>
    

    需要在beans签名上新增xmlns:c="http://www.springframework.org/schema/c",在指定构造参数时可以直接配置为c:属性名的形式。

    2.2 静态工厂方式

    编写一个静态工厂类,如下:

    public class ClientService {
    
        private static final ClientService service = new ClientService();
    
        public static ClientService getInstance() {
            return service;
        }
    
        public ClientService getService(){
            return service;
        }
    }
    

    XML配置信息如下:

     <bean name="clientService" class="com.vielat.springframework.bean.service.ClientService" factory-method="getInstance" />
    

    测试类如下:

        @Test
        public void testStaticFactoryInitializing(){
            ApplicationContext ac = new ClassPathXmlApplicationContext("META-INF/application.xml");
            System.out.println(ac.getBean("clientService"));
        }
    

    2.3 工厂方法

    与静态工厂方法类似,也是通过FactoryBean来初始化对象,只不过初始化方法没有static进行修饰。
    工厂方法定义如下:

    public class DefaultServiceLocator {
        public ClientService createClientService() {
            return new ClientService();
        }
    }
    

    XML定义如下:

      <bean id="defaultServiceLocator" class="com.vielat.springframework.bean.service.DefaultServiceLocator"/>
      <bean id="serviceLocator" factory-bean="defaultServiceLocator" factory-method="createClientService" />
    

    测试类如下:

        @Test
        public void testFactoryInitializing() {
            ApplicationContext ac = new ClassPathXmlApplicationContext("META-INF/applicationContext.xml");
            System.out.println(ac.getBean("serviceLocator"));
        }
    

    无论是静态工厂还是普通工厂类,均可以初始化多个Bean,只需在工厂类中定义多个方法即可,代码略。这类工厂即被称之为FactoryBean

  • 相关阅读:
    毕业设计:参考文献(3)
    毕业设计:参考文献(5)
    毕业设计:参考文献(1)
    毕业设计:参考文献(7)
    毕业设计:参考文献(2)
    2021ICPC沈阳站游记
    2021CCPC广州站游记
    常用linux 脚本
    Jenkins调优实践
    linux工具安装
  • 原文地址:https://www.cnblogs.com/vielat/p/15631571.html
Copyright © 2020-2023  润新知