Spring学习总结 (1)

IOC的使用(1)

一、IOC概述

  1. IOC:控制反转(Inversion Of Control),是一个容器

  2. 容器:管理所有的组件(类、对象等),使用组件时无需去new资源,而是由容器创建和管理组件, 使用组件时直接从容器中获取即可

二、使用IOC容器获取组件并为其属性赋值

  1. 创建一个Java Project,创建包,创建Student类
public class Student {
   private String lastName;
   private Integer age;
   private String gender;
   private String email;
   
   //构造器、getter/setter、toString结构
}
  1. 导包

项目名右键新建一个lib文件夹,jar包右键add to build path

在这里插入图片描述

  1. 写配置

Spring的xml配置文件中集合了Spring的IOC容器管理的所有组件:

在这里插入图片描述

创建出一个xml文件,自定义命名为ioc.xml,文件内容如下:

   <!-- 在容器中注册一个Student对象,容器会自动创建该对象,无需手动去new -->
      <!--
       一个bean标签可以注册一个组件(对象、类等)
       class属性写要注册的组件所在类的全类名
       id属性写这个组件的唯一标识,自定义标识名称
   -->
   
   <bean class="com.qizegao.test.Student" id="Student01">
       
       <!--
          bean标签中使用property标签为Student对象的属性赋值
             每给一个属性赋值就要写一个property标签
          name属性写要赋值的属性名
          value属性写赋予的值
       -->
       
       <property name="lastName" value="周杰伦"></property> 
       <property name="age" value="18"></property>
       <property name="email" value="@qq.com"></property>
       <property name="gender" value=""></property>
       
   </bean>
   
   <!-- 另一个Student对象 -->
   <bean id="Student02" class="com.qizegao.test.Student">
       <property name="lastName" value="昆凌"></property>
   </bean>

  1. 测试

包名右键

在这里插入图片描述

public class IOCTest {
   /**
    * 从容器中拿到这个组件(Student对象)
    */
   @Test
   public void test() {
       //ApplicationContext表示IOC容器的接口   
       //ClassPathXmlApplicationContext:当前应用的xml配置文件的位置
              //根据Spring的配置文件得到IOC容器对象
       ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
       
       //容器已经创建好了Student对象
       //使用容器对象的getBean方法获取组件(Student对象),参数中是bean标签的id属性值
       Student bean = (Student) ioc.getBean("Student01");
       System.out.println(bean); 
//Student [lastName=周杰伦, age=18, gender=男, email=@qq.com]
       
       //再获取一个Student对象
       Student bean2 = (Student) ioc.getBean("Student01");
       System.out.println(bean == bean2); //true
   }
}
  1. 注意

(1) src目录是源码包开始的路径,称为类路径的开始

(2) Spring的容器接管了图标标志了s的类:

(3) new ClassPathXmlApplicationContext:
Spring的xml配置文件在src目录下(与包同等级)时使用

(4)new FileSystemXmlApplicationContext:
Spring的xml配置文件在别的位置时使用

(5) 工程名右键new Source Folder,此文件夹的作用与src目录一致

(6) 同一个组件在IOC容器中默认是单例的,是容器创建好时创建好的,即多次从调用容器中 调用同一个组件得到的都是同一个对象,地址值相同

(7) 容器中没有所要获取的组件时报错:NoSuchBeanDefinitionException

(8) 容器中使用property标签时会调用setter方法为属性赋值

(9) property标签的name属性由getter/setter方法的方法名决定:方法名的set前缀去掉,剩 余字符串首字母小写就是name属性值,并不是定义时的属性名

(10) 在容器中创建组件对象时默认调用的是无参构造器

三、根据bean的类型从IOC容器中获取bean的实例

   @Test
   public void test2() {
       ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
       Student s1 = ioc.getBean(Student.class); //不用类型转换
       /**
        * 1. getBean方法的参数是Class对象时,如果xml文件中此类型的组件有多个时就会报错:
        * expected single matching bean but found 2
        *
        * 2. 如果容器中一个类有多个组件时,只能使用参数为id属性值的getBean方法
        *
        * 3. getBean方法的参数是Class对象时,得到的结果不用类型转换
        *
        * 4. getBean方法的参数是Class对象时,会获取到此类的所有实现类(子类)
        */
   }

