Spring IOC配置与使用

前言

​ 上篇文章简单介绍了IOC, 本文则是重点讲述如何使用Spring5里的IOC进行Bean管理, 有两种方式, 分别是基于xml文件和注解, 我们都会一一讲到。

BeanFactory接口

IOC容器的底层就是对象工厂

Spring提供了IOC容器的两种实现方式(两个接口):

  • BeanFactory: IOC容器基本实现, 是Spring内部使用的接口, 不提供给开发人员使用, 加载文件的时候不会船舰对象, 在获取对象的时候才会创建对象

  • ApplicationContext: BeanFactory接口的子接口, 提供更多更强大的功能, 加载配置文件的时候就会把在配置文件中配置好的对象进行创建

    ApplicationContext的四个常用实现类:

    • **FileSystemXmlApplicationContext:**加载配置文件的时候采用的是项目的路径, 也就是绝对路径。
    • **ClassPathXmlApplicationContext:**加载配置文件的时候根据ClassPath位置, 也就是项目相对路径。
    • **XmlWebApplicationContext:**在Web环境下初始化监听器的时候会加载该类。
    • **AnnotationConfigApplicationContext:**根据注解的方式启动Spring 容器。

SpringDI的方式

​ Spring提供了三种方式来依赖注入,有构造方法注入, setter方法注入以及接口注入。其中Spring以往推荐使用Setter的方法现在改成推荐构造方法注入。使用构造方法注入需要注意的一点就是要避免循环依赖。所谓的循环依赖指的就是在A对象的构造方法中Spring要注入B,而在B对象中Spring要注入A。这个时候会形成一个闭环因为Spring不知道该先注入哪一个接着会抛出异常。而Spring建议的处理方式是说如果遇到这种情况的话就改用Setter方式注入。

Bean管理(基于xml)

基于xml创建对象:

  • 在Spring配置文件中, 使用bean标签创建对象
  • 在bean标签中有多个属性, 常用的有:
    • id: 唯一标识, 用于获取对象
    • class: 类的全路径
  • 创建对象的时候, 默认的是执行无参构造方法完成对象创建
<!--配置User对象的创建-->
<bean id="user" class="com.ayu.User"></bean>

基于xml方式注入属性:

  • 使用set方法进行注入

    在bean标签内使用property标签完成属性注入:

    name: 类中属性名称

    value: 属性中注入的值

    <!--使用set方法进行参数注入-->
    <bean id="user1" class="com.ayu.User">
    	<property name="name" value="Tom"></property>
    	<property name="age" value="11"></property>
    </bean>
    
  • 使用有参构造器进行注入

    在bean标签内使用constructor-arg标签完成属性注入:

    name: 类中属性名称

    value: 属性中注入的值

    <!--使用有参构造方法进行参数注入-->
    <bean id="user2" class="com.ayu.User">
    	<constructor-arg name="name" value="Jerry"></constructor-arg>
    	<constructor-arg name="age" value="11"></constructor-arg>
    </bean>
    

xml注入其他属性:

  • 字面量

    • null值

      使用标签

      <!--null值-->
      <property name="name">
          <null/>
      </property>
      
    • 属性值包含特殊符号

      使用CDATA

      <!--特殊字符-->
      <property name="name">
          <value>
          	<![CDATA[<<时生>>]]>
          </value>
      </property>
      
  • 注入外部bean

    使用ref属性注入外部创建好的bean对象

    <!--外部bean-->
    <bean id="userService" class="com.ayu.service.UserService">
            <property name="userDao" ref="userDao"></property>
    </bean>
    
    <bean id="userDao" class="com.ayu.dao.UserDao"></bean>
    
  • 注入内部bean

    直接在property标签里创建bean标签

    <!--内部bean-->
    <bean id="emp" class="com.ayu.bean.Emp">
        <property name="name" value="张三"></property>
        <property name="dept">
            <bean id="dept" class="com.ayu.bean.Dept">
                <property name="deptNo" value="101"></property>
            </bean>
        </property>
    </bean>
    
  • 级联赋值

    对属性里的bean对象的属性进行赋值操作

    <!--级联赋值-->
    <bean id="emp1" class="com.ayu.bean.Emp">
        <property name="name" value="张三"></property>
        <property name="dept" ref="dept1"></property>
        <property name="dept.deptNo" value="102"></property>
    </bean>
    <bean id="dept1" class="com.ayu.bean.Dept"></bean>
    

