天天看点

Spring框架(二)—— Spring IOC 概念、Spring IOC使用过程一、Spring IOC 概念二、Spring IOC 使用过程

文章目录

  • 一、Spring IOC 概念
    • 1、Spring IOC 概述
    • 2、自定义 IOC 框架核心步骤
  • 二、Spring IOC 使用过程
    • 1、Spring IOC 使用步骤
    • 2、Spring IOC 使用过程
    • 3、案例

一、Spring IOC 概念

1、Spring IOC 概述

(1)IOC(Inverse Of Control)是Spring核心功能之一,“控制反转”(IOC) 将应用程序的配置和依赖性规范与实际的应用程序代码分开。

举个例子:在开发过程中业务层需要调用数据层的方法,这时我们会选择在业务层中手动创建数据层对象。Spring的IOC致力于将对象的创建过程以及对象的依赖性关系通过应用程序来完成,控制权交还给了应用程序,所以被称为控制反转。使用Spring我们会将数据层对象和业务层对象的创建工作交给Spring来负责,由于业务层中需要调用数据层方法,也就是说业务层对象对于数据层对象有依赖,那么Spring在创建业务层对象的同时,还会将数据层对象注入到业务层对象中,该过程被称为依赖注入(DI)。

(2)DI(Dependency Injection),即依赖注入:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。

2、自定义 IOC 框架核心步骤

(1)通过xml文件定义需要交给容器来创建的java类型,以及对象之间的依赖关系。

(2)通过xml解析读取xml信息,使用java反射来创建所需的java对象。

(3)将创建好的java对象保存到全局的对象容器中(在spring中被称为spring容器)

(4)通过xml的配置将A对象中所依赖的其他类型的B对象注入到A对象中

(5)在需要使用对象时,从容器中取出对象使用。

二、Spring IOC 使用过程

1、Spring IOC 使用步骤

(1)项目中导入spring所需jar包

Spring框架(二)—— Spring IOC 概念、Spring IOC使用过程一、Spring IOC 概念二、Spring IOC 使用过程

(2)在src目录下新建applicationContext.xml(如果是web项目则需要在WEB-INF目录下新建)。

(3)在xml顶部导入schema,schema和dtd不同,dtd是一个独立的标签,而schema是属于根标签的属性。所以在导入schema时,也就定义好了根标签。以下是一份完整的schema包含了spring-framework中用到的所有内容,除了springmvc。因为springmvc的配置和spring的配置文件是分开的。

<beans	
	xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context" 	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx" 	xmlns:p="http://www.springframework.org/schema/p" 	xmlns:util="http://www.springframework.org/schema/util" 	xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xmlns:cache="http://www.springframework.org/schema/cache"
	xsi:schemaLocation="
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context-4.0.xsd
		http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
		http://www.springframework.org/schema/tx
		http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
		http://www.springframework.org/schema/jdbc
		http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd
		http://www.springframework.org/schema/cache
		http://www.springframework.org/schema/cache/spring-cache-4.0.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-4.0.xsd
		http://www.springframework.org/schema/util
		http://www.springframework.org/schema/util/spring-util-4.0.xsd">
</beans>
           

(4)在配置文件中使用bean标签来定义spring容器负责创建的类,以及类的依赖关系,最终spring在创建好对象以后会自动完成依赖注入。

① service 层

public class UserService {
	private UserDao userDao;
	public UserDao getUserDao() {
		return userDao;
	}
	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}

	public void insert() {
		System.out.println("执行新增业务");
		userDao.insert();
	}
}
           

② dao层

public class UserDao {
	public void insert() {
		System.out.println("执行新增数据");
	}
}
           

③ xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="service" class="com.spring.demo1.service.UserService">
        <property name="userDao" ref="dao"></property>
    </bean>
    <bean id="dao" class="com.spring.demo1.dao.UserDao"></bean>
</beans>
           

(5)解析xml文件

public class UserBean {
	//定义容器保存框架创建的所有对象
	private static Map<String,Object> container = new HashMap<String,Object>();
	
	static {
		//使用dom4j解析xml文件读取bean标签,将对象创建出来用id为键,对象为值保存到容器中
		SAXReader reader = new SAXReader();
		try {
			//读取xml文件
			Document document = reader.read("src/spring1.xml");
			//获取根便签
			Element rootElement = document.getRootElement();
			//读取bean标签,得到对应对象集合
			List<Element> beans = rootElement.elements("bean");
			
			//获取bean标签中的id作为键,class对象值,并保存到容器中
			for(Element b: beans) {
				//获取id键
				String key = b.attributeValue("id");
				//获取class值
				String value = b.attributeValue("class");
				//存入容器中
				container.put(key, Class.forName(value).newInstance());
			}
			
			//根据property标签定义的依赖关系完成依赖注入
			for(Element b: beans) {
				//获取bean下的property标签
				List<Element> properties = b.elements("property");
				for(Element p: properties) {
					//需要被赋值的属性名称
					String pName = p.attributeValue("name");
					//该属性需要赋值的对象在容器里的id
					String pRef = p.attributeValue("ref");
					//从容器中拿到被依赖的对象
					Object refObject = container.get(pRef);
					
					//根据属性名称获取set方法
					//根据bean标签拿到正在循环的这个bean标签的id
					String key = b.attributeValue("id");
					//从容器中取出依赖方对象
					Object object = container.get(key);
					//获取依赖方的字节码信息
					Class c = object.getClass();
					//获取需要被赋值的属性对象
					Field field = c.getDeclaredField(pName);
					//获取该属性的set方法
					Method method=c.getDeclaredMethod("set"+pName.substring(0, 1).toUpperCase()+pName.substring(1), field.getType());
					//调用方法将被依赖对象赋值给依赖对象的目标属性
					method.invoke(object, refObject);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public Object getBean(String id) {
		return container.get(id);
	}
}
           

(6)新建测试类,在入口方法中从spring容器里面取出业务层对象,执行方法。

public class UserAction{
	public static void main(String[] args) {
		UserBean factory = new UserBean();
		UserService service = (UserService) factory.getBean("service");
		service.insert();
	}
}
           

(7)另外也可以不用dom4j来解析xml文件,而是直接用下面的方法来获取xml文件内容。

public class UserAction{
	public static void main(String[] args) {
		ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("hk1.xml");
		UserService service = (UserService) ac.getBean("service");
		service.insert();
	}
}
           

这样就可以省略步骤(5)和步骤(6),而用步骤(7)代替。

2、Spring IOC 使用过程

(1)工厂模式

假如我们的某个对象不能用new的方式来获取,而是我们或者是其他第三方工具使用工厂模式来产生的,那么我们在定义bean标签时需要,需要把

<bean>

的class属性配置为工厂类(或者把

factory-bean

属性配置为工厂类对象),使用属性

factoryMethod

来定义工厂类中获取此对象的方法。

或者

<bean id="dbutil" class="com.spring.util.DBUtil"></bean>
<bean id="connection" factory-bean="dbutil" factory-method="openConnection"></bean>
           

(2)选择构造函数创建对象

在创建对象时,如果想通过不同的构造方法创建,可以在bean标签下使用

<constructor-arg>

标签来定义构造函数的参数,一个

<constructor-arg>

标签就表示一个参数,如果构造函数有多个标签则需要使用多个

<constructor-arg>

标签,前提是类中必须定义对应的带参构造函数。

① 业务实现层

private UserServiceImp(){}
private UserServiceImp(String name){
	System.out.println(name);
}
private UserServiceImp(String name, int age){
	System.out.println(name);
	System.out.println(age);
}
           

② 配置文件

<bean id="userService" class="com.spring.service.UserServiceImp">
	<constructor-arg>
		<value>张三</value>
	</constructor-arg>
	<constructor-arg>
		<value>18</value>
	</constructor-arg>
	<property name="dao" ref="userDao"></property>
</bean>
           

(3)单例模式

Spring 在创建对象时,默认采用的是单例模式,也就是在 Spring 容器中,同一个类型的对象只会保存一个,如果想用非单例模式则需要在 bean 标签中使用

singleton="false"

(spring4以前)或者

scope="prototype"

(spring4.0以后)来进行修改。

scope 在 web 环境中还可以设置为 request 和 session

  • request:表示每次发送新的请求都会创建一个对象
  • session:表示在一次会话中使用的是同一个对象
  • <property>

    标签配置:

    <property>

    标签用于向对象中进行属性注入,注入的属性可以是基本数据类型、String、集合、键值对、对象。

    <property>

    标签需要配置在对应的

    <bean>

    标签之下,

    <property>

    标签中的 name 属性表示的对象中的属性名称。

① 基本数据类型和字符串的注入

基本数据类型和字符串的注入都可以使用

<property>

标签下的

<value>

子标签完成注入。

如:

<property name="password">
	<value>值</value>
</property>
           

如果要给字符串属性注入空串

<value>

标签中空着就行了,但是如果想要注入的是null,则需要使用

<null/>

子标签。

<property name="password">
	<null></null>
</property>
           

② 对象注入

注入对象的方式有两种:

第一种注入已有的 bean 对象,使用 ref 属性

ref="bean id"

的方式或者使用

<ref bean="bean id">

子标签都可以。

或者

<property name="dao">
	<ref="userDao" />
</property>
           

第二种注入还未通过

<bean>

标签创建的对象,在

<property>

标签中使用

<bean>

标签来定义匿名对象。

<property name="dao">
	<bean class="com.spring.dao.UserDaoImp"></bean>
</property>
           

③ List 注入

要为对象注入List集合需要在

<property>

中使用

<list>

子标签,

<list>

标签中可通过

<value>

标签配置任意类型对象。如果为Java对象,则使用 ref指定,或者使用

<bean>

定义新实例,如果是普通类型如 String、int、double 等,直接用字符串即可。

<list/>

里的元素会按配置的先后顺序排序。

<property name="names">
	<list>
		<value>值1</value>
		<value>值2</value>
		<ref bean="bean id" />
	</list>
</property>
           

④ Set 注入

要为对象注入Set集合需要在

<property>

中使用

<set>

子标签,

<set>

标签中可通过

<value>

标签配置任意类型对象。如果为 Java 对象,则使用ref指定,或者使用

<bean>

定义新实例,如果是普通类型如 String、int、double 等,直接用字符串即可。

<property name="names">
	<set>
		<value>值1</value>
		<value>值2</value>
		<ref bean="bean id" />
	</set>
</property>
           

⑤ Map 注入

要为对象注入 Map 键值对需要使用

<property>

的子标签

<map>

<entry>

配置 Map 里的元素,key 指定键,如果 key 为对象,使用 key-ref 属性,value 指定值。如果为 Java 对象,则使用 ref 指定,或者使用

<bean>

定义新实例。

<property name="names">
	<map>
		<entry key="键1">
			<value>值1</value>
		</entry>
		<entry key="键2">
			<ref bean="bean id" />
		</entry>
	</map>
</property>
           

(4)在

applicationContext.xml

文件中使用外界资源文件

有时我们在配置

<bean>

标签时,需要注入一下数据给对象,例如,当我们使用 Spring 来创建 JDBC Connection 对象或者是连接池对象时,都需要配置连接数据库的相关信息,我们可以直接在配置文件中写,但是这样不利于维护,我们可以将这些信息配置在资源文件中,然后通过加载资源文件,在

applicationContext.xml

文件中直接使用资源文件的值。

例如:使用 Spring 创建 c3p0 连接池对象

① 首先导入 c3p0 的jar包以及它的依赖包

在src下配置一个

c3p0.properties

的资源文件,在该资源文件中定义 jdbc 的连接信息以及 c3p0 的各种信息。

#jdbc连接信息
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl = jdbc:mysql://localhost:3306/ebuy?useUnicode=true&characterEncoding=utf-8
jdbc.user = root
jdbc.password =
#c3p0各项属性
c3p0.minPoolSize = 10
c3p0.maxPoolSize = 50
c3p0.initialPoolSize = 20
c3p0.maxIdleTime = 60
c3p0.acquireIncrement = 1
c3p0.checkoutTimeout=6000
c3p0.idleConnectionTestPeriod=600
           

② 在

applicationContext.xml

中使用

<bean>

标签创建资源文件加载器对象,该对象不需要定义 id,Spring 会通过类型完成注入。

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	<property name="location" value="classpath:c3p0.properties"/>
<bean>
           

④ 使用

<bean>

标签创建 c3p0 数据源对象,使用 el 表达式获取资源文件中的数据,将该数据注入到c3p0的各项属性中去。

<bean id="pool" class="com.mchange.v2.c3p0.ComboPooledDataSource">
	<!--提供数据库连接信息和连接池的常用信息-->
    	<!--驱动类-->
    	<property name="driverClass" value="${jdbc.driverClass}" />
    	<!--连接数据库的地址-->
	<property name="jdbcUrl" value="${jdbc.jdbcUrl}" />
	<!--连接数据库的用户名-->
	<property name="user" value="${jdbc.user}" />
	<!--连接数据库的密码-->
	<property name="password" value="${jdbc.password}" />
	
	<!--最大连接数-->
	<property name="maxPoolSize" value="${c3p0.maxPoolSize}"></property>
	<!--最小连接数-->
	<property name="minPoolSize" value="${c3p0.minPoolSize}"></property>
	<!--初始化连接数-->
	<property name="initialPoolSize" value="${c3p0.initialPoolSize}"></property>
	<!--自增长连接数-->
	<property name="acquireIncrement" value="${c3p0.acquireIncrement}"></property>
	<!--连接对象空闲时间  超出10分钟销毁-->
	<property name="maxIdleTime" value="${c3p0.maxIdleTime}"></property>
	<!--连接数据库的等待时间 超出等待时间 抛出异常-->
	<property name="checkoutTimeout" value="${c3p0.checkoutTimeout}"></property>
	<!--检查连接的间隔时间-->
	<property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"></property>
</bean>
           

⑤ 在需要该连接池对象的类中,定义对应的属性,提供 get、set 方法,再通过

<bean>

标签完成依赖注入即可。

需要使用连接池对象的类:

public class UserDaoImp implements UserDao {
	private DataSource dataSource;
	public DataSource getDataSource() {
		return dataSource;
	}
	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}
}
           

<bean>

标签完成依赖注入:

<bean id="userDao " class="com.spring.dao.imp.UserDaoImp">
	<property name="dataSource" ref="pool"></property>
</bean>
           

3、案例

① service层

public class UserService {
	private UserDao userDao;
	public UserDao getUserDao() {
		return userDao;
	}
	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}

	public void select() {
		System.out.println("正在执行查询业务");
		userDao.select();
	}
}
           

② dao层

public class UserDao {
	private DataSource dataSource;
	public DataSource getDataSource() {
		return dataSource;
	}
	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}
	public void select() {
		System.out.println("执行新增数据");
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			conn = dataSource.getConnection();
			String sql = "select * from products";
			ps = conn.prepareStatement(sql);
			ResultSet resultSet = ps.executeQuery();
			while(resultSet.next()) {
				System.out.println(resultSet.getString("p_name"));
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				conn.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
			try {
				ps.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}
           

③ applicationContext.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans  
	xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"     xmlns:p="http://www.springframework.org/schema/p"   xmlns:util="http://www.springframework.org/schema/util"     xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xmlns:cache="http://www.springframework.org/schema/cache"
	xsi:schemaLocation="
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context-4.0.xsd
		http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
		http://www.springframework.org/schema/tx
		http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
		http://www.springframework.org/schema/jdbc
		http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd
		http://www.springframework.org/schema/cache
		http://www.springframework.org/schema/cache/spring-cache-4.0.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-4.0.xsd
		http://www.springframework.org/schema/util
		http://www.springframework.org/schema/util/spring-util-4.0.xsd">
		
    <bean id="service" class="com.spring.hk1.service.UserService">
        <property name="userDao" ref="dao"></property>
    </bean>
    <bean id="dao" class="com.spring.hk1.dao.UserDao">
        <property name="dataSource" ref="pool"></property>
    </bean>
	
	<!-- 创建连接池 -->
	<bean id="pool" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--提供数据库连接信息和连接池的常用信息-->
        <!--驱动类-->
        <property name="driverClass" value="${jdbc.driverClass}" />
        <!--连接数据库的地址-->
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}" />
        <!--连接数据库的用户名-->
        <property name="user" value="${jdbc.user}" />
        <!--连接数据库的密码-->
        <property name="password" value="${jdbc.password}" />
        
        <!--最大连接数-->
        <property name="maxPoolSize" value="${c3p0.maxPoolSize}"></property>
        <!--最小连接数-->
        <property name="minPoolSize" value="${c3p0.minPoolSize}"></property>
        <!--初始化连接数-->
        <property name="initialPoolSize" value="${c3p0.initialPoolSize}"></property>
        <!--自增长连接数-->
        <property name="acquireIncrement" value="${c3p0.acquireIncrement}"></property>
        <!--连接对象空闲时间  超出10分钟销毁-->
        <property name="maxIdleTime" value="${c3p0.maxIdleTime}"></property>
        <!--连接数据库的等待时间 超出等待时间 抛出异常-->
        <property name="checkoutTimeout" value="${c3p0.checkoutTimeout}"></property>
        <!--检查连接的间隔时间-->
        <property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"></property>
    </bean>
    <!-- 创建资源文件加载器对象 -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
       <property name="location" value="classpath:c3p0.properties"></property>
    </bean>
</beans>
           

④ 测试类:Action类

public class UserAction{
	public static void main(String[] args) {
		ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("spring2.xml");
		UserService service = (UserService) ac.getBean("userService");
		service.select();
	}
}