• 第2章 Spring中的Bean


    2.1 Bean的配置

    Bean本质是Java中的类。Spring可以被看做一个大型工厂,这个工厂的作用就是生产和管理Spring容器zho中的Bean。想在项目中使用这个工厂,就需要对Spring的配置文件进行配置。

    Spring支持XML和Properties两种格式的配置文件。常用XML文件配置,该方式通过XML文件来注册并管理Bean之间的依赖关系。

    XML配置文件的根元素是<beans>,包含了多个<bean>子元素,每个子元素地定义一个Bean。

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

    如果在Bean中未指定id和name,则Spring会把class的值当做id使用。

    getBean( id/name/class );

    2.2 Bean的实例化:

    2.21 构造器实例化:Spring容器通过Bean对应类中默认的无参构造方法来实例化Bean

    package com.itheima.instance.constructor;
    public
    class Bean1 { <?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-4.3.xsd">
        <bean id="bean1" class="com.itheima.instance.constructor.Bean1" />
    </beans 1 package com.itheima.instance.constructor;
    package com.itheima.instance.constructor;
    import org.springframework.context.ApplicationContext;
    import 
        org.springframework.context.support.ClassPathXmlApplicationContext;
    public class InstanceTest1 {
        public static void main(String[] args) {
            // 定义配置文件路径
            String xmlPath = "com/itheima/instance/constructor/beans1.xml";
            // ApplicationContext在加载配置文件时,对Bean进行实例化
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
            Bean1 bean = (Bean1) applicationContext.getBean("bean1");
            System.out.println(bean);
        }
    }

    如果xml文件不在包里,就不需要在路径上加入包名;如果在包里,需要加上包名。

    输出类Bean1的地址:

    com.itheima.instance.constructor.Bean1@234bef66

    2.22静态工厂方式实例化:要求开发者创建一个静态工厂的方法来创建Bean的实例,其Bean配置中的class属性所指定的不再是Bean实例的实现类,而是静态工厂类,同时还需要使用factory-method属性来指定所创建的静态工厂方法。

    package com.itheima.instance.static_factory;
    public class Bean2 {
    }
    package com.itheima.instance.static_factory;
    public class MyBean2Factory {    
        //使用自己的工厂创建Bean2实例
        public static Bean2 createBean222(){
            return new Bean2();
        }
    }
    <?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-4.3.xsd">
        <bean id="bean2" class="com.itheima.instance.static_factory.MyBean2Factory" 
            factory-method="createBean222" />
        <!-- factory-method后面等于的东西是方法 -->
    </beans>
    package com.itheima.instance.static_factory;
    public class MyBean2Factory {    
        //使用自己的工厂创建Bean2实例
        public static Bean2 createBean222(){
            return new Bean2();
        }
    }

    输出类Bean2的地址:

    com.itheima.instance.static_factory.Bean2@7921b0a2

    说白了就是,有一个类叫Bean2,要实例化Bean2,这种方法不直接实例化它,要再创一个工厂类MyBean2Factory,工厂类里有个方法返回实例化的Bean2,return new Bean2(),先new再返回,并且这个方法名和Factory-method对应。

    2.23实例工厂方法实例化:采用实例工厂,不再是静态工厂,采用直接创建Bean实例的方式。

    package com.itheima.instance.factory;
    public class Bean3  {
    }
    package com.itheima.instance.factory;
    public class MyBean3Factory {
        public MyBean3Factory() {
            System.out.println("bean3工厂实例化中");
        }
        //创建Bean3实例的方法
        public Bean3 createBean111(){
            return new Bean3();
        }
    }
    <?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-4.3.xsd">
        <!-- 配置工厂 -->
        <bean id="myBean3Factory" 
                class="com.itheima.instance.factory.MyBean3Factory" />
        <!-- 使用factory-bean属性指向配置的实例工厂,
              使用factory-method属性确定使用工厂中的哪个方法-->
        <bean id="bean3" factory-bean="myBean3Factory" 
               factory-method="createBean111" />
    </beans>
    package com.itheima.instance.factory;
    import org.springframework.context.ApplicationContext;
    import 
        org.springframework.context.support.ClassPathXmlApplicationContext;
    public class InstanceTest3 {
        public static void main(String[] args) {
            // 指定配置文件路径
            String xmlPath = "com/itheima/instance/factory/beans3.xml";
            // ApplicationContext在加载配置文件时,对Bean进行实例化
            ApplicationContext applicationContext = 
                    new ClassPathXmlApplicationContext(xmlPath);
            System.out.println(applicationContext.getBean("bean3"));
            
            //顺便尝试构造器实例化
            String xmlPath2 = "com/itheima/instance/factory/beans3.xml";
            ApplicationContext applicationContext2 = 
                    new ClassPathXmlApplicationContext(xmlPath2);
            System.out.println(applicationContext2.getBean("myBean3Factory"));
        }
    } 

    输出:

    bean3工厂实例化中
    com.itheima.instance.factory.Bean3@7921b0a2

    bean3工厂实例化中
    com.itheima.instance.factory.MyBean3Factory@32d992b2

    这种方式说白了就是:有一个类Bean3,要实例化它,不直接实例化,再造一个工厂类MyBean3Factory来return new Bean3();

    在xml文件中,先配置实例工厂类的bean;再配置要类Bean3的bean,并通过factory-bean指明是哪个实例工厂类(谁)要实例它,再通过factory-method指明是哪个方法来返回实例后的它。

    (回顾旧知识:实例工厂类MyBean3Factory里面有一个无参构造函数,在实例MyBean3Factory的时候自动执行该方法,构造函数要求是无参无返回值,方法名和类名一样)

     

    2.3 Bean的作用域

    Spring 4.3中为Bean的实例定义了7种作用域。

    singleton和prototype最常用,解释如下。

    1.singleton作用域:

    Spring容器默认的作用域,当Bean作用域为singleton时,Spring容器就会存在一个共享的Bean实例,并且所有对Bean的请求,只要id与该Bean的id属性相匹配,就会返回同一个Bean实例。singleton作用域对于无会话状态的Bean(如Dao组件、Service组件)来说,是最理想的选择。演示如下:

    package com.itheima.scope;
    public class Scope  {
    }
    <?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="scope" class="com.itheima.scope.Scope" scope="singleton"/> 
    </beans>
    package com.itheima.scope;
    import org.springframework.context.ApplicationContext;
    import 
        org.springframework.context.support.ClassPathXmlApplicationContext;
    public class ScopeTest {
        public static void main(String[] args) {
            // 定义配置文件路径
            String xmlPath = "com/itheima/scope/beans4.xml";
            // 加载配置文件
            ApplicationContext applicationContext = 
                        new ClassPathXmlApplicationContext(xmlPath);
            // 输出获得实例
            System.out.println(applicationContext.getBean("scope"));
            System.out.println(applicationContext.getBean("scope"));
        }
    }

    输出:

    com.itheima.scope.Scope@234bef66

    com.itheima.scope.Scope@234bef66

    2.prototype作用域:

    对需要保持会话状态的Bean(如Struts2的Action类)应该使用prototype作用域。在使用prototype作用域时,Spring容器会为每个对该Bean的请求都创建一个新的实例。

    xml文件中的<bean>改一下,<bean id="scope" class="com.itheima.scope.Scope" scope="prototype" />

    输出:

    com.itheima.scope.Scope@5a61f5df
    com.itheima.scope.Scope@3551a94

    2.4 Bean的生命周期

    例如singleton和prototype作用域的生命周期。

    singleton关闭时销毁,prototype长时间不用自动销毁。

    Bean生命周期流程图:

    2.5 Bean的装配方式:

    Bean的装配可以理解为依赖关系注入,Bean的装配方式即Bean依赖注入的方式。Spring容器支持多种形式的Bean的装配方式,如基于XML的装配、基于注解(Annotation)的装配和自动装配(其中最常用的是基于注解的装配)。

    2.51 基于XML的装配:设值注入(Setter Injectiion)和构造注入(Constructor Injection)

    构造注入要求:提供默认无参构造方法,为所有属性提供setter方法

    设值注入要求:Bean类必须提供一个默认的无参构造方法,并且为需要注入的属性提供对应的setter方法

    package com.itheima.assemble;
    import java.util.List;
    public class User {
        private String username;
        private Integer password;
        private List<String> list;
        /**
         * 1.使用构造注入 
         * 1.1提供带所有参数的有参构造方法。
         */
        public User(String username, Integer password, List<String> list) {
            super();// 调用父类的无参构造方法,可以不写
            this.username = username;
            this.password = password;
            this.list = list;
        }
        /**
         * 2.使用设值注入 
         * 2.1提供默认空参构造方法 ;
         * 2.2为所有属性提供setter方法。
         */
        public User() {//无参构造方法
            super();//// 调用父类Object的无参构造方法,可以不写
        }
        public void setUsername(String username) {
            this.username = username;
        }
        public void setPassword(Integer password) {
            this.password = password;
        }
        public void setList(List<String> list) {
            this.list = list;
        }
        @Override
        public String toString() { // 重写父类的Object的toString方法
            return "User [username=" + username + ", password=" + password +
                    ", list=" + list + "]";
        }
    }
    <?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-4.3.xsd">
        <!--1.使用构造注入方式装配User实例 -->
        <bean id="user1" class="com.itheima.assemble.User">
            <constructor-arg index="0" value="tom" />
            <constructor-arg index="1" value="123456" />
            <constructor-arg index="2">
                <list>
                    <value>"constructorvalue1"</value>
                    <value>"constructorvalue2"</value>
                </list>
            </constructor-arg>
        </bean>
        <!--2.使用设值注入方式装配User实例 -->
        <bean id="user2" class="com.itheima.assemble.User">
            <property name="username" value="张三"></property>
            <property name="password" value="654321"></property>
            <!-- 注入list集合 -->
            <property name="list">
                <list>
                    <value>"setlistvalue1"</value>
                    <value>"setlistvalue2"</value>
                </list>
            </property>
        </bean>
    </beans>

    构造注入:constructor-arg元素中的index表示的是有参构造方法的参数顺序,value表示注入的值。

    设值注入:property元素中的name表示要注入的属性名,value表示要注入的值,子元素list同样表示要注入的值。

    package com.itheima.assemble;
    import org.springframework.context.ApplicationContext;
    import 
        org.springframework.context.support.ClassPathXmlApplicationContext;
    public class XmlBeanAssembleTest {
        public static void main(String[] args) {
            // 定义配置文件路径
            String xmlPath = "com/itheima/assemble/beans5.xml";
            // 加载配置文件
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
            // 构造方式输出结果
            System.out.println(applicationContext.getBean("user1"));
            // 设值方式输出结果
            System.out.println(applicationContext.getBean("user2"));
        }
    }

    为了能看到结果,重写toString方法。

    输出:

    User [username=tom, password=123456, list=["constructorvalue1", "constructorvalue2"]]
    User [username=张三, password=654321, list=["setlistvalue1", "setlistvalue2"]]

    2.52 基于注解(Annotation)的装配

    XML文件可以实现Bean的装配工作,但是Bean多时,XML文件臃肿巨大,为后续的维护和升级带来困难。为此,提供了对注解技术的全面支持。

     

    package com.itheima.annotation;
    public interface UserDao {
        public void save();
    }
    package com.itheima.annotation;
    import org.springframework.stereotype.Repository;
    
    /**首先使用@Repository注解将UserDaoImpl类标识为Spring中的Bean,相当于配置文件中的
    <bean id="userDao" class="com.itheima.annotation.UserDaoImpl"/> 很普通的配置
    */
    @Repository("userDao") 
    public class UserDaoImpl implements UserDao{
       public void save(){
          System.out.println("userdao...save...");
       }
    }
    package com.itheima.annotation;
    public interface UserService {
        public void save();
    }
    package com.itheima.annotation;
    import javax.annotation.Resource;
    import org.springframework.stereotype.Service;
    
    /**使用@Service注解将UserServiceImpl类标识为Spring中的Bean,相当于
    <bean id="userService" class="com.itheima.annotation.UserServiceImpl"/> 很普通的配置*/
    @Service("userService") 
    
    public class UserServiceImpl implements UserService{
        
        /**使用@Resource注解标注属性uesrDao上,相当于配置文件中的
         <property name="userDao" ref="userDao"/>,
         不是普通的配置,依赖注入,将id为userDao的Bean实例注入到userService实例中
         */
        @Resource(name="userDao") 
        private UserDao userDao;
        public void save() {
             //调用userDao中的save方法
            this.userDao.save();
            System.out.println("userservice....save...");
        }
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    }
    package com.itheima.annotation;
    import javax.annotation.Resource;
    import org.springframework.stereotype.Controller;
    
    /**使用@Controller 注解标注了 UserController类,这相当于配置文件中编写
     <bean id="userController" class="com.itheima.annotation.UserController"/>;普通配置
    */
    @Controller("userController")
    
    public class UserController {
        /**
         @Resource 注解标注在userService属性上,相当于配置文件中编写
         <property name="userService" ref="userService"/>,依赖注入
         */
        @Resource(name="userService")
        private UserService userService;
        public void save(){
            this.userService.save();
            System.out.println("userController...save...");
        }
        public void setUserService(UserService userService) {
            this.userService = userService;
        }
    }
    <?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-4.3.xsd
                http://www.springframework.org/schema/context 
               http://www.springframework.org/schema/context/spring-context-4.3.xsd">
          
         <!-- 凡出现context都是约束信息,使用 context 命名空间 ,在配置文件中开启相应的注解处理器 -->
         <context:annotation-config /> 
        <!--使用 context 命名空间 ,通知Spring扫描指定包下所有Bean类,进行注解解析-->   
         <context:component-scan base-package="com.itheima.annotation" /> 
    </beans>
    package com.itheima.annotation;
    import org.springframework.context.ApplicationContext;
    import 
       org.springframework.context.support.ClassPathXmlApplicationContext;
    public class AnnotationAssembleTest {
        public static void main(String[] args) {
            // 定义配置文件路径
            String xmlPath = "com/itheima/annotation/beans6.xml";
            // 加载配置文件
            ApplicationContext applicationContext = 
                    new ClassPathXmlApplicationContext(xmlPath);
            // 获取UserController实例
            UserController userController = 
              (UserController) applicationContext.getBean("userController");
            // 调用UserController中的save()方法
            userController.save();
        }
    }

    输出:

    userdao...save...
    userservice....save...
    userController...save...

    说白了,先配置类Dao,再把Dao用依赖注入的方式注入到Service,再把Service用依赖注入的方式注入到Controller。注解的配置和注入形式不一样,最后在控制层调用say方法,层层往下,先到服务层,再到数据层,数据层(Dao层)是底层,再层层往上分别调用say方法,因此输出顺序是Dao,Service,Controller。

    (上述案例中用@Autowired注解替换@Resource也可以达到同样效果)

    2.53 自动装配

    注解方式装配一定程度减少配置文件的代码量,但是也有企业项目是没有使用注解方式开发的,自动装配也可以减少代码量。

    Bean元素中包含一个autowire属性,通过属性值来自动装配Bean元素。所谓自动装配,就是将一个Bean自动地注入到其他Bean的Property中。autowire属性有5个值。

    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-4.3.xsd
                http://www.springframework.org/schema/context 
               http://www.springframework.org/schema/context/spring-context-4.3.xsd">
            <!-- 使用bean元素的autowire属性完成自动装配 -->
        <bean id="userDao" class="com.itheima.annotation.UserDaoImpl" />
        <bean id="userService" 
          class="com.itheima.annotation.UserServiceImpl" autowire="byName" />
        <bean id="userController" 
          class="com.itheima.annotation.UserController" autowire="byName"/>
    </beans>

    用于配置userService和userController的<bean>元素中除了id和class属性外,还增加了autowire属性,并将其属性值设置为byName。在默认情况下,配置文件中要通过ref来装配Bean,但设置了autowire=“byName”后,Spring会自动寻找userService Bean中的属性,并将其属性名称与配置文件中定义的Bean做匹配。由于UserServiceImpl中定义了userDao属性及其setter方法,这与配置文件中的id为userDao的Bean相匹配,所以Spring会自动地将id为userDao的Bean装配到id为userService的Bean中。自动装配同样能完成依赖注入。

  • 相关阅读:
    Python中判断字典的键或值在不在字典中
    Python中字符串,列表,元组三者之间相互转换
    mysql /*! */
    【kubernetes入门学习】使用minikube创建k8s本地单节点集群
    Java中发生内存泄漏的常见场景
    python的list()列表数据类型的方法详解
    python的str()字符串类型的方法详解
    python的运算符
    python基础之while语句continue以及break --语法以及案例
    python的if条件语句的语法和案例
  • 原文地址:https://www.cnblogs.com/shoulinniao/p/10884386.html
Copyright © 2020-2023  润新知