四、通过构造器为bean的属性赋值

  <bean id="Student04" class="com.qizegao.test.Student">
       <!-- 调用有参构造器创建对象并为其属性赋值 -->
       <!-- public Student(String lastName, Integer age, String gender, String email) -->
       <!-- 有几个参数写几个 constructor-arg 标签,name属性对应参数名,value属性对应赋予的值 -->
       
       <constructor-arg name="lastName" value="郭富城"></constructor-arg>
       <constructor-arg name="email" value="@qq.com"></constructor-arg>
       <constructor-arg name="age" value="22"></constructor-arg>
       <constructor-arg name="gender" value=""></constructor-arg>
   </bean>
   <bean id="Student05" class="com.qizegao.test.Student">
       <!--
          1. xml文件的constructor-arg标签 可以不使用name属性,但要求这些标签的顺序必须与构造器中参数的顺序一致
          1. 构造器重载时,可以在 constructor-arg标签 中使用type属性指定参数的类型
          2. 可以在 constructor-arg标签 中使用index属性指定构造器中参数的索引,从0开始
        -->
       
       <!-- public Student(String lastName, String age, String gender, String email) -->
       <!-- public Student(String lastName, Integer age, String gender, String email) -->
       
       <constructor-arg value="郭富城"></constructor-arg>
       <constructor-arg value="22" index="1" type="java.lang.Integer"></constructor-arg>
       <constructor-arg value=""></constructor-arg>
       <constructor-arg value="@qq.com"></constructor-arg>
   </bean>

五、为类中各种类型的属性赋值

  1. xml中如果没有给属性赋值,则属性使用默认值:引用类型为null值,基本数据类型为对应的默 认值,如果定义类时给属性赋予了默认值,则容器中的对象默认值为初始时赋予的

  2. xml中编写的内容都是文本格式,使用property标签赋值时,会自动的进行类型转换(把文本格式 转换为对应的属性的类型),如:String类型的属性在xml中使用property标签进行赋值:value = “null”,赋予的是字符串null,并不是null值

  3. 给属性赋予特殊值时,应该在property标签中进行赋值,而不是使用value属性赋值

  4. 给属性赋予null值

<property name="lastName">
     <null /> <!-- 赋予null值的标签  -->
</property>
  1. 给类类型的属性赋值

(1) 如果在容器中已经定义了要赋值的属性所在类的组件

在property标签中使用ref属性可以引用容器中已经存在的其他组件,值为其id值
注:ref属性表示的是引用,代表地址值相同

(2) 如果在容器中没有定义要赋值的属性所在类的组件

<bean id="Student09" class="com.qizegao.test.Student">
       <property name="book">
          <!-- 可以使用bean标签创建Book对象:引用内部bean -->
          <bean class="com.qizegao.test.Book">
             <property name="author" value="桂纶镁"></property>
          </bean>
       </property>
</bean>
<!-- 内部bean不可以被getBean方法获取,只能内部使用,所以一般不写id属性 -->
  1. 给List集合类型的属性赋值
   <property name="books"> <!-- books为List集合类型的属性名 -->
      <list> <!-- list标签体中添加每一个元素-->
        
         <!-- 第一个元素 -->
         <bean class="com.qizegao.bean.Book">
             <property name="bookName" value="西游记"></property>
         </bean>
             
         <!-- 第二个元素,引用外部一个元素 -->
         <ref bean="book01" /> <!-- book01是xml中另一个组件对象 -->
     
      </list>
   </property>
  1. 给Map集合类型的属性赋值
   <property name="maps"> <!-- maps是Map类型的属性名 -->
      <map> <!-- 在map标签体中编写键值对 -->
         <!-- 一个entry代表一个键值对 -->
         <entry key="key01" value="张三"></entry>
         <entry key="key02" value="18"></entry>
         <entry key="key03" value-ref="book01"></entry> <!-- 引用其他组件 -->
         <entry key="key04"> <!-- 使用内部bean -->
             <bean class="com.qizegao.bean.Car">
                <property name="carName" value="宝马"></property>
             </bean>
         </entry>
         <entry key="key05">
             <value>李四</value>
         </entry>
      </map>
   </property>

  1. 给Properties类型的属性赋值
