Java之Spring之基于xml的IOC

IoC的概念和作用

程序的耦合和解耦

什么是程序的耦合

在软件工程中,耦合就是指对象之间的依赖性。

理解:实现一个类时要new 一个对象[其他类]

比如:早期我们的JDBC操作,为什么不使用DriverManger的register方法,而是采用Class.forName的方式
public class JdbcDemo1 {
    public static void main(String[] args) {
        //1.注册驱动
        //DriverManager.registerDriver(new com.mysql.jdbc.Driver());
        Class.forName("com.mysql.jdbc.Driver");
        //2.获取连接
        //3.获取预处理 sql 语句对象
        //4.获取结果集
        //5.遍历结果集
    }
}
原因就是:我们的类依赖了数据库的具体驱动类(MySQL),如果这时候更换了数据库品牌(比如Oracle),需要修改源码。

开发中,有些依赖关系是必须的,有些依赖关系是可以通过优化代码来解除的。

再比如:
/**
* 账户的业务层实现类
*/
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao = new AccountDaoImpl();
}
  业务层调用持久层,并且此时业务层在依赖持久层的接口和实现类。如果此时没有持久层实现类,编译将不能通过。这种编译器依赖关系,应该在开发中杜绝。

解决程序耦合的思路

我们一般通过反射来注册驱动:

Class.forName("com.mysql.jdbc.Driver");

此时我们类不再依赖具体的驱动类,此时就算删除mysql的驱动jar包,依然可以编译[不可能允许。因为没驱动]。

同时,也产生了一个新的问题,mysql 驱动的全限定类名字符串是在 java 类中写死的,一旦要改还是要修改
源码。

解决这个问题也很简单,使用配置文件配置。

工厂模式解耦

在实际开发中我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的方法通过读取配置文件,把这些对象创建出来并存起来。在接下来使用的时候,直接拿过来用。

那么,这个读取配置文件,创建和获取三层对象的类就是工厂。

控制反转IoC

上面提到的解耦有两个问题需要解决:

  • 1、存到那里去?

分析:因为有很多对象,所以需要一个集合。又因为需要查找对象,因此用map
解答:在应用加载的时候,创建一个map,用于存放三层对象。
这个map就叫做容器。

  • 2、工厂是什么?

工厂就是负责给我们从容器中获取指定对象的类。这时候我们获取对象的方式发生了改变:

原来:获取对象是,采用new方式。是主动的。
在这里插入图片描述
现在:获取对象时跟工厂要,工厂来查找或者创建对象。是被动的。
在这里插入图片描述
这种被动接收的方式获取对象的思想就是控制反转。它是spring框架的核心之一。

控制反转:把创建对象的权力交给框架。包括依赖注入和依赖查找。

**IOC的作用:**消减计算机程序的耦合。

使用spring的IOC解决程序耦合

准备

引入依赖

1、创建一个maven工厂,不使用骨架
2、修改pom.xml

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
    </dependencies>

创建业务层接口和实现类

package com.oceanstar.service;

/*
 *          操作账户:业务层接口
 * */
public interface IAccountService {
    /*
     * 模拟保存账户
     * */
    void saveAccount();
}



/**
 *
 *  账户业务实现
 */
public class AccountServiceImpl implements IAccountService {
    private IAccountDao accountDao = new AccountDaoImpl();   此处的依赖关系有待解决
    
     public void saveAccount() {
        accountDao.saveAccount();  
     }

}

创建持久层接口和实现类

/**
* 账户的持久层接口
* @author 黑马程序员
* @Company http://www.ithiema.com
* @Version 1.0
*/
public interface IAccountDao {
/**
* 保存账户
*/
void saveAccount();
}
/**
* 账户的持久层实现类
* @author 黑马程序员
* @Company http://www.ithiema.com
* @Version 1.0
*/
public class AccountDaoImpl implements IAccountDao {
@Override
public void saveAccount() {
System.out.println("保存了账户");
} }

基于xml的配置

创建配置文件,并配置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">

    <!--把对象的创建交给spring来管理
    bean标签:用于配置让spring创建对象,并且存入ioc容器之中
    id属性:      对象的唯一标识
    class属性:  指定要创建对象的全限定类名
    -->
    <bean id="accountService" class="com.oceanstar.service.impl.AccountServiceImpl"></bean>
    <bean id="accountDao" class="com.oceanstar.dao.impl.AccountDaoImpl"></bean>
</beans>

测试配置是否成功

