【Spring学习随笔】3. Spring Bean

3. Spring Bean

3.1 Bean的配置

​ Spring可以看作一个大型工厂,用于生产和管理Spring容器中的Bean。如果要使用这个工厂生产和管理bean,需要开发者将Bean配置在Spring的配置文件中。Spring框架支持XML和Properties两种格式的配置文件,在实际开发中常用XML格式的配置文件。

3.1.1 <bean>元素的常用属性及其子元素

3.1.2 Bean的配置实例代码

<?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
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 使用id属性定义myTestDIDao,其对应的实现类为dao.TestDIDaoImpl-->
    <bean id="myTestDIDao" class="dao.TestDIDaoImpl" />
    <!-- 使用构造方法注入 -->
    <bean id="testDIService" class="service.TestDIServiceImpl">
        <!-- 给构造方法传引用类型的参数值myTestDIDao -->
        <constructor-arg index="0" ref="myTestDIDao"/>
    </bean>
</beans>

3.2 Bean的实例化

​ 在Spring框架中,如果想使用Spring容器中的Bean,需要实例化Bean。Spring框架实例化Bean有3种方式,即构造方法实例化静态工厂实例化、和实例工厂实例化,其中最常见的是构造方法实例化

3.2.1 构造方法实例化

​ 在Spring框架中,Spring容器可以调用Bean对应类中的无参数构造方法来实例化Bean,这种方式称为构造方法实例化。

实例演示:

  • 创建Web应用,在src目录下创建instance包,在包中创建BeanClass类

    package instance;
    public class BeanClass {
        public String message;
        public BeanClass() {
            message = "构造方法实例化Bean";
      }
    
        public BeanClass(String s) {
            message = s;
        }
    }
  • 创建配置文件

    <?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
                               http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <!--    构造方法实例化Bean-->
        <bean id="constructorInstance" class="instance.BeanClass" />
    
    </beans>
  • 创建测试类

    package test;
    
    import instance.BeanClass;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestInstance {
        public static void main(String[] args) {
    //        初始化Spring容器ApplicationContext,加载配置文件
            ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml");
    //        测试构造方法实例化Bean
            BeanClass b1 = (BeanClass) appCon.getBean("constructorInstance");
            System.out.println(b1+b1.message);
        }
    
    }
  • 运行结果

3.2.2 静态工厂实例化

​ 在使用静态工厂实例化Bean时要求开发者在工厂类中创建一个静态方法来创建Bean的实例。在配置Bean时,class属性指定静态工厂类,同时还需要使用factory-method属性指定工厂类中的静态方法

实例演示:

  • 创建工厂类BeanStaticFactory,在instance包中创建工厂类BeanStaticFactory

    package instance;
    
    public class BeanStaticFactory {
        private static BeanClass beanInstance = new BeanClass("调用静态工厂方法实例化Bean");
        public static BeanClass createInstance() {
            return beanInstance;
        }
    
    }
  • 编辑配置文件

    <?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <!--    静态工厂方法实例化Bean,createInstance为静态工厂类BeanStaticFactory中的静态方法-->
        <bean id="staticFactoryInstance" class="instance.BeanStaticFactory" factory-method="createInstance" />
    </beans>
  • 添加测试代码

    package test;
    
    import instance.BeanClass;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestInstance {
        public static void main(String[] args) {
    //        初始化Spring容器ApplicationContext,加载配置文件
            ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml");
    //        测试静态工厂方法实例化Bean
            BeanClass b2 = (BeanClass) appCon.getBean("staticFactoryInstance");
            System.out.println(b2+b2.message);
    
        }
    }
  • 运行效果

3.2.3 实例工厂实例化

​ 在使用实例工厂实例化Bean时要求开发者在工厂类中创建一个实例方法来创建Bean的实例。在配置Bean时需要使用factory-method属性指定配置的实例工厂,同时还需要使用factory-method属性指定实例工厂中的实例方法

  • 创建工厂类BeanStaticFactory

    在instance包中创建工厂类BeanInstanceFactory,该类中有一个实例方法类实例化对象

    package instance;
    
    public class BeanInstanceFactory {
        public BeanClass createBeanClassInstance() {
            return new BeanClass("调用实例工厂方法实例化Bean");
        }
    }
  • 编辑配置文件

    <?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <!--    配置工厂-->
        <bean id="myFactory" class="instance.BeanInstanceFactory" />
    <!--    使用factory-bean属性指定配置工厂,使用factory-method属性指定使用工厂中的哪个方法实例化Bean-->
        <bean id="instanceFactoryInstance" factory-bean="myFactory" factory-method="createBeanClassInstance" />
    </beans>
    
  • 添加测试代码

    package test;
    
    import instance.BeanClass;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestInstance {
        public static void main(String[] args) {
    //        初始化Spring容器ApplicationContext,加载配置文件
            ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml");
    //        测试实例工厂方法实例化Bean
            BeanClass b3 = (BeanClass) appCon.getBean("instanceFactoryInstance");
            System.out.println(b3+b3.message);
    
        }
    }
    
  • 运行效果

