美文网首页
Spring -- IOC 基础应用

Spring -- IOC 基础应用

作者: Travis_Wu | 来源:发表于2021-02-16 15:23 被阅读0次

    一、启动 IOC 容器的方式

    • Java环境下启动 IOC 容器
      1. ClassPathXmlApplicationContext:从类的根路径下加载配置文件(推荐使用)
      2. FileSystemXmlApplicationContext:从磁盘路径上加载配置文件
      3. AnnotationConfigApplicationContext:纯注解模式下启动 Spring 容器
    • Web环境下启动 IOC 容器
      1. 从 xml 启动容器
        <!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >
        <web-app>
            <display-name>Archetype Created Web Application</display-name>
            <!--配置Spring ioc容器的配置文件-->
            <context-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:applicationContext.xml</param-value>
            </context-param>
            <!--使用监听器启动Spring的IOC容器-->
            <listener>
                <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
            </listener>
        </web-app>
        
      2. 从配置类启动容器
        <!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >
        <web-app>
            <display-name>Archetype Created Web Application</display-name>
            <!--告诉ContextloaderListener知道我们使用注解的方式启动ioc容器-->
            <context-param>
                <param-name>contextClass</param-name>
                <param-value>org.springframework.web.context.support.AnnotationConfigWebAppli
                             cationContext</param-value>
            </context-param>
           <!--配置启动类的全限定类名-->
            <context-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>com.wujun.edu.SpringConfig</param-value>
            </context-param>
            <!--使用监听器启动Spring的IOC容器-->
            <listener>
                <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
            </listener>
        </web-app>
        

    二、BeanFactory 与 ApplicationContext 区别

    • BeanFactory 是 Spring 框架中 IOC 容器的顶层接口,它只是用来定义⼀些基础功能,定义⼀些基础规范
    • ApplicationContext 是 BeanFactory 的⼀个子接口,所以 ApplicationContext 是具备 BeanFactory 提供的全部功能的
    • 通常,我们称 BeanFactory 为 SpringIOC 的基础容器,ApplicationContext 是容器的高级接口,比 BeanFactory 要拥有更多的功能,比如说国际化支持和资源访问(xml,java配置类)等等


    三、 纯 XML 模式

    3.1 引入相关依赖

    <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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <!--引入Spring IoC容器功能-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.1.12.RELEASE</version>
    </dependency>
    
    <!--引入spring web功能-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>5.1.12.RELEASE</version>
    </dependency>
    

    3.2 启动 IOC 容器

    // JavaSE方式启动
    public class IoCTest {
        @Test
        public void testIoC() throws Exception {
            // 通过读取classpath下的xml文件来启动容器(xml模式SE应用下推荐)
            ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
            // 不推荐使用
            //ApplicationContext applicationContext1 = new FileSystemXmlApplicationContext("文件系统的绝对路径");
            AccountDao accountDao = (AccountDao) applicationContext.getBean("accountDao");
            System.out.println("accountDao:" + accountDao);
       }
    }
    
    private TransferService transferService = null ;
    // web 方式启动
    @Override
    public void init() throws ServletException {
        WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
        transferService = (TransferService) webApplicationContext.getBean("transferService");
    }
    
    <!DOCTYPE web-app PUBLIC
     "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
     "http://java.sun.com/dtd/web-app_2_3.dtd" >
    
    <web-app>
        <display-name>Archetype Created Web Application</display-name>
        <!--配置Spring ioc容器的配置文件-->
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </context-param>
        <!--使用监听器启动Spring的IOC容器-->
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
    </web-app>
    

    3.3 实例化 Bean 的三种方式

    • 使用无参构造函数(推荐)
      在默认情况下,它会通过反射调用无参构造函数来创建对象,如果类中没有无参构造函数,将创建失败
      <!--配置service对象-->
      <bean id="userService" class="com.wujun.service.impl.TransferServiceImpl">
      </bean>
      
    • 使用静态方法创建
      1. 在实际开发中,我们使用的对象有些时候并不是直接通过构造函数就可以创建出来的,它可能在创建的过程中会做很多额外的操作,此时会提供⼀个创建对象的方法,恰好这个方法是 static 修饰的方法
      2. 例如,我们在做 JDBC 操作时,会用到 java.sql.Connection 接口的实现类,如果是 mysql 数据库,那么用的就是 JDBC4Connection,但是我们不会去写JDBC4Connection connection = new JDBC4Connection()因为我们要注册驱动,还要提供 URL 和凭证信息,用DriverManager.getConnection方法来获取连接
      3. 那么在实际开发中,尤其早期的项目没有使用 Spring 框架来管理对象的创建,但是在设计时使用了工厂模式解耦,那么当接入 Spring 之后,工厂类创建对象就具有和上述例子相同特征,即可采用此种方式配置
      public class CreateBeanFactory {
          public static ConnectionUtils getInstanceStatic() {
              return new ConnectionUtils();
          }
      }
      
      <!--使用静态方法创建对象的配置方式-->
      <bean id="connectionUtils" class="com.wujun.edu.factory.CreateBeanFactory" factory-method="getInstanceStatic"/>
      
    • 使用实例化方法创建
      此种方式和上面静态方法创建其实类似,区别是用于获取对象的方法不再是 static 修饰的了,而是类中的一个普通方法,此种方式比静态方法创建的使用几率要高⼀些,
      在早期开发的项目中,工厂类中的方法有可能是静态的,也有可能是非静态方法,当是非静态方法时,即可采用下面的配置方式
      public class CreateBeanFactory {
      
          public static ConnectionUtils getInstanceStatic() {
              return new ConnectionUtils();
          }
      
          public ConnectionUtils getInstance() {
              return new ConnectionUtils();
          }
      }
      
      <!--方式三:实例化方法-->
      <bean id="createBeanFactory" class="com.lagou.edu.factory.CreateBeanFactory"></bean>
      <bean id="connectionUtils" factory-bean="createBeanFactory" factory-method="getInstance"/>
      

    3.4 Bean 的作用范围及生命周期

    • 作用范围的改变
      在spring框架管理 Bean 对象的创建时,Bean 对象默认都是单例的,但是它支持配置的方式改变作用范围,我们实际开发中用到最多的作用范围就是 singleton(单例模式)和 prototype(原型模式,也叫多例模式),配置方式参考下面的代码
      <!--配置service对象-->
      <bean id="transferService" class="com.wujun.service.impl.TransferServiceImpl" scope="singleton">
      </bean>
      
    • 不同作用范围的生命周期
      1. 单例模式:singleton(单例模式的 bean 对象生命周期与容器相同)
        对象出生:当创建容器时,对象就被创建了
        对象活着:只要容器在,对象⼀直活着
        对象死亡:当销毁容器时,对象就被销毁了
      2. 多例模式:prototype(多例模式的 bean 对象,spring 框架只负责创建,不负责销毁)
        对象出生:当使用对象时,创建新的对象实例
        对象活着:只要对象在使用中,就⼀直活着
        对象死亡:当对象长时间不用时,被 java 的垃圾回收器回收了

    3.5 Bean 标签属性

    在基于 xml 的 IOC 配置中,bean 标签是最基础的标签,它表示了 IOC 容器中的⼀个对象,换句话说,如果⼀个对象想让 Spring 管理,在 XML 的配置中都需要使用此标签配置,Bean标签的属性如下:

    • id
      用于给 bean 提供⼀个唯⼀标识,在⼀个标签内部,标识必须唯⼀
    • class属性
      用于指定创建 Bean 对象的全限定类名
    • name属性
      用于给 bean 提供⼀个或多个名称,多个名称用空格分隔
    • factory-bean属性
      用于指定创建当前 bean 对象的工厂 bean 的唯⼀标识,当指定了此属性之后,class 属性失效
    • factory-method
      用于指定创建当前 bean 对象的工厂方法,如配合 factory-bean 属性使用,则 class 属性失效,如配合 class 属性使用,则方法必须是 static 的
    • scope
      用于指定 bean 对象的作用范围,通常情况下就是 singleton,当要用到多例模式时,
      可以配置为 prototype
    • init-method
      用于指定 bean 对象的初始化方法,此方法会在 bean 对象装配后调用,必须是⼀个无参方法
    • destory-method
      用于指定 bean 对象的销毁方法,此方法会在 bean 对象销毁前执行,它只能为 scope 是 singleton 时起作用

    3.6 DI 依赖注入的 xml 配置

    public class JdbcAccountDaoImpl implements AccountDao {
    
        private ConnectionUtils connectionUtils;
        private String name;
        private int sex;
        private float money;
    
        public void setConnectionUtils(ConnectionUtils connectionUtils) {
            this.connectionUtils = connectionUtils;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public void setSex(int sex) {
            this.sex = sex;
        }
    
        public void setMoney(float money) {
            this.money = money;
        }
    
        public JdbcAccountDaoImpl(ConnectionUtils connectionUtils, String name, int sex, float money) {
            this.connectionUtils = connectionUtils;
            this.name = name;
            this.sex = sex;
            this.money = money;
        }
    
        private String[] myArray;
        private Map<String,String> myMap;
        private Set<String> mySet;
        private Properties myProperties;
    
        public void setMyArray(String[] myArray) {
            this.myArray = myArray;
        }
    
        public void setMyMap(Map<String, String> myMap) {
            this.myMap = myMap;
        }
    
        public void setMySet(Set<String> mySet) {
            this.mySet = mySet;
        }
    
        public void setMyProperties(Properties myProperties) {
            this.myProperties = myProperties;
        }
    }
    
    <bean id="accountDao" class="com.wujun.edu.dao.impl.JdbcTemplateDaoImpl" scope="singleton" init-method="init" destroy-method="destory">
            
            <!--set注入使用property标签,如果注入的是另外一个bean那么使用ref属性,如果注入的是普通值那么使用的是value属性-->
            <property name="ConnectionUtils" ref="connectionUtils"/>
            <property name="name" value="zhangsan"/>
            <property name="sex" value="1"/>
            <property name="money" value="100.3"/>
    
            <constructor-arg index="0" ref="connectionUtils"/>
            <constructor-arg index="1" value="zhangsan"/>
            <constructor-arg index="2" value="1"/>
            <constructor-arg index="3" value="100.5"/>
    
            <!--name:按照参数名称注入,index按照参数索引位置注入-->
            <constructor-arg name="connectionUtils" ref="connectionUtils"/>
            <constructor-arg name="name" value="zhangsan"/>
            <constructor-arg name="sex" value="1"/>
            <constructor-arg name="money" value="100.6"/>
    
            <!--set注入注入复杂数据类型-->
            <property name="myArray">
                <array>
                    <value>array1</value>
                    <value>array2</value>
                    <value>array3</value>
                </array>
            </property>
    
            <property name="myMap">
                <map>
                    <entry key="key1" value="value1"/>
                    <entry key="key2" value="value2"/>
                </map>
            </property>
    
            <property name="mySet">
                <set>
                    <value>set1</value>
                    <value>set2</value>
                </set>
            </property>
    
            <property name="myProperties">
                <props>
                    <prop key="prop1">value1</prop>
                    <prop key="prop2">value2</prop>
                </props>
            </property>
    
        </bean>
    

    四、 XML与注解相结合模式

    4.1 注意

    • 实际企业开发中,纯 xml 模式使用已经很少了
    • 引入注解功能,不需要引入额外的 jar
    • xml + 注解结合模式,xml 文件依然存在,所以,spring IOC 容器的启动仍然从加载 xml 开始
    • 哪些 bean 的定义写在 xml 中,哪些 bean 的定义使用注解
      1. 第三方 jar 中的 bean 定义在 xml,比如德鲁伊数据库连接池
      2. 自己开发的 bean 定义使用注解

    4.2 xml 中标签与注解的对应(IOC)

    xml 形式 对应的注解形式
    标签 (1)@Component("accountDao"),注解加在类上
    (2)bean 的 id 属性内容直接配置在注解后面如果不配置,默认定义个这个 bean 的 id 为类的类名首字母小写
    (3)针对分层代码开发提供了 @Componenet 的三种别名 @Controller、@Service、@Repository 分别用于控制层类、服务层类、dao层类的 bean 定义,这四个注解的用法完全⼀样,只是为了更清晰的区分而已
    标签的 scope 属性 @Scope("prototype"),默认单例,注解加在类上
    标签的 init-method 属性 @PostConstruct,注解加在方法上,该方法就是初始化后调用的方法
    标签的 destory-method 属性 @PreDestory,注解加在方法上,该方法就是销毁前调用的方法

    4.3 DI 依赖注入的注解实现方式

    • @Autowired(推荐使用)
      1. Spring 提供的注解,需要导入包 org.springframework.beans.factory.annotation.Autowired
      2. 采取的策略为按照类型注入,这样会产生⼀个问题,当⼀个类型有多个 bean 值的时候,会造成无法选择具体注入哪⼀个的情况,这个时候我们需要配合 @Qualifier 一起使用
        @Autowired
        @Qualifier(name="jdbcAccountDaoImpl") 
        private AccountDao accountDao;
        
    • @Resource
      1. 由 J2EE 提供,需要导入包 javax.annotation.Resource
      2. 默认按照 ByName 自动注入
         @Resource 
         private AccountDao accountDao;
         @Resource(name="studentDao") 
         private StudentDao studentDao;
         @Resource(type="TeacherDao") 
         private TeacherDao teacherDao;
         @Resource(name="manDao",type="ManDao") 
         private ManDao manDao;
        
        • 如果同时指定了 name 和 type,则从 Spring 上下文中找到唯⼀匹配的 bean 进行装配,找不到则抛出异常
        • 如果指定了 name,则从上下文中查找名称(id)匹配的 bean 进行装配,找不到则抛出异常
        • 如果指定了 type,则从上下文中找到类似匹配的唯⼀ bean 进行装配,找不到或是找到多个,都会抛出异常。
        • 如果既没有指定 name,又没有指定 type,则自动按照 byName 方式进行装配
      3. 注意:@Resource 在 Jdk 11中已经移除,如果要使用,需要单独引入 jar 包
        <dependency>
           <groupId>javax.annotation</groupId>
           <artifactId>javax.annotation-api</artifactId>
           <version>1.3.2</version>
        </dependency>
        

    五、纯注解模式

    改造 xml + 注解模式,将 xml 中遗留的内容全部以注解的形式迁移出去,最终删除xml,从 Java 配置类启动
    对应注解
    @Configuration 注解,表名当前类是⼀个配置类
    @ComponentScan 注解,替代 context:component-scan
    @PropertySource,引入外部属性配置文件
    @Import 引入其他配置类
    @Value 对变量赋值,可以直接赋值,也可以使用 ${} 读取资源配置文件中的信息
    @Bean 将方法返回对象加入 SpringIOC 容器

    相关文章

      网友评论

          本文标题:Spring -- IOC 基础应用

          本文链接:https://www.haomeiwen.com/subject/cfivxltx.html