zoukankan      html  css  js  c++  java
  • [Re] Spring-1(bean)

    1. 框架

    • 高度抽取可重用代码的一种设计,有高度的通用性。
    • 框架是多个「可重用模块」的集合(半成品软件),形成一个某个领域的整体解决方案。

    2. Spring 模块

    Spring是一个 IOC(DI) 和 AOP 容器框架 // 容器:管理所有的组件(类)

    • Test:Spring 的单元测试模块
      spring-test-4.0.0.RELEASE.jar
      
    • Core Container:核心容器(IOC)
      # 黑框代表功能的组成 jar,若要使用这部分的完整功能,jar 都需要导入
      spring-aspects-4.0.0.RELEASE.jar
      spring-core-4.0.0.RELEASE.jar
      spring-context-4.0.0.RELEASE.jar
      spring-expression-4.0.0.RELEASE.jar
      
    • AOP + Aspects:面向切面编程模块
      spring-aop-4.0.0.RELEASE.jar
      spring-aspects-4.0.0.RELEASE.jar
      
    • 数据访问/集成:访问数据库模块
      spring-jdbc-4.0.0.RELEASE.jar
      spring-orm-4.0.0.RELEASE.jar     # 对象关系映射
      spring-tx-4.0.0.RELEASE.jar      # 事务
      spring-oxm-4.0.0.RELEASE.jar     # x: xml
      spring-jms-4.0.0.RELEASE.jar
      
    • web:开发 web 应用的模块
      spring-websocket-4.0.0.RELEASE.jar
      spring-web-4.0.0.RELEASE.jar                # Servlet
      spring-webmvc-4.0.0.RELEASE.jar             # web
      spring-webmvc-portlet-4.0.0.RELEASE.jar
      

    3. IOC

    IOC:Inversion(反转) Of Control(控制,即资源的获取方式)

    • 主动式:要什么资源手动创建即可
    • 被动式:由容器(管理所有的组件,即封装功能的类)主动将资源推动给需要的组件,将原来的主动 new 资源变成被动地接受资源。

    DI:Dependency Injection,依赖注入。IOC 是一种反转控制的思想,而 DI 是对 IOC 的一种具体实现。

    4. HelloWorld

    4.1 导包

    Spring 运行依赖以上日志包,没有的话运行会报错。

    4.2 写配置文件

    创建一个源码文件夹 Source Folder → 创建一个 Spring Bean Configuration File,即 Spring 的核心配置文件。// 注意一定是放在源码文件夹,不然根本放不到 bin 目录!

    4.3 编写测试类

    @Test // 从容器中拿到这个组件
    public void test() {
        // ApplicationContext 代表 IOC 容器 = 当前应用的配置文件在类路径下
        ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
        // 容器帮我们创建的对象
        Person person = (Person) ioc.getBean("person01");
        System.out.println(person);
    }
    

    4.4 Tips

    • src 源码包开始的路径,也称为“类路径的开始”;所有源码包的东西都会被合并放在类路径里面。
      • java:/bin/
      • web 类路径:/WEB-INF/classes
    • 需要导入额外的日志包:commons-logging-1.1.3.jar,否则启动将报错
    • 一定要先导包,再创建配置文件
    • Spring 的容器接管了该类对象的标志
    • ServletContext(IOC 容器的接口) 的继承结构
      • ClassPathXmlApplicationContext:IOC 容器的配置文件在类路径下
      • FileSystemXmlApplicationContext:IOC 容器的配置文件在磁盘路径下
    • 给容器中注册一个组件,我们也从容器中按照 id 拿到了这个组件的对象,所有组件的创建默认是在容器启动完成之前,而非是在获取组件时创建
    • 同一个组件在 IOC 容器中是单实例的
      Person person1 = (Person) ioc.getBean("person01");
      Person person2 = (Person) ioc.getBean("person01");
      System.out.println(person1 == person2); // true
      
    • 获取一个容器中没有的组件,抛异常:NoSuchBeanDefinitionException
    • 容器在对组件初始化的时候,是利用 setter 给属性赋值的 // JavaBean 属性名是由 setXxx 的 xxx 决定的

    5. bean 的属性赋值

    5.1 简单属性赋值

    5.1.1 通过 property

    <bean id="person02" class="cn.edu.nuist.bean.Person">
        <property name="lastName" value="李四"></property>
        <property name="age" value="28"></property>
        <property name="email" value="zhangsan@163.com"></property>
        <property name="gender" value="男"></property>
    </bean>
    

    5.1.2 通过 constructor-org

    <!-- 多个重载构造器导致赋值错误情况, 可用 type 指定参数数据类型 -->
    <bean id="person05" class="cn.edu.nuist.bean.Person">
        <constructor-arg value="小花"></constructor-arg>
        <constructor-arg value="18" type="java.lang.Integer"></constructor-arg>
        <constructor-arg value="女"></constructor-arg>
    </bean>
    
    <!--
    可以省略 name 属性,但此时必须严格按照构造器声明的形参顺序赋值,也可用 index 属性指明形参索引
    (String lastName, Integer age, String gender, String email)
    -->
    <bean id="person04" class="cn.edu.nuist.bean.Person">
        <constructor-arg value="小花"></constructor-arg>
        <constructor-arg value="18"></constructor-arg>
        <constructor-arg value="女"></constructor-arg>
        <constructor-arg value="xiaohua@hahaha.com"></constructor-arg>
    </bean>
    
    <bean id="person03" class="cn.edu.nuist.bean.Person">
        <constructor-arg name="lastName" value="小明"></constructor-arg>
        <constructor-arg name="gender" value="男"></constructor-arg>
        <constructor-arg name="age" value="10"></constructor-arg>
        <constructor-arg name="email" value="xiaoming@gmail.com"></constructor-arg>
    </bean>
    

    5.1.3 通过 p 名称空间

    <!--
        通过 p 名称空间为 bean 赋值 // 需要手动导入 p 名称空间
        名称空间:在 XML 中名称空间是用来防止标签重复的
        <book>
            <b:name>西游记</b:name>
            <price>19.9</price>
            <author>
                <a:name>吴承恩</a:name>
                <gender>男</gender>
            </author>
        </book>
    
        带前缀的标签 <c:forEach>, <jsp:forward> ...
    -->
    <bean id="person06" class="cn.edu.nuist.bean.Person"
        p:age="18" p:email="xiaogang@163.com" p:lastName="小强" p:gender="男"
    ></bean>
    

    5.2 复杂属性赋值

    5.2.1 赋值 null

    ioc.xml

    <!-- 如下写法 或者 什么都不写就是 null -->
    <bean id="person01" class="cn.edu.nuist.bean.Person">
        <property name="lastName">
            <null />
        </property>
    </bean>
    

    5.2.2 赋值 bean

    • 引用其他 bean
      <bean id="car01" class="cn.edu.nuist.bean.Car">
          <property name="brand" value="宝马"></property>
          <property name="color" value="黑色"></property>
          <property name="price" value="100000"></property>
      </bean>
      
      <!-- 测试复杂类型赋值 -->
      <bean id="person02" class="cn.edu.nuist.bean.Person">
          <!-- ref 代表该属性是引用一个外面的值 → car = ioc.getBean("car01") -->
          <property name="car" ref="car01"></property>
      </bean>
      
      -----------------------------------------------------
      @Test
      public void test() {
          Person person = ioc.getBean("person02", Person.class);
          Car car = ioc.getBean("car01", Car.class);
          System.out.println(person.getCar() == car); // true
      }
      
    • 引用内部 bean
      <!-- 测试引用内部 bean -->
      <bean id="person03" class="cn.edu.nuist.bean.Person">
          <property name="car">
              <!-- 对象,我们可以使用 bean 标签创建 → car = new Car()-->
              <bean class="cn.edu.nuist.bean.Car"></bean>
          </property>
      </bean>
      -----------------------------------------------------
      @Test
      public void test2() {
          Person person = ioc.getBean("person03", Person.class);
          Car car = ioc.getBean("car01", Car.class);
          System.out.println(person.getCar() == car); // false
      }
      

    5.2.3 赋值 集合bean

    • 赋值 list
      <bean id="outerBook" class="cn.edu.nuist.bean.Book">
          <property name="bookName" value="狐狸在说什么"></property>
          <property name="author" value="韩国人"></property>
      </bean>
      
      <!-- 测试 list -->
      <bean id="person04" class="cn.edu.nuist.bean.Person">
          <property name="books">
              <!-- books = new ArrayList<Book>(); -->
              <list>
                  <!-- list 标签体中添加每一个元素 -->
                  <bean id="innerBook" class="cn.edu.nuist.bean.Book"
                          p:bookName="cmbyn" p:author="美国人"></bean>
                  <!-- 引用外部一个元素 -->
                  <ref bean="outerBook"></ref>
              </list>
          </property>
      </bean>
      
      -----------------------------------------------------
      @Test
      public void test3() {
          Person person = ioc.getBean("person04", Person.class);
          System.out.println(person.getBooks());
          // NoSuchBeanDefinitionException: No bean named 'innerBook' is defined
          // 所以,内部 bean 只能内部使用
          // Book innerBook = ioc.getBean("innerBook", Book.class);
          Book outerBook = ioc.getBean("outerBook", Book.class);
      }
      
    • 赋值 map
      <bean id="person05" class="cn.edu.nuist.bean.Person">
          <!-- map = new LinkedHashMap<>(); -->
          <property name="map">
              <map>
                  <entry key="key01" value="value01"></entry>
                  <entry key="key02" value="value02"></entry>
                  <entry key="key03" value-ref="outerBook"></entry>
                  <entry key="key04">
                      <bean class="cn.edu.nuist.bean.Car">
                          <property name="brand" value="宝马"></property>
                      </bean>
                  </entry>
                  <!--
                  <entry key="key05">
                      <map></map>
                  </entry>
                  -->
              </map>
          </property>
      </bean>
      
    • 赋值 properties
      <bean id="person06" class="cn.edu.nuist.bean.Person">
          <property name="properties">
              <!-- properties = new Properties(); -->
              <props>
                  <!--  因为 k=v 都是 String -->
                  <prop key="username">root</prop>
                  <prop key="password">123</prop>
              </props>
          </property>
      </bean>
      

    5.2.4 util 名称空间

    <bean id="person07" class="cn.edu.nuist.bean.Person">
        <!--  引用 util 名称空间创建的集合类型 bean -->
        <property name="map" ref="myMap"></property>
    </bean>
    
    <!-- new LinkedHashMap(); -->
    <util:map id="myMap">
        <entry key="key01" value="value01"></entry>
        <entry key="key02" value="value02"></entry>
        <entry key="key03" value-ref="outerBook"></entry>
    </util:map>
    
    <util:list id="myList">
        <list></list>
        <bean class="cn.edu.nuist.bean.Person"></bean>
        <value>1101</value>
        <ref bean="myMap"/>
    </util:list>
    

    5.2.5 赋值 级联属性

    <!-- 级联属性赋值:属性的属性 -->
    <bean id="person08" class="cn.edu.nuist.bean.Person">
        <property name="car" ref="car01"></property>
        <!-- 改变 ref-car 的价格 -->
        <property name="car.price" value="9999"></property>
    </bean>
    

    5.3 继承实现配置信息的重用

    <!--
    abstract: 将 person01 设置为不可被获得实例,只能被用于继承的模板 bean
              否则将抛出异常 BeanIsAbstractException: Error creating
              bean with name 'person01': Bean definition is abstract
    -->
    <bean id="person01" class="cn.edu.nuist.bean.Person" abstract="true">
        <property name="lastName" value="张三"></property>
        <property name="age" value="30"></property>
        <property name="email" value="zhangsan@163.com"></property>
        <property name="gender" value="男"></property>
    </bean>
    
    <!-- parent: 指定当前 bean 的配置信息继承于哪个,类型可省 -->
    <bean id="person02" parent="person01">
        <property name="lastName" value="李四"></property>
    </bean>
    

    6. bean 补充

    6.1 bean 之间的依赖

    只是改变 bean 的创建顺序。

    <!--
    默认按照配置顺序创建 bean,如何改变 bean 的创建顺序?
    bean 之间的依赖(只是改变创建顺序) → depends-on 属性
     -->
    <bean id="car01" class="cn.edu.nuist.bean.Car" depends-on="person01, book01"></bean>
    <bean id="person01" class="cn.edu.nuist.bean.Person"></bean>
    <bean id="book01" class="cn.edu.nuist.bean.Book"></bean>
    ----------------
    Person 被创建了
    Book 被创建了
    Car 被创建了
    

    6.2 bean 多实例

    <!--
    测试 bean 的作用域,分别创建单实例和多实例的 bean → 使用 scope 属性
        prototype:多实例,容器启动时不会创建,在 getBean 时才创建对象,每次获取都是新的对象
        singleton:单实例(默认),在容器启动完成之前就已经创建好对象,保存在容器中了
        request:在 web 环境下,同一次请求创建一个 bean 实例(没用)
        session:在 web 环境下,同一次会话创建一个 bean 实例(没用)
    -->
    <bean id="book" class="cn.edu.nuist.bean.Book" scope="prototype"></bean>
    

    6.3 工厂 bean

    bean 的创建默认就是框架利用反射 new 出来的 bean 实例。

    工厂模式:工厂帮我们创建对象,一个专门创建对象的类,这个类就是工厂。

    6.3.1 静态工厂

    静态工厂本身不用创建对象,通过静态方法调用。

    ioc.xml

    <!--
    1. 静态工厂
        class 指定为静态工厂全类名
        factory-method 指定造对象的方法名
        constructor-arg 可以为工厂方法传参
    -->
    <bean id="airplane01" factory-method="getAirplane"
            class="cn.edu.nuist.factory.AirplaneStaticFactory">
        <!-- 可以为方法指定参数 -->
        <constructor-arg value="李四"></constructor-arg>
    </bean>
    

    AirplaneStaticFactory

    public class AirplaneStaticFactory {
        public static Airplane getAirplane(String captain) {
            System.out.println("静态工厂正在造飞机...");
            Airplane ap = new Airplane();
            ap.setCaptain(captain);
            ap.setLengthOfWing("50");
            ap.setNumber("1101");
            return ap;
        }
    }
    

    6.3.2 实例工厂

    工厂本身需要创建对象,然后调用工厂对象的成员方法。

    ioc.xml

    <!-- 2. 实例工厂 -->
    <bean id="airplaneInstanceFactory"
            class="cn.edu.nuist.factory.AirplaneInstanceFactory"></bean>
    
    <!--
    factory-method: 指定这个实例工厂中哪个方法是工厂方法
    factory-bean: 指定创建当前对象是使用的哪个工厂
    -->
    <bean id="airplane02" class="cn.edu.nuist.bean.Airplane"
            factory-bean="airplaneInstanceFactory"
            factory-method="getAirplane">
        <constructor-arg value="王五"></constructor-arg>
    </bean>
    

    AirplaneInstanceFactory:

    public class AirplaneInstanceFactory {
        public Airplane getAirplane(String captain) {
            System.out.println("实例工厂正在造飞机...");
            Airplane ap = new Airplane();
            ap.setCaptain(captain);
            ap.setLengthOfWing("50");
            ap.setNumber("1101");
            return ap;
        }
    }
    

    6.3.3 FactoryBean

    实现了 FactoryBean 接口的类是 Spring 可以认识的工厂类。
    该工厂类返回的 bean 的无论设置的是单实例还是多实例,都是在获取的时候才创建对象。

    public class MyFactoryBean implements FactoryBean<Book> {
    
        @Override
        public Book getObject() throws Exception {
            System.out.println("MyFactoryBean 创建 Book 对象");
            Book book = new Book();
            book.setBookName(UUID.randomUUID().toString());
            return book;
        }
    
        // 返回创建的对象的类型
        @Override
        public Class<?> getObjectType() {
            return Book.class;
        }
    
        @Override
        public boolean isSingleton() {
            return true;
        }
    }
    

    Test

    @Test
    public void test() {
        Object bean = ioc.getBean("myFactoryBean");
        Object bean2 = ioc.getBean("myFactoryBean");
        System.out.println(bean == bean2); // true
    }
    

    6.4 带有生命周期方法的 bean

    <!--
    自定义初始化方法和销毁方法,方法要求没有参数,但可以抛出异常
    init-method, destroy-method
    -->
    <bean id="book01" class="cn.edu.nuist.bean.Book"
            init-method="init" destroy-method="destroy"
            <!-- scope="prototype" -->
    ></bean>
    
    • Test1
    • Test2
    • Test3

    6.5 bean 的后置处理器

    /*
     * 1. 编写后置处理器实现类
     * 2. 将后置处理器注册在配置文件中
     * 注:就算没有定义 init-method,后置处理器也会默认其有,然后继续工作
     */
    public class MyBeanPostProcessor implements BeanPostProcessor {
    
        /**
         * 初始化方法之前调用
         */
        @Override
        public Object postProcessBeforeInitialization(Object bean
                , String beanName) throws BeansException {
            System.out.println(beanName + "BeforeInitialization..." + bean);
            // 返回的是什么,容器中保存的就是什么
            return bean;
        }
    
        /**
         * 初始化方法之后调用
         */
        @Override
        public Object postProcessAfterInitialization(Object bean
                , String beanName) throws BeansException {
            System.out.println(beanName + "AfterInitialization..." + bean);
            return bean;
        }
    }
    

    ioc.xml

    <bean id="book01" class="cn.edu.nuist.bean.Book"
            init-method="init" destroy-method="destroy">
        <property name="bookName" value="狐狸在说什么"></property>
    </bean>
    <bean id="car01" class="cn.edu.nuist.bean.Car"></bean>
    <bean id="beanPostProcessor" class="cn.edu.nuist.bean.MyBeanPostProcessor"></bean>
    

    7. 引入外部属性文件

    • dbconfig.properties
      jdbc.user=root
      jdbc.password=root
      jdbc.jdbcUrl=jdbc:mysql:///test
      jdbc.driverClass=com.mysql.jdbc.Driver
      
    • ioc.xml
      <!-- 引用外部属性文件;"classpath:" 固定写法,引入类路径下资源 -->
      <context:property-placeholder location="classpath:dbconfig.properties"/>
      
      <!-- 数据库连接池作为单实例是最好的,一个项目就一个连接池,连接池里面管理很多连接 -->
      <!-- 可以让 Spring 帮我们创建连接池对象(管理连接池) -->
      <!-- value 属性值中不要放多余的空格,如:"${jdbc.user} " -->
      
      <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
          <property name="user" value="${jdbc.user}"></property>
          <property name="password" value="${jdbc.password}"></property>
          <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
          <property name="driverClass" value="${jdbc.driverClass}"></property>
      </bean>
      
      <!-- username 是 Spring 的 key 中的关键字 → 系统用户名 -->
      <bean id="car01" class="cn.edu.nuist.bean.Car">
          <property name="brand" value="${username}"></property>
      </bean>
      
    • Test
      @Test
      public void test() {
          System.out.println(ioc.getBean("car01", Car.class).getBrand()); // 10646
          DataSource dataSource = ioc.getBean("dataSource", DataSource.class);
          System.out.println(dataSource);
      }
      

    注意!${...} 是取出配置文件中的值,而 #{...} 是 Spring 的表达式语言,别搞错了。

  • 相关阅读:
    集训Day 7 2020.3.7 动态规划(二)
    集训Day 6 2020.3.6 动态规划(一)
    集训Day 5 2020.3.4 杂题选讲(二)
    集训Day 4 2020.3.3 杂题选讲(一)
    集训Day 2 2020.3.1 数论(质数与筛法)
    集训Day 1 2020.2.29 数论复习(gcd)(一)
    [BZOJ4152]The Captain
    知识点清单(全)
    字符串相关知识
    分块相关知识
  • 原文地址:https://www.cnblogs.com/liujiaqi1101/p/13672272.html
Copyright © 2011-2022 走看看