/*
* 表现层到业务层
* */
public class Client {
    /*
    * 获取spring容器的Ioc核心容器,并根据id获取对象
    * */
    public static void main(String[] args) {
        //1、获取spring容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");

        //2、根据bean的id获取对象
        IAccountService iAccountService = (IAccountService) applicationContext.getBean("accountService");
        System.out.println(iAccountService);


        IAccountDao iAccountDao = (IAccountDao) applicationContext.getBean("accountDao");
        System.out.println(iAccountDao);
    }
}

运行结果:
在这里插入图片描述

Spring基于xml的IOC细节

spring中工厂的类结构图

在这里插入图片描述

BeanFactory和ApplicationContext的区别

beanFaactory是Spring容器种的顶层接口
ApplicationContext是他的子接口

BeanFactory和ApplicationContext的区别:创建对象的时间点不一样。

	ApplicationContext:只要一读取配置文件,默认情况下就会创建对象。
	BeanFactory:什么时候使用什么时候创建对象
ApplicationConext接口的实现类
  • ClassPattXmlApplicationContext: 从类的根路径下加载配置文件
  • FileSystemXmlApplicationContext:从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
  • AnnotationConfigApplicationContext: 当我们使用注解配置容器对象时,使用此类对象来创建spring容器。它用来读取注解

IOC中bean标签和管理对象细节

bean标签

作用:

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

属性

  • id:给对象在容器中提供一个唯一标识。用于获取对象
  • class:指定类的全限定类名。用于反射创建对象。默认情况下创建无参构造函数
  • scope:指定对象的作用范围:
    • singleton:默认值,单例
    • prototype:多例
    • request:web项目中,Spring创建一个bean对象,将对象存入到request域中
    • session:web项目中,Spring创建一个bean对象,将对象存入到session域中
    • global session: web项目中,应用在portlet环境,如果没有portlet环境那么globalsession相当于session
  • init-method:指定类中的初始化方法名称
  • destory-method:指定类中销毁方法名称
实例化对象的三种方式
  • (1) 使用默认无参构造函数
    默认情况下,它会根据默认无参构造函数来创建类对象。如果bean种没有默认无参构造函数,将会创建失败
    <bean id="accountService" class="com.oceanstar.service.impl.AccountServiceImpl"></bean>
  • (2) spring管理静态工厂—使用静态工厂的方法创建对象
/*
* 模拟一个静态工厂,创建业务实例类
* */
public class StaticFactory {
    public static IAccountService createAccountService(){
        return new AccountServiceImpl();
    }
}

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

   <bean id="accountService"
          class="com.oceanstar.factory.StaticFactory"
        factory-method="createAccountService"></bean>
  • (3) spirng管理实例工厂—使用实例工厂的方法创建对象
/*
* 模拟一个实例工厂,创建业务层实现类
* 此工厂创建对象,必须现有工厂实例对象,再调用方法
* */
public class InstanceFactory {
    public IAccountService createAccountService(){
        return new AccountServiceImpl();
    }
}

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

    <bean id="instanceFactory" class="com.oceanstar.factory.InstanceFactory"></bean>
    <bean id="accountService"
          factory-bean="instanceFactory"
        factory-method="createAccountService"></bean>

spring的依赖注入

依赖注入的概念

依赖注入:是spring框架核心IoC的具体实现
我们的程序在编写时,通过控制反转,把对象的创建交给了spring,但是代码种不可能出现没有依赖的情况。ioc解耦只是降低了他们的依赖关系,但是不会消除。例如,我们的业务层仍然会调用持久层的方法。
那这种业务层和持久层的依赖关系,在使用spring之后,就让spring来维护了。
简单的来说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取了。

构造函数注入

/**
 *
 *  账户业务实现
 */
public class AccountServiceImpl implements IAccountService {
    private String name;
    private Integer age;
    private Date bithday;

    public AccountServiceImpl(String name, Integer age, Date bithday) {
        this.name = name;
        this.age = age;
        this.bithday = bithday;
    }

    public void saveAccount() {
        System.out.printf("name:" + name
        + " age:" + age
        + " bithday:" + bithday);
     }

}