<!-- 为Properties类型的属性赋值,所有的key、value都是String型的 -->
   <!-- Student类中添加属性Properties properties -->
   <bean id="Student10" class="com.qizegao.test.Student">
       <property name="properties">
          <props>
             <!-- Properties的value值写在prop标签中 -->
             <prop key="username">root</prop>
             <prop key="password">gaoqize</prop>
          </props>
       </property>
   </bean>

  1. 给级联属性赋值
    Student类中声明了Book book,Book是另一个类,其中有属性price,则price为Student的级联属性,对其赋值时,name标签的内容是book.price

六、创建各种类型的bean

1. 使用util名称空间创建集合类型的bean

名称空间:在xml文件中名称空间是用来防止标签重复的

使用方法:
在这里插入图片描述

2. 通过继承实现bean配置信息的重用

   <!-- parent属性指定当前的配置信息继承于哪个组件 -->
   <!--
       1. 仅仅指的是配置信息的继承,并不代表是类的继承,即二者没有子父类的关系
       2. 相同的配置信息不用写,需要修改的配置信息写出
    -->
   <bean id="Student11" parent="Student01">
       <property name="lastName" value="李刚"></property>
       <!-- 其余三项与Student01相同 -->
   </bean>

3. 通过abstract属性创建模板bean

在bean标签中使用属性abstract = “true”,表示此组件只可以被其他组件继承配置信息,无法 被获取,如果对此组件执行getBean,会报错:BeanIsAbstractException

4. 创建单实例和多实例的bean

   <!-- scope属性可指定此bean为单实例或多实例

       1. singleton:单实例(不使用scope属性默认的形式):
          (1) 创建容器时创建此组件,保存在容器中
          (2) 每次获取的都是已经创建好的组件(同一个)
          
       2. prototype:多实例:
          (1) 创建容器时不会创建此组件
          (2) 获取此组件时才创建此组件
          (3) 每次获取的都是一个新的对象
   -->

   <bean id="Student12" class="com.qizegao.test.Student" scope="prototype"></bean>

5. 通过静态工厂方法创建bean

工厂模式:一个类专门用来创建对象,这个类就是工厂

静态工厂:不需要创建工厂本身,直接调用静态工厂的静态方法创建对象:
bean对象 = 工厂类. 静态方法()

(1) 创建一个静态工厂类

public class StaticFactory {
   //工厂方法
   public static Student getBean(String lastName) {
       Student student = new Student(lastName);
       student.setAge(12);
       return student;
   }
  }

(2) 在xml文件中创建bean

   <!--
       静态工厂使用步骤:
         1. 在bean标签中class属性写工厂类的全类名,使用factory - method属性指明工厂方法
         2. constructor - arg标签为工厂方法传参
    -->
    <bean id="Student13" class="com.qizegao.test.StaticFactory" factory-method="getBean">
      <constructor-arg value="周杰伦"></constructor-arg>
    </bean>

(3) 测试

    public void test() {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
        Student student =  (Student)ioc.getBean("Student13");
        System.out.println(student); //Student [lastName=周杰伦, gender=null, age=12]
    }

6. 通过实例工厂方法创建bean

实例工厂:需要创建工厂对象,调用对象中的方法创建对象:
工厂对象 = new 工厂类(),bean对象 = 工厂对象. 工厂方法()

(1) 创建一个实例工厂类

    public class InstanceFactory {
   //工厂方法
   public Student getBean(String lastName) {
       Student student = new Student(lastName);
       student.setGender(0);
       return student;
   }
 }

(2) 在xml文件中创建bean

    <!--
       实例工厂使用步骤:
         1. 创建一个实例工厂类的组件(对象)
     -->
     <bean id="instancefactory" class="com.qizegao.test.InstanceFactory"></bean>
    
     <!-- 2. 创建需要的对象:
            (1) class属性不是工厂类
            (2) factory-bean:指定使用哪个实例工厂类        
            (3) factory-method:指定工厂方法
     -->
     <bean id="Student14" class="com.qizegao.test.Student"
            factory-bean="instancefactory" factory-method="getBean">
       <constructor-arg value="李刚"></constructor-arg>    
     </bean>

(3) 测试

   public void test() {
       ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
       Student student =  (Student)ioc.getBean("Student14");
       System.out.println(student); //Student [lastName=李刚, gender=0, age=null]
   }

7. 通过实现FactoryBean创建bean

FactoryBean是Spring的一个接口,此接口中有三个抽象方法,只要是这个接口的实现类,Spring 都认为是一个工厂:

i. Spring会自动调用该工厂的工厂方法创建实例
ii. 该工厂创建的组件(实例),ioc容器创建时不会创建此组件
iii. 获取组件时才创建组件(对象),不论FactoryBean的实现类创建的对象是否为单例

(1) 创建一个实现了FactoryBean的工厂类

 public class ImplFactoryBean implements FactoryBean<Student>{
 
   //工厂方法,返回创建的对象
   @Override
   public Student getObject() throws Exception {
       Student student = new Student();
       return student;
   }
   
   //返回创建的对象的类型
   //Spring会自动的调用这个方法来确定创建的对象是什么类型
   @Override
   public Class<?> getObjectType() {
       return Student.class;
   }
  
   //创建出的对象是否为单例
   @Override
   public boolean isSingleton() {
       return true;
   }
 }

(2) 在xml中创建bean

<bean id="factorybeanimpl" class="com.qizegao.test.ImplFactoryBean"></bean>

(3) 测试

    public void test() {
       ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
       Student student1 =  (Student)ioc.getBean("factorybeanimpl");
       Student student2 =  (Student)ioc.getBean("factorybeanimpl");
       System.out.println(student1 == student2); //true
     }

8. 创建带有生命周期方法的bean

可以为bean自定义一些生命周期方法,Spring在创建或者销毁bean时会自动调用指定的方法, 这些生命周期方法必须无参,但可抛出任何异常

(1) 在Student类中添加两个方法

   //初始化方法
   public void init() {
       System.out.println("执行了初始化方法");
   }
   
   //销毁方法
   public void destory() {
       System.out.println("执行了销毁方法");
   }

(2) 在xml配置文件中编写

 <bean id="Student15" class="com.qizegao.test.Student"
        init-method="init" destroy-method="destory"></bean>

(3) 测试

   public void test1() {
       //此类型有close方法
       ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
       ioc.close();
       /**
        * 运行结果:
        * 执行了初始化方法
        * 执行了销毁方法
        */
   }

注意:
(1) 单例bean的生命周期:(容器创建)调用构造器 → 调用初始化方法 → (容器关闭)调用 销毁方法
(2) 多实例bean的生命周期:(获取bean)调用构造器 → 调用初始化方法 → (容器关闭) 不调用bean的销毁方法

七、bean的后置处理器

Spring有一个接口:BeanPostProcessor,即后置处理器,其中有两个抽象方法,分别可以在bean的 初始化前后自动的调用

(1) 编写后置处理器的实现类

   public class ImplBeanPostProcessor implements BeanPostProcessor {
  
    /**
    * bean初始化之前调用
    * 参数1:将要初始化的bean
    * 参数2:xml文件中配置的id
    */
   @Override
   public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
       System.out.println("初始化之前调用的方法");
       return bean;
   }

   /**
    * bean初始化之后调用
    * 参数1:初始化的bean
    * 参数2:xml文件中配置的id
    */
   @Override
   public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
       System.out.println("初始化之后调用的方法");
       return bean;
   }
}

(2) 将后置处理器注册在配置文件中

<bean id="beanpostprocessor" class="com.qizegao.test.ImplBeanPostProcessor"></bean>

(3) 测试

  public void test1() {
       ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
       ioc.close();
       /**
        * 运行结果:
        * 初始化之前调用的方法
        * 执行了初始化方法
        * 初始化之后调用的方法
        * 执行了销毁方法
        */
   }

注意:

  1. 初始化之前、之后执行与有没有初始化方法并无关系
  2. xml文件中一旦使用了此实现类的配置语句,此xml文件中所有的组件都会执行初始化之前、 之后的方法
  3. 单实例后置处理器执行过程:(容器创建)调用构造器 → 初始化之前执行的方法 → 初始化 方法 → 初始化之后执行的方法 → (容器销毁)调用销毁方法
  4. 初始化之前调用的方法返回null,则此组件在初始化之前就已经成为了null,初始化之后调 用的方法返回null,则此组件初始化之后就会成为null
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Nice2cu_Code

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值