• Spring(3)使用 spring 的IOC解决程序耦合


    一、环境搭建

      此处拿账户的业务层和持久层的依赖关系举例(简单的模拟,不涉及增删改查操作)解决它们之间的依赖。 

    1.创建Java的maven工程

    (1)选择File → New → MavenProject 开始创建Maven项目

     (2)选择要创建的Maven项目原型

    (3)输入创建Maven项目所必须的参数

    2.测试代码编写

    (1)引入需要的jar包,修改pom.xml

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
    
      <groupId>com.xhbjava</groupId>
      <artifactId>Spring02</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <packaging>jar</packaging>
    
      <name>Spring02</name>
      <url>http://maven.apache.org</url>
    
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      </properties>
    
     <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.0.2.RELEASE</version>
            </dependency>
        </dependencies>
    </project>

    (2)创建业务层接口类和实现类

    package com.xhbjava.service;
    
    /**
     * 账户的业务层接口
     * 
     * @author mr.wang
     *
     */
    public interface IAccountService {
        /**
         * 保存账户
         */
        void saveAccount();
    
    }
    package com.xhbjava.service.impl;
    
    import com.xhbjava.dao.IAccoutDao;
    import com.xhbjava.dao.impl.AccountDaoImpl;
    import com.xhbjava.service.IAccountService;
    /**
     * 账户业务层接口实现类
     * @author mr.wang
     *
     */
    public class AccountServiceImpl implements IAccountService {
        //此处依赖有待解决
        private IAccoutDao accountDao = new AccountDaoImpl();
        public void saveAccount() {
            accountDao.saveAccount();
        }
    
    }

    (3)创建账户的持久层接口和实现类

    package com.xhbjava.dao;
    
    /**
     * 账户持久层接口
     * 
     * @author mr.wang
     *
     */
    public interface IAccoutDao {
        /**
         * 保存账户
         */
        void saveAccount();
    
    }
    package com.xhbjava.dao.impl;
    
    import com.xhbjava.dao.IAccoutDao;
    
    /**
     * 用户持久层接口实现类
     * 
     * @author mr.wang
     *
     */
    public class AccountDaoImpl implements IAccoutDao {
        /**
         * 保存账户
         */
        public void saveAccount() {
            System.out.println("保存了账户");
        }
    
    }

    二、基于XML配置

    1.工程根目录创建bean.xml(该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"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        
    </beans>

    2.让Spring管理资源,在配置文件中配置Service和Dao

    <?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="accountService" class="com.xhbjava.service.impl.AccountServiceImpl"></bean>
    <bean id="accountDao" class="com.xhbjava.dao.impl.AccountDaoImpl"></bean>
        
    </beans>

    3.编写测试类进程测试

    package com.xhbjava.test;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.xhbjava.dao.IAccoutDao;
    import com.xhbjava.service.IAccountService;
    
    public class testSpring {
        
        public static void main(String args[]) {
            //1.使用ApplicationContest接口获取srping容器
            ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
            //2.根据beanid获取对象
            IAccountService accountService = (IAccountService) ac.getBean("accountService");
            System.out.println(accountService);
            
            IAccoutDao accountDao = (IAccoutDao) ac.getBean("accountDao");
            System.out.println(accountDao);
            
        }
    
    }

     三、Spring基于XML的IOC细节

    1.Spring 中工厂的类结构

     BeanFactory 是Srping容器中顶层的接口(适用于多例模式),ApplicationContext是其子接口(适用于单例模式)。

     (1)BeanFactory 和 和 ApplicationContext 的区别

      创建对象时间点不一样:

      BeanFactory:什么使用什么时候创建对象。

      ApplicationContext:只要一读取配置文件,默认情况下就会创建对象。

    (2)ApplicationContext 接口的实现类

      ClassPathXmlApplicationContext :它是从类的根路径下加载配置文件 推荐使用这种

      FileSystemXmlApplicationContext :它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。

      AnnotationConfigApplicationContext:当我们使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。
    2.
    IOC中bean标签和管理对象细节

    (1)bean标签

      作用:用于配置对象让Spring来创建。默认情况下它调用的是类中的无参构造函数。如果没有无参构造函数则不能创建成功。

    (2)bean标签属性介绍

    <bean 
         --给对象在容器中提供一个唯一标识。用于获取对象。
        id="accountDao"
        --指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数
        class="com.xhbjava.dao.impl.AccountDaoImpl"
         -- 指定对象的作用范围:取值及其含义如下
            -- singleton: 默认值,单例的【常用】
            -- prototype: 多例的
            -- request: web项目中,Spring创建一个Bean对象,将对象存入request域中,请求范围
            -- session: web项目中,Spring创建一个Bean对象,将对象存入session域中,会话范围
            -- golbal-session: web项目中,【应用在Protlet环境,集群环境】,如果没有Protlet
                     环境,那么golbalSession相当于session
        scope="prototype"
         -- 指定类中的初始化方法的名称
        init-method="init"
        -- 延迟加载,默认false
        lazy-init="true"
        -- 指定类中销毁方法的名称
        destroy-method="destory"
        
        ></bean>

    (3)bean 的作用范围和生命周期

           单例对象:属性scope="singleton"

    一个应用只有一个对象的实例。作用范围就是整个引用

    • 生命周期
      • 对象出生:当应用加载,创建容器时,对象就被创建了。
      • 对象活着:容器在,对象在
      • 对象拜拜:应用卸载、容器销毁时。

          多例对象:属性scope="prototype"

    每次访问对象时,都会重新创建对象实例;销毁方法就不会被执行了!控制权交给GC了

    • 生命周期
      • 对象出生:当使用对象时
      • 对象活着:对象在被使用中
      • 对象拜拜:长时间不使用,被java的垃圾回收机制GC回收了

    3. 实例化Bean的三种方式

    (1)使用默认无参构造函数

      在默认情况下,会根据默认无参构造函数来创建类对象,如果没有默认无参构造函数,将会创建失败。

    <bean id="accountService"    class="com.xhbjava.service.impl.AccountServiceImpl"></bean>

    (2) spring管理【静态】工厂-使用静态工厂的方式创建对象【使用jar包中的类】

       创建模拟工厂类:

    package com.xhbjava.factory;
    
    import com.xhbjava.service.IAccountService;
    import com.xhbjava.service.impl.AccountServiceImpl;
    /**
     * 模拟静态工厂,创建业务层实现类
     * @author mr.wang
     *
     */
    public class StaticFactory {
        public static IAccountService createAccountService() {
            return new AccountServiceImpl();
        }
    
    }

    修改bean.xml:

    使用 StaticFactory 类中的静态方法 createAccountService 创建对象,并存入 spring 容器
    id 属性:指定 bean 的 id,用于从容器中获取
    class 属性:指定静态工厂的全限定类名
    factory-method 属性:指定生产对象的静态方法

        <bean id="accountService" class="com.xhbjava.factory.StaticFactory" factory-method="createAccountService"></bean>

    (3)spring 管理实例工厂- 使用实例工厂的方法创建对象

      创建实例工厂类:

      

    /**
    * 模拟一个实例工厂,创建业务层实现类
    * 此工厂创建对象,必须现有工厂实例对象,再调用方法
    */
    public class InstanceFactory {
    public IAccountService createAccountService(){
    return new AccountServiceImpl();
    }
    }

       修改bean.xml:

      该方式是先把工厂的创建交给 spring 来管理。然后在使用工厂的 bean 来调用里面的方法。
      factory-bean 属性:用于指定实例工厂 bean 的 id。
      factory-method 属性:用于指定实例工厂中创建对象的方法。

    <bean id="instancFactory" class="om.xhbjava.factory.InstanceFactory"></bean>
    <bean id="accountService" factory-bean="instancFactory" factory-method="createAccountService"></bean>

    4.Spring的依赖注入

    (1)概念

      依赖注入:Dependency Injection。它是 spring 框架核心 ioc 的具体实现。

      通过前面学习我们了解了控制反转,通过控制反转我们将创建对象的任务交给了Spring,我们的代码中不会没有依赖的情况,通过ioc解耦只是降低各个代码之间的依赖关系,但是不会消除。在使用了Spring之后我们处理依赖关系就让Spring来维护。

      在实际环境中实现IoC容器的方式主要有两类,一类是依赖查找,即通过资源定位把对应的资源找回来;另一类则是依赖注入,Spring主要使用的就是依赖注入。一般而言依赖注入分为以下3中方式:

    • 构造器(构造函数)注入
    • setter注入
    • 接口注入

      构造器注入和setter注入是主要的方式,而接口注入是从别的地方注入的方式,比如web工程中配置数据源通常是通过服务器去配置,这个时候可以用JNDI的形式通过接口将它注入Spring IOC容器中。

    (2)构造函数注入

      就是通过类中的构造函数给成员变量赋值,这个赋值操是通过配置的方式由Spring框架来进行注入。

      具体代码如下:

      类中需要提供一个对应参数列表的构造函数。

    package com.xhbjava.pojo;
    
    import java.util.Date;
    
    public class Account {
        private String name;
        private String age;
        private Date birthday;
        
        public Account(String name, String age, Date birthday) {
            this.name = name;
            this.age = age;
            this.birthday = birthday;
        }
    
        @Override
        public String toString() {
            return "Account [name=" + name + ", age=" + age + ", birthday=" + birthday + "]";
        }
    
    }
    <?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="account" class="com.xhbjava.pojo.Account">
    <constructor-arg name="name" value="张三"></constructor-arg>
    <constructor-arg name="age" value="17"></constructor-arg>
    <constructor-arg name="birthday" ref="now"></constructor-arg>
    </bean>
    <bean id="now" class="java.util.Date"></bean>
    </beans>

      涉及标签的属性:

      constructor-arg

        index:指定参数在构造函数参数列表的索引位置

        type:指定参数在构造函数中的数据类型

        name:指定参数在构造函数中的名称

        value:它能赋的值是基本数据类型和 String 类型

        ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean

    测试类测试:

    package com.xhbjava.test;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.xhbjava.pojo.Account;
    
    public class testSpring {
        
        public static void main(String args[]) {
            //1.使用ApplicationContest接口获取srping容器
            ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
            //2.根据beanid获取对象
            Account account = (Account) ac.getBean("account");
            System.out.println(account);
            
        }
    
    }

     (3)使用setter注入

      1)setter注入

      setter注入是Spring中最主流的注入方式,它利用Java Bean规范所定义的setter方法来完成注入,灵活且可读性高。它消除了使用构造器注入时出现多个参数的可能性,首先可以把构造方法声明为无参数的,然后利用setter注入为其设置对应的值,其实也是通过Java反射实现的。

      代码示例如下:

    package com.xhbjava.pojo;
    
    import java.util.Date;
    
    public class Account1 {
        private String name;
        private String age;
        private Date birthday;
        
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getAge() {
            return age;
        }
    
        public void setAge(String age) {
            this.age = age;
        }
    
        public Date getBirthday() {
            return birthday;
        }
    
        public void setBirthday(Date birthday) {
            this.birthday = birthday;
        }
    
        @Override
        public String toString() {
            return "Account [name=" + name + ", age=" + age + ", birthday=" + birthday + "]";
        }
    
    }
    <?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="account" class="com.xhbjava.pojo.Account">
    <constructor-arg name="name" value="张三"></constructor-arg>
    <constructor-arg name="age" value="17"></constructor-arg>
    <constructor-arg name="birthday" ref="now"></constructor-arg>
    </bean>
    <bean id="now" class="java.util.Date"></bean>
    </beans>

    测试类进行测试:

      2)使用 p 名称空间注入数据(本质还是调用 setter方法)

      此种方式是通过在 xml中导入 p名称空间,使用 p:propertyName 来注入数据,它的本质仍然是调用类中的set 方法实现注入功能。

      代码示例如下:

      

    package com.xhbjava.pojo;
    
    import java.util.Date;
    
    public class Account2 {
        private String name;
        private String age;
        private Date birthday;
        
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getAge() {
            return age;
        }
    
        public void setAge(String age) {
            this.age = age;
        }
    
        public Date getBirthday() {
            return birthday;
        }
    
        public void setBirthday(Date birthday) {
            this.birthday = birthday;
        }
    
        @Override
        public String toString() {
            return "Account [name=" + name + ", age=" + age + ", birthday=" + birthday + "]";
        }
    
    }
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:p="http://www.springframework.org/schema/p"
        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="account" class="com.xhbjava.pojo.Account">
    <constructor-arg name="name" value="张三"></constructor-arg>
    <constructor-arg name="age" value="17"></constructor-arg>
    <constructor-arg name="birthday" ref="now"></constructor-arg>
    </bean> -->
    <bean id="account" class="com.xhbjava.pojo.Account2"  p:name="test" p:age="20" p:birthday-ref="now"/>
    <bean id="now" class="java.util.Date"></bean>
    </beans>

    测试类测试:

    3)注入集合属性

      就是给类的集合成员传值,其本质也是setter方法注入,只不变量的数据类型都是集合。

      这里介绍注入数组,List,Set,Map和Properties。

      代码示例如下:

      

    package com.xhbjava.pojo;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.Map;
    import java.util.Properties;
    import java.util.Set;
    
    public class Account {
        private String [] myStr;
        private List<String> myList;
        private Set<String> mySet;
        private Map<String,String> myMap;
        private Properties myProps;
        public String[] getMyStr() {
            return myStr;
        }
        public void setMyStr(String[] myStr) {
            this.myStr = myStr;
        }
        public List<String> getMyList() {
            return myList;
        }
        public void setMyList(List<String> myList) {
            this.myList = myList;
        }
        public Set<String> getMySet() {
            return mySet;
        }
        public void setMySet(Set<String> mySet) {
            this.mySet = mySet;
        }
        public Map<String, String> getMyMap() {
            return myMap;
        }
        public void setMyMap(Map<String, String> myMap) {
            this.myMap = myMap;
        }
        public Properties getMyProps() {
            return myProps;
        }
        public void setMyProps(Properties myProps) {
            this.myProps = myProps;
        }
        @Override
        public String toString() {
            return "Account [myStr=" + Arrays.toString(myStr) + ", myList=" + myList + ", mySet=" + mySet + ", myMap="
                    + myMap + ", myProps=" + myProps + "]";
        }
        
        
    
    }
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:p="http://www.springframework.org/schema/p"
        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="account" class="com.xhbjava.pojo.Account">
        <!-- 在注入集合数据时,只要结构相同,标签可以互换 -->
    <!-- 给数组注入数据 -->
            <property name="myStr">
                <set>
                    <value>aaa</value>
                    <value>bbb</value>
                </set>
            </property>
            <!-- 注入 list 集合数据 -->
            <property name="myList">
                <array>
                    <value>123</value>
                    <value>123</value>
                </array>
            </property>
            <!-- 注入 set 集合数据 -->
            <property name="mySet">
                <list>
                    <value>234</value>
                </list>
            </property>
            <!-- 注入 Map 数据 -->
            <property name="myMap">
                <props>
                    <prop key="a">aaa</prop>
                    <prop key="b">bbb</prop>
                </props>
            </property>
            <!-- 注入 properties 数据 -->
            <property name="myProps">
                <map>
                    <entry key="a" value="a"></entry>
                    <entry key="b">
                        <value>b</value>
                    </entry>
                </map>
            </property>
        </bean>
    
    </beans>

    测试类进行测试:

      

    (4)接口注入

       有时候我们需要的资源并非来自系统本身,而是来自外界,比如数据库连接资源在Tomcat下配置,然后通过JNDI形式去获取,这样数据库连接资源是属于工程外的资源,此时我们可以采用接口注入的方式来获取,比如在Tomcat中可以配置数据源,在Eclipse中配置Tomcat后,可以打开context.xml.如图:

      

       我们在context.xml中添加我们自己的一个资源:

      

    <?xml version="1.0" encoding="UTF-8"?>
    <Context>
    
      <Resource name="jdbc/ssm" auth="Container" type="javax.sql.DataSource" dirverClassName="com.mysql.jdbc.Driver"
      url="jdbc:mysql://localhost:3306/ssm?zeroDateTimeBehavior=convertToNull" username="root" password="root">
      
      </Resource>
        
    </Context>

      如果我们配置了相应数据库连接,那么eclipse会把数据库的驱动包复制到对应的Tomcat的lib文件夹下,否则需要自己手复制驱动包放到Tomcat的工作目录下,启动Tomca,此时数据库资源也会在Tomcat启动的时候被其加载进来。

      如果Tomcat的Web工程使用了Spring可以通过Spring机制,用JNDI获取Tomcat启动时的数据库连接池,具体代码如下:

      

    <?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="dataSource"
            class="org.springframework.jndi.JndiObjectFactoryBean">
            <property name="jndiName">
                <value>java:com/xhbjava/jdbc/ssm</value>
            </property>
    </bean> </beans>

      此时我们就可以在Spring的IoC容器中获取Tomcat所管理的数据库连接池了,这就是一种接口注入的形式。

  • 相关阅读:
    struts2基础---->自定义拦截器
    struts2基础---->第一个Struts2程序
    Vue基础---->vue-router的使用(一)
    java框架---->zxing框架的使用
    java基础---->java输入输出流
    java基础----->TCP和UDP套接字编程
    JS基础---->js中ajax的使用
    tomcat源码---->request的请求参数分析
    Android Http请求方法汇总
    Android如何通过shareduserid获取系统权限
  • 原文地址:https://www.cnblogs.com/xhbJava/p/12875650.html
Copyright © 2020-2023  润新知