使用构造函数的方式,给service中的属性传值。
要求:
类中需要提供一个对应参数列表的构造函数。
涉及的标签:
constructor-arg
属性:

  • index:指定参数在构造函数参数列表的索引位置
  • type:指定参数在构造函数中的数据类型
  • name:指定参数在构造函数中的名称 用这个找给谁赋值
    =上面三个都是找给谁赋值,下面两个指的是赋什么值的========
  • value:它能赋的值是基本数据类型和 String 类型
  • ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean
    <bean id="accountService" class="com.oceanstar.service.impl.AccountServiceImpl">
        <constructor-arg name="name" value="oceanstar"></constructor-arg>
        <constructor-arg name="age" value="18"></constructor-arg>
        <constructor-arg name="bithday" ref="now"></constructor-arg>
    </bean>

    <bean id="now" class="java.util.Date"></bean>
set方法注入

就是在类中提供需要注入成员的set方法。

/**
 *
 *  账户业务实现
 */
public class AccountServiceImpl implements IAccountService {
    private String name;
    private Integer age;
    private Date bithday;


    public void setName(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void setBithday(Date bithday) {
        this.bithday = bithday;
    }

    public void saveAccount() {
        System.out.printf("name:" + name
        + " age:" + age
        + " bithday:" + bithday);
     }

}

通过配置文件给 bean 中的属性传值:使用 set 方法的方式
涉及的标签:
property
属性:

  • name:找的是类中 set 方法后面的部分
  • ref:给属性赋值是其他 bean 类型的
  • value:给属性赋值是基本数据类型和 string 类型的
    实际开发中,此种方式用的较多。
    <bean id="accountService" class="com.oceanstar.service.impl.AccountServiceImpl">
        <property name="name" value="oceanstar"></property>
        <property name="age" value="18"></property>
        <property name="bithday" ref="now"></property>
    </bean>

    <bean id="now" class="java.util.Date"></bean>
使用p名称空间注入数据

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

/**
 *
 *  账户业务实现
 */
public class AccountServiceImpl implements IAccountService {
    private String name;
    private Integer age;
    private Date bithday;


    public void setName(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void setBithday(Date bithday) {
        this.bithday = bithday;
    }

    public void saveAccount() {
        System.out.printf("name:" + name
        + " age:" + age
        + " bithday:" + bithday);
     }

}
<?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="accountService"
          class="com.oceanstar.service.impl.AccountServiceImpl"
         p:name="oceanstar" p:age="18" p:bithday-ref="now">
    </bean>

    <bean id="now" class="java.util.Date"></bean>

</beans>
注入集合属性

就是给类中的集合成员传值,也是调用set注入,只不过变量的数据类型都是集合。

/**
 *
 *  账户业务实现
 */
public class AccountServiceImpl implements IAccountService {
    private String[] myStrs;
    private List<String> myList;
    private Set<String> mySet;
    private Map<String , String> myMap;
    private Properties myProps;

    public void setMyStrs(String[] myStrs) {
        this.myStrs = myStrs;
    }

    public void setMyList(List<String> myList) {
        this.myList = myList;
    }

    public void setMySet(Set<String> mySet) {
        this.mySet = mySet;
    }

    public void setMyMap(Map<String, String> myMap) {
        this.myMap = myMap;
    }

    public void setMyProps(Properties myProps) {
        this.myProps = myProps;
    }

    public void saveAccount() {
        System.out.printf(Arrays.toString(myStrs));
        System.out.println(myList);
        System.out.println(mySet);
        System.out.println(myMap);
        System.out.println(myProps);
     }
}

注入集合数据
List 结构的:
array,list,set
Map 结构的
map,entry,props,prop

<?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">

    <!--把对象的创建交给spring来管理
    bean标签:用于配置让spring创建对象,并且存入ioc容器之中
    id属性:      对象的唯一标识
    class属性:  指定要创建对象的全限定类名
    -->


    <bean id="accountService"
          class="com.oceanstar.service.impl.AccountServiceImpl">
        <!-- 在注入集合数据时,只要结构相同,标签可以互换 -->
        <!-- 给数组注入数据 -->
        <property name="myStrs">
            <set>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </set>
        </property>
        <!-- 注入 list 集合数据 -->
        <property name="myList">
            <array>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </array>
        </property>
        <!-- 注入 set 集合数据 -->
        <property name="mySet">
            <list>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </list>
        </property>
        <!-- 注入 Map 数据 -->
        <property name="myMap">
            <props>
                <prop key="testA">aaa</prop>
                <prop key="testB">bbb</prop>
            </props>
        </property>
        <!-- 注入 properties 数据 -->
        <property name="myProps">
            <map>
                <entry key="testA" value="aaa"></entry>
                <entry key="testB"> <value>bbb</value></entry>
            </map>
        </property>
        </bean>



</beans>
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值