xml注入集合属性:

  • array标签对数组进行赋值
  • list标签对List集合进行赋值
  • set标签对Set集合进行赋值
  • map标签对Map集合进行赋值
<bean id="stu" class="com.ayu.collection.Student">
<property name="courses">
    <array>
        <value>语文</value>
        <value>数学</value>
        <value>英语</value>
    </array>
</property>
<property name="list">
    <list>
        <value>java</value>
        <value>go</value>
        <value>c++</value>
    </list>
</property>
<property name="map">
    <map>
        <entry key="java" value="88"></entry>
        <entry key="c++" value="78"></entry>
    </map>
</property>
<property name="set">
    <set>
        <value>MySQL</value>
        <value>SQLServer</value>
    </set>
</property>
<property name="courseList">
    <list>
        <ref bean="course1"></ref>
        <ref bean="course2"></ref>
    </list>
</property>
</bean>

<bean id="course1" class="com.ayu.collection.Course">
	<property name="name" value="Spring5"></property>
</bean>
<bean id="course2" class="com.ayu.collection.Course">
	<property name="name" value="SpringMVC"></property>
</bean>

使用util标签共享bean:

<!-- 配置命名空间 -->
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans 
                    http://www.springframework.org/schema/beans/spring-beans.xsd
                    http://www.springframework.org/schema/util
                    http://www.springframework.org/schema/util/spring-util-2.0.xsd">

<!-- 使用util标签来配置共享的bean-->
<util:list id="bookList">
    <value>大话数据结构</value>
    <value>程序是怎样跑起来的</value>
    <value>操作系统导论</value>
</util:list>

<bean id="book" class="com.ayu.collection.Book">
    <property name="list" ref="bookList"></property>
</bean>

使用p命名空间配置bean:

<!-- 配置命名空间 -->
xmlns:p="http://www.springframework.org/schema/p"

<!--使用p命名空间来配置bean-->
<bean id="user" class="com.ayu.User" p:name="Tom" p:age="11"></bean>

Bean作用域:

Spring中可以使用scope属性来配置bean的作用域:

  • singleton: 单例, 在初始化配置文件时生成单例bean对象**(默认)**
  • prototype: 原型的, 在初始化配置文件时不生成bean对象, 使用时返回不同的bean对象
  • request: web环境下每一个request请求都会返回一个不同的bean, 只在本次请求中有效
  • session: web环境下每一个request请求都会返回一个不同的bean, 在session中有效
<!--设置scope属性-->
<bean id="user" class="com.ayu.User" scope="singleton"></bean>

Bean生命周期:

  1. 通过构造方法生成bean的实例
  2. 为bean注入属性
  3. 调用初始化方法**(通过init-method属性配置)**
  4. bean的使用
  5. IOC容器关闭时,调用销毁方法**(通过destroy-method属性配置)**

创建一个测试用的Order类:

public class Order {
    private String name;
    
    public Order() {
        System.out.println("1.无参构造");
    }
    
    public void setName(String name) {
        this.name = name;
        System.out.println("2.set方法");
    }
    
    public void initMethod() {
        System.out.println("3.初始化方法");
    }

    public void destroyMethod() {
        System.out.println("5.销毁方法");
    }
    
    @Test
    public void testOrder() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
        Order order = context.getBean("order", Order.class);
        System.out.println("4.获取到bean对象");
        System.out.println(order);
        ((ClassPathXmlApplicationContext) context).close();
    }
}

配置文件:

<bean id="order" class="com.ayu.bean.Order" init-method="initMethod" destroy-method="destroyMethod">
    <property name="name" value="手机"></property>
</bean>

测试结果:

1.无参构造
2.set方法
3.初始化方法
4.获取到bean对象
com.ayu.bean.Order@69b0fd6f
5.销毁方法

Bean的后置处理器:

使用后置处理器后的生命周期:

  1. 通过构造方法生成bean的实例
  2. 为bean注入属性
  3. 将bean传给后置处理器的postProcessBeforeInitialization方法
  4. 调用初始化方法**(通过init-method属性配置)**
  5. 将bean传给后置处理器的postProcessAfterInitialization方法
  6. bean的使用
  7. IOC容器关闭时,调用销毁方法**(通过destroy-method属性配置)**

创建一个后置处理器类实现BeanPostProcessor接口:

public class MyBeanPost implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化之前");
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化之后");
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}

在配置文件中配置BeanPostProcessor:

<bean id="myBeanPost" class="com.ayu.bean.MyBeanPost"></bean>

测试结果:

1.无参构造
2.set方法
初始化之前
3.初始化方法
初始化之后
4.获取到bean对象
com.ayu.bean.Order@66d1af89
5.销毁方法

Bean的自动装配

bean标签的autowire属性实现自动装配, autowire有两个常用的值:

  • byName: 根据属性名称注入, 注入值bean的id值和类属性值名称需一样

    <bean id="emp" class="com.ayu.autowrite.Emp" autowire="byName">
        <!--<property name="dept" ref="dept"></property>-->
    </bean>
    <bean id="dept" class="com.ayu.autowrite.Dept"></bean>
    
  • byType: 根据属性类型注入

    <bean id="emp" class="com.ayu.autowrite.Emp" autowire="byType">
        <!--<property name="dept" ref="dept"></property>-->
    </bean>
    <bean id="dept" class="com.ayu.autowrite.Dept"></bean>
    

    注: 通过byType自动装配,已配置的bean中有多个该类型的bean时会报错

Bean管理(基于注解)

Spring针对bean管理提供的注解

下面四个注解功能是一样的,都可以用来创建 bean 实例

  • @Controller: 控制器, 推荐给controller层添加此注解。
  • @Service: 业务逻辑, 推荐给业务逻辑层添加此注解。
  • @Repository: 仓库管理, 推荐给数据访问层添加此注解。
  • @Component: 给不属于以上基层的组件添加此注解。

基于注解实现对象创建

  1. 开启组件扫描:

    如果扫面多个包, 可以用逗号隔开, 或是扫描包的上层目录

    <context:component-scan base-package="com.ayu"></context:component-scan>
    
  2. 创建类, 在类上添加对象注解

    //value属性可以不写, 会默认类名首字母小写为value的值
    @Repository(value="userDao")
    public class UserDaoImpl implements UserDao{
        @Override
        public void hello() {
            System.out.println("hello dao");
        }
    }
    

开启组件扫描细节配置

<!--示例一:use-default-filters表示现在不使用默认filter,自己配置filter
	content:include-filter 设置扫面那些内容-->
<content:component-scan base-package="com.ayu" use-default-filters="false">
    <content:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</content:component-scan>
<!--示例二:下面配置扫面包所有内容
	content:exclude-filter 设置哪些内容不进行扫描-->
<content:component-scan base-package="com.ayu" use-default-filters="false">
    <content:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</content:component-scan>

基于注解实现属性注入

  • @Autowired: 根据属性类型进行自动装配

    @Service
    public class UserService {
    
        //不需要set方法
        @Autowired
        private UserDao userDaO;
    
        public void hello() {
            System.out.println("hello service");
            userDaO.hello();
        }
    }
    
  • @Qualifier: 根据属性名称进行注入, 通常和@Autowired一起使用

    @Service
    public class UserService {
    
        //不需要set方法
        //区别同一接口下多个实现类
        @Autowired
        @Qualifier(value="userDaoImpl")
        private UserDao userDaO;
    
        public void hello() {
            System.out.println("hello service");
            userDaO.hello();
        }
    }
    
  • @Resource: 可以根据类型注入,也可以根据名称注入(这个注解是JDK提供的)

    @Resource(value="userDaoImpl")
    private UserDao userDaO;
    
  • @Value: 注入普通类型属性

    @Value(value = "service")
    private String name;
    

完全注解开发

创建配置类, 代替xml配置文件

@Configuration
@ComponentScan(basePackages = {"com.ayu"})
public class SpringConfig {}

此时应使用AnnotationConfigApplicationContext这个实现类:

//AnnotationConfigApplicationContext需要将配置类传给它
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = context.getBean("userService", UserService.class);
userService.hello();

最后

我的个人主页: www.ayu.link
本文连接: ┏ (゜ω゜)=☞

上一篇:撸了谷歌内部写的Spring源码笔记后,感觉之前读的都是ZZ


下一篇:Spring的IOC常用注解(含源码)