3.3 Bean的作用域

3.3.1 在Spring 5.0中为Bean的实例定义的作用域

singletonprototype是最常用的两种,后面4中作用域尽在Web Spring应用程序上下文中使用。

3.3.2 singleton作用域

​ 当bean的scope设置为singleton时,Spring IoC容器仅生成和管理一个Bean实例。在使用id或name获取Bean实例时,IoC容器将会返回共享的Bean实例。

​ singleton时scope的默认方式,有两种方式将bean的scope设置为singleton。示例如下:

<bean id="constructorInstance" class="instance.BeanClass" />

<bean id="constructorInstance" class="instance.BeanClass" scope="singleton"/>

在使用id或name获取Bean实例时,IoC容器仅返回同一个Bean实例。

3.3.3 prototype作用域

​ 当bean的scope设置为prototype时,Spring IoC容器将为每次请求创建一个新的实例。示例如下:

<bean id="constructorInstance" class="instance.BeanClass" scope="prototype"/>

3.4 Bean的生命周期

  • Bean的生命周期过程:

  • 步骤文字描述:
  1. Spring启动,查找并加载需要被Spring管理的bean,进行Bean的实例化。
  2. Bean实例化后对将Bean的引入和值注入到Bean的属性中。
  3. 如果Bean实现了BeanNameAware接口的话,Spring将Bean的Id传递给setBeanName()方法。
  4. 如果Bean实现了BeanFactoryAware接口的话,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入。
  5. 如果Bean实现了ApplicationContextAware接口的话,Spring将调用Bean的setApplicationContext()方法,将bean所在应用上下文引用传入进来。
  6. 如果Bean实现了BeanPostProcessor接口,Spring就将调用他们的postProcessBeforeInitialization()方法。
  7. 如果Bean 实现了InitializingBean接口,Spring将调用他们的afterPropertiesSet()方法。类似的,如果bean使用init-method声明了初始化方法,该方法也会被调用
  8. 如果Bean 实现了BeanPostProcessor接口,Spring就将调用他们的postProcessAfterInitialization()方法。
  9. 此时,Bean已经准备就绪,可以被应用程序使用了。他们将一直驻留在应用上下文中,直到应用上下文被销毁。
  10. 如果bean实现了DisposableBean接口,Spring将调用它的destory()接口方法,同样,如果bean使用了destory-method 声明销毁方法,该方法也会被调用。

实例演示Bean的生命周期:

  • 创建Bean的实现类

    在src目录中创建life包,在life包下创建BeanLife类。在BeanLife类中有两个方法,一个演示初始化方法,一个演示销毁过程。

    package life;
    
    public class BeanLife {
        public void initMyself() {
            System.out.println(this.getClass().getName()+"执行自定义的初始化方法");
        }
    
        public void destoryMyself() {
            System.out.println(this.getClass().getName()+"执行自定义的销毁方法");
        }
    }
    
  • 配置Bean

    在Spring配置文件中使用实现类BeanLife配置一个id为beanLife的Bean。

    <?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <!--    配置bean,使用iniit-method属性指定初始化方法,使用destroy-method属性指定销毁方法-->
        <bean id="beanLife" class="life.BeanLife" init-method="initMyself" destroy-method="destoryMyself" />
    </beans>
    
  • 测试声明周期

    在test包中创建测试类TestLife。

    package test;
    
    import life.BeanLife;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestLife {
        public static void main(String[] args) {
    //        初始化Spring容器,加载配置文件
    //        为了方便演示销毁方法的执行,这里使用ClassPathXmlApplicationContext
    //        实现类声明容器
            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
            System.out.println("获得对象前");
            BeanLife blife = (BeanLife)ctx.getBean("beanLife");
            System.out.println("获得对象后" + blife);
            ctx.close();     //关闭容器,销毁Bean对象
        }
    }
    
  • 运行效果

3.5 Bean的装配方式

Bean的装配可以理解为将bean依赖注入到Spring容器中Bean的装配方式即Bean依赖注入的方式

3.5.1 基于XML配置的装配

​ 在使用构造方法注入方式装配Bean时,Bean的实现类需要提供带参数构造方法,并在配置文件中使用 元素的子元素 来定义构造方法的参数;在使用 属性的setter方法注入方式装配Bean时,Bean的实现类需要提供一个 默认无参数的构造方法,并为需要注入的属性提供对应的 setter方法,另外还需要使用 元素的子元素 为每个属性注入值。

实例演示:

  • 创建Bean的实现类

    在src目录中创建assemble包,在assemble包下创建ComplexUser类。在ComplexUser类中分别使用构造方法注入和使用属性的setter方法注入。

    package assemble;
    
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    
    public class ComplexUser {
        private String uname;
        private List<String> hobbyList;
        private Map<String,String> residenceMap;
        private Set<String> aliasSet;
        private String[] array;
    
        /*
        使用构造方法注入,需要提供带参数的构造方法
         */
        public ComplexUser(String uname, List<String> hobbyList, Map<String, String> residenceMap, Set<String> aliasSet, String[] array) {
            super();
            this.uname = uname;
            this.hobbyList = hobbyList;
            this.residenceMap = residenceMap;
            this.aliasSet = aliasSet;
            this.array = array;
        }
        /**
         * 使用属性的setter方法注入,提供默认无参数的构造方法,并为注入的属性提供setter方法
         */
        public ComplexUser() {
            super();
        }
    
        @Override
        public String toString() {
            return  "uname=" + uname + ";hobbyList=" + hobbyList + ";residenceMap=" + residenceMap + ";aliasSet=" + aliasSet +";array=" + array;
        }
    
        //    此处为所有属性的setter方法
        public void setUname(String uname) {
            this.uname = uname;
        }
    
        public void setHobbyList(List hobbyList) {
            this.hobbyList = hobbyList;
        }
    
        public void setResidenceMap(Map residenceMap) {
            this.residenceMap = residenceMap;
        }
    
        public void setAliasSet(Set aliasSet) {
            this.aliasSet = aliasSet;
        }
    
        public void setArray(String[] array) {
            this.array = array;
        }
    }
    
  • 配置Bean

    在Spring配置文件中使用实现类ComplexUser配置Bean的两个实例。

    <?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
                               http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <!--    使用构造方法注入方式装配ComplexUser实例user1-->
        <bean id="user1" class="assemble.ComplexUser">
            <constructor-arg index="0" value="yihang1" />
            <constructor-arg index="1">
                <list>
                    <value>唱歌</value>
                    <value>跳舞</value>
                    <value>爬山</value>
                </list>
            </constructor-arg>
    
            <constructor-arg index="2">
                <map>
                    <entry key="dalian" value="大连" />
                    <entry key="beijing" value="北京" />
                    <entry key="shanghai" value="上海" />
                </map>
            </constructor-arg>
    
            <constructor-arg index="3">
                <set>
                    <value>逸航01</value>
                    <value>逸航02</value>
                    <value>逸航03</value>
                </set>
            </constructor-arg>
    
            <constructor-arg index="4">
                <array>
                    <value>aaaaa</value>
                    <value>bbbbb</value>
                </array>
            </constructor-arg>
        </bean>
    
    <!--    使用属性的setter方法注入方式装配 ComplexUser实例user2-->
        <bean id="user2" class="assemble.ComplexUser">
            <property name="uname" value="yihang2" />
            <property name="hobbyList">
                <list>
                    <value>看书</value>
                    <value>学习Spring</value>
                </list>
            </property>
    
            <property name="residenceMap" >
                <map>
                    <entry key="shenzhen" value="深圳" />
                    <entry key="guangzhou" value="广州" />
                    <entry key="tianjin" value="天津" />
                </map>
            </property>
    
            <property name="aliasSet">
                <set>
                    <value>逸航04</value>
                    <value>逸航05</value>
                    <value>逸航06</value>
                </set>
            </property>
    
            <property name="array">
                <array>
                    <value>ccccc</value>
                    <value>ddddd</value>
                </array>
            </property>
        </bean>
    </beans>
    
  • 测试基于XML配置的装配方式

    在test包中创建测试类TestAssemble。

    package test;
    
    import assemble.ComplexUser;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestAssemble {
        public static void main(String[] args) {
            ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml");
    //        使用构造方法装配测试
            ComplexUser u1 = (ComplexUser) appCon.getBean("user1");
            System.out.println(u1);
            System.out.println("-------------------------");
    //        使用setter方法装配测试
            ComplexUser u2 = (ComplexUser) appCon.getBean("user2");
            System.out.println(u2);
        }
    }
    
  • 运行效果

3.5.2 基于注解的装配

​ 大量的Bean装配会导致XML配置文件过于庞大,不方便以后的升级与维护,因此更多的时候推荐开发者使用注解(annotation)的方式去装配Bean。

3.5.1.1 @Component

​ 该注解是一个泛化的概念,仅仅表示一个组件对象(Bean),可以作用在任何层次上。

3.5.2.2 @Repository

​ 该注解用于将数据访问层(DAO)的类标识为Bean,即注解数据访问层Bean,其功能与@Component相同。

3.5.2.3 @Service

​ 该注解用于标注一个业务逻辑组件类(Service层),其功能与@Component相同。

3.5.2.4 @Controller

​ 该注解用于标注一个控制器组件类(Spring MVC的Controller),其功能与@Component相同。

3.5.2.5 @Autowired

​ 该注解可以对类成员变量、方法及构造方法进行标注,完成自动装配的工作。通过使用@Autowired来消除setter和getter方法。默认按照Bean的类型进行装配。

3.5.2.6 @Resource

​ 该注解与@Autowired的功能一样,区别在于该注解默认是按照名称来装配注入的,只有当找不到与名称匹配的Bean时才会按照类型来装配注入;而@Autowired默认按照Bean的类型进行装配,如果想按照名称来装配注入,则需要和@Qualifier注解一起使用。

3.5.3.7 @Qualifier

​ 该注解与@Aitowired注解配合使用。当@Autowired注解需要按照名称来装配注入时需要和该注解一起使用,Bean的实例名称由@Qualifier注解的参数制定。

实例演示:

  • 创建DAO层

    在src中创建annotation.dao包,在该包下创建TestDao接口和TestDaoImpl实现类,并将实现类TestDaoImpl使用@Repository注解标注为数据访问层。

    TestDao的代码如下:

    package annotation.dao;
    
    public interface TestDao {
        public void save();
    }
    

    TestDaoImpl的代码如下:

    package annotation.dao;
    
    import org.springframework.stereotype.Repository;
    
    @Repository("testDao")
    /**
     * 相当于@Repository,但如果在service层中使用@Resource(name="testDao"),
     * testDao不能省略
     */
    public class TestDaoImpl implements TestDao {
        @Override
        public void save() {
            System.out.println("testDao save");
        }
    }
    
  • 创建Service层

    在src中创建annotation.service包,在该包下创建TestService接口和TestServiceImpl实现类,并将实现类TestServiceImpl使用@service注解标注为业务逻辑层。

    Testservice的代码如下:

    package annotation.service;
    
    public interface TestService {
        public void save();
    }
    

    TestServiceImpl的代码如下:

    package annotation.service;
    
    import annotation.dao.TestDao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service("testService")  //相当于@Service
    public class TestSerivceImpl implements TestService {
    //    @Resource(name="testDao")
        @Autowired
    //    相当于@Autowired,@Autowired默认按照Bean状态装配
        private TestDao testDao;
    
        @Override
        public void save() {
            testDao.save();
            System.out.println("testService save");
        }
    }
    
  • 创建Controller层

    在src中创建annotation.controller包,在该包下创建TestController类,并将TestController类使用@Controller注解标注为控制器层。

    TestController的代码如下:

    package annotation.controller;
    
    import annotation.service.TestService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    
    @Controller
    public class TestController {
        @Autowired
        private TestService testService;
    
        public void save() {
            testService.save();
            System.out.println("testController save");
        }
    }
    
  • 配置注解

    由于annotation.dao、annotation.service和annotation.controller包都属于annotation包的子包,因此不要在配置文件annotationContext.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"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    
        <context:component-scan base-package="annotation" />
    </beans>
    
  • 创建测试类

    package test;
    
    import annotation.controller.TestController;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestMoreAnnotation {
        public static void main(String[] args) {
            ApplicationContext appCon = new ClassPathXmlApplicationContext("annotationContext.xml");
            TestController testcon = (TestController) appCon.getBean("testController");
            testcon.save();
        }
    }
    
  • 运行效果

猜你喜欢

转载自www.cnblogs.com/yihangjou/p/11929345.html
今日推荐