2019-8-5 [Java_Spring]2.Spring概述框架 IOC简单实用 IOC技术详解 IOC的技术深入 IOC和DI对比 举例说明

20 篇文章 0 订阅

2.Spring框架 控制反转(IOC)/依赖注入(DI)初级使用

2.1 为什么要使用Spring

刚才我们做的自定义的IOC操作虽然能够解决我们的代码耦合问题,但是编写起来还是过于麻烦,这时我们就在想有没有大神已经帮我们完成了这个功能我们直接用就可以呢?
当然有的,那就是鼎鼎大名的Spring!

2.2 Spring概述 EJB

2.2.1 什么是Spring呢?

Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。

Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。在这里插入图片描述\

2.2.2 Spring能够为我们做什么呢?

2.2.2.1 轻量 :

从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类。

2.2.2.2 控制反转: IOC

Spring通过一种称作控制反转(IoC)的技术促进了低耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。
你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。

2.2.2.3 面向切面: AOP

Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。
应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。

2.2.2.4 容器:DI

Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。
然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。

2.2.2.5 框架:spring

Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。

2.2.2.6 MVC:

Spring的作用是整合,但不仅仅限于整合,Spring 框架可以被看做是一个企业解决方案级别的框架。

客户端发送请求,服务器控制器(由DispatcherServlet实现的)完成请求的转发,控制器调用一个用于映射的类HandlerMapping,该类用于将请求映射到对应的处理器来处理请求。

HandlerMapping 将请求映射到对应的处理器Controller(相当于Action)在Spring 当中如果写一些处理器组件,一般实现Controller 接口,在Controller 中就可以调用一些Service 或DAO 来进行数据操作 ModelAndView 用于存放从DAO 中取出的数据,还可以存放响应视图的一些数据。

如果想将处理结果返回给用户,那么在Spring 框架中还提供一个视图组件ViewResolver,该组件根据Controller 返回的标示,找到对应的视图,将响应response 返回给用户。
在这里插入图片描述
通过上图概括得知,Spring技术其实已经能够囊括所有的JavaEE开发领域,比如 对象管理 数据库管理 页面管理以及一些细小技术的整合,可以这么说,Spring除了不能帮你写代码之外

剩下的它都能帮你做,所以我们把这么强大的一个框架引入我们的项目,这样带来的好处就是我们可以节省很多时间去干自己想干的事情;

而且Spring的学习也是非常简单的,Spring有一句名言叫做不重复的去创造轮子,也就是说我们现在学的技术Spring都能整理并且能让我们现在的技术变得更简单!

  1. 我们应用Spring怎么创建对象?
  2. 我们应用spring创建的对象怎么赋值?

2.3 SpringIOC的简单使用

我们引入Spring其实最核心的项目就是能过简化我们的开发,就好比刚才我们自己开发的IOC框架一样,其实spring已经具备了这个功能我们拿过来用就可以了

2.3.1 添加spring的相关jar包

在这里插入图片描述

2.3.2 编写spring配置文件

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

这个文件比我们之前写的文件多了很多spring功能所以我们可以直接使用

<?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="userDAO" class="com.hnxy.dao.impl.UserDAOImpl" />
	<bean id="userService" class="com.hnxy.service.impl.UserServiceImpl" />
</beans>

2.3…3 编写测试类获取配置文件中的信息

// spring方式1 获取配置文件中的对象
Resource resource = new ClassPathResource("applicationContext.xml");
// 获取工厂类
BeanFactory factory = new XmlBeanFactory(resource);
// 获取我们需要的对象信息
UserDAO dao = (UserDAO)factory.getBean("userDAO");
UserService service = (UserService)factory.getBean("userService"); 
System.out.println(dao);
System.out.println(service);

这是跟我们之前自己写的方式差不多的获取对象的形式,其实spring对这种形式还做了简化
我们要使用的方法
ApplicationContext (接口)获取spring的 配置文件的核心对象
ClassPathXmlApplicationContext src下一个xml类型的applicationContext核心配置文件

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDAO userDAO = (UserDAO)context.getBean("userDAO");
UserService userService = (UserService)context.getBean("userService");
System.out.println(userDAO);
System.out.println(userService);

当然如果你觉得按ID获取对象的形式过于繁琐还可以根据类型获取

<?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">
	<!-- 按类型创建对象 不需要指定ID -->
	<bean class="com.hnxy.dao.impl.UserDAOImpl" />
	<bean class="com.hnxy.service.impl.UserServiceImpl" />
</beans>

2.3.4 测试类 :

package com.hnxy.test;

import com.hnxy.entity.UserInfo;
import com.hnxy.service.UserService;
import com.hnxy.service.impl.UserServiceImpl;
import com.hnxy.utils.BeanFactory;

public class Test1 {

	public static void main(String[] args) {
		try {
			UserService service = (UserService)BeanFactory.getBean("userService");
			UserInfo user = service.findUserByID(1);
			System.out.println(user);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

效果 :
在这里插入图片描述
当然这种写法一定要注意一个问题就是 在spring配置文件中不能出现两个类型相同的bean,如果出现了两个类型相同的bean
Spring就无法判定该使用哪个Bean来创建对象,从而引发报错!

2.4 SpringIOC的技术详解

2.4.1 jar包

  1. spring-core : spring的核心包,只要用spring这个是必须要有的
  2. spring-beans : spring的核心包,我们刚才配置的XML根目录就是beans,所以spring用它管理bean
  3. spring-aop : spring的重要包,主要完成spring的另一大核心功能AOP,面向切面编程。
  4. spring-expression : spring的重要包,里面定义了一些表达式语言和AOP包一起用完成AOP功能的
  5. spring-context : spring的核心包,spring的大部分容器功能都是由它来完成的,就像咱今天用的IOC容器就是其中之一
  6. spring-jcl : spring的日志管理包,spring进行日志管理的重要jar包

2.4.2 配置文件

<?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="userDAO" class="com.hnxy.dao.impl.UserDAOImpl" />
	<bean id="userService" class="com.hnxy.service.impl.UserServiceImpl" />
</beans>

spring的配置文件和我们平时用的XML不太一样,spring因为要完成很多的功能,所以它把它的功能都拆分成小的XSD文件,放在同一的schema目录下,

然后我们用什么功能就引入什么功能即可,这个叫做命名空间引入功能,此处我们在spring的初始的时候只用到spring的对象管理功能所以只引入了一个beans的XSD

说道XSD还需要注意一点就是spring的配置文件可以配置的功能都放在一个一个的小XSD文件中了,这种方式叫做命名空间引入功能,此处的命名空间指的就是schema这个统一的XSD存放位置

此处还需说明的一点是,spring把每个类都当做JavaBean来封装功能,所以此处我们在创建对象的时候都是以bean标签进行数据操作的;

2.4.3 bean标签

bean标签可以通过属性的指定来创建不同类的对象,其中class属性配置类的权限定名称用于反射生成对象,id用于区分bean与bean之间的唯一标识

2.5 SpringIOC的技术深入 : DI依赖注入

问题 : 现在我们可以使用springIOC功能创建对象了,但是我们观察下面的代码就会出现一个问题:
在这里插入图片描述
上面这个代码很明显 是要求在创建UserServiceImpl对象的时候给类中的userDAO属性实例化,那么这样光靠IOC肯定办不到,那么怎么办呢?此时可以采用依赖注入的方式完成

这个需求

2.5.1 控制反转(IOC)/依赖注入(DI)对比

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度

其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。

通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

注意 :
控制反转(IOC)可以帮助我们创建一个类的对象
依赖注入(DI)它可以在IOC创建对象的时候帮助IOC给这个对象的属性赋值

2.5.2 举例说明

2.5.2.1 假如我们有个实体类叫UserInfo
package com.hnxy.entity;

/**
 * 用户表的实体类
 * @author sysmaster
 *
 */
public class UserInfo {
	
	// 私有属性
	
	private Integer id;		// 编号
	private String name;	// 姓名
	
	// 共有方法
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "UserInfo [id=" + id + ", name=" + name + "]";
	}
}
2.5.2.2 我们可以利用spring来创建这个类的对象
<?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">
	
	<!-- 创建user对象 -->
	<bean id="user1" class="com.hnxy.entity.UserInfo"></bean>
</beans>

测试类

package com.hnxy.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.hnxy.entity.UserInfo;

public class Test2 {
	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
		UserInfo user = (UserInfo)context.getBean("user1");
		System.out.println(user);
	}
}

结果 :
在这里插入图片描述
观察结果不难发现IOC确实帮助我们创建了一个对象但是属性都没有值,如果你想让属性有值很简单,修改下spring的配置文件

<?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">
	
	<!-- 创建user对象 -->
	<bean id="user1" class="com.hnxy.entity.UserInfo">
		<!-- DI 依赖注入在创建这个对象的时候调用这个对象的属性完成赋值 -->
		<property name="id" value="1" />
		<property name="name" value="赵文明" />
	</bean>	
</beans>

其他的不变直接运行测试类,
在这里插入图片描述
这个时候就已经能够获取值了,是不是很神奇呢?

我们来总结一下这个DI依赖注入,它的赋值原理很简单,根据你提供的属性名称找到对应的set方法 然后把值赋予这个属性完成操作;

所以你要想使用DI进行属性值的赋予的话,属性就必须有自己的set方法,如果没有是付不了值的!
没有就会报错

十月 31, 2018 9:37:22 上午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@8c92f4: startup date [Wed Oct 31 09:37:22 CST 2018]; root of context hierarchy
十月 31, 2018 9:37:22 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [applicationContext.xml]
十月 31, 2018 9:37:22 上午 org.springframework.context.support.AbstractApplicationContext refresh
警告: Exception encountered during context initialization - cancelling refresh attempt: 
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'user1' 
defined in class path resource [applicationContext.xml]: Error setting property values;
 nested exception is org.springframework.beans.NotWritablePropertyException: 
Invalid property 'id' of bean class [com.hnxy.entity.UserInfo]:
 Bean property 'id' is not writable or has an invalid setter method.
  Does the parameter type of the setter match the return type of the getter
	... 33 more

上面的报错显示id属性没有给get和set方法,所以就会报错!
当然如果我们不给属性提供get set方法我们能不能给属性赋值呢? 当然可以,我们可以利用构造函数赋值

2.5.2.3 实体类改造
package com.hnxy.entity;

/**
 * 用户表的实体类
 * @author sysmaster
 *
 */
public class UserInfo {
	
	// 私有属性
	
	private Integer id;		// 编号
	private String name;	// 姓名
	
	// 无参构造
	public UserInfo(){}
	
	// 有参构造
	public UserInfo(Integer id, String name) {
		super();
		this.id = id;
		this.name = name;
	}
    
	@Override
	public String toString() {
		return "UserInfo [id=" + id + ", name=" + name + "]";
	}
}

我们分别提供了无参和有参构造,如果想给属性赋值我们可以利用有参构造给属性赋值

2.5.2.4 编写Spring配置文件
<?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="user1" class="com.hnxy.entity.UserInfo">
		<!-- 通过构造函数给属性赋值 -->
		<!-- 根据构造函数的参数名赋值 -->
		<constructor-arg name="id" value="1" />
		<!-- 还可以根据构造函数的参数索引赋值,索引从0开始,所以第二个name参数索引为1 -->
		<constructor-arg index="1" value="赵文明" />
	</bean>	
</beans>

运行测试类看下效果
在这里插入图片描述
和刚才是一样的
那么我们经过对比来开其实这两种给属性赋值的方式各有千秋
set 方式给属性赋值我们称之为 值注入 好处就是 一个属性一个set 非常直接 坏处就是 set方法要写一堆
构造函数的赋值的方式我们称之为 构造注入 好处就是一个构造函数可以给多个属性赋值 坏处就是编写起来不太容易

这两个方法还有一个共同的弱点就是如果创建的对象很多赋值的需求也很多的时候不管用哪种其实都很累,这个问题我们后面再解决,现在我们来看一个非常棘手的问题

2.5.2.5 加入UserInfo类中加入了一个日期格式的属性
package com.hnxy.entity;

import java.util.Date;

/**
 * 用户表的实体类
 * 
 * @author sysmaster
 *
 */
public class UserInfo {

	// 私有属性
	private Integer id; // 编号
	private String name; // 姓名
	private Date birthday; // 生日
	
	// 共有方法	
	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

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

	public Date getBirthday() {
		return birthday;
	}

	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}

	@Override
	public String toString() {
		return "UserInfo [id=" + id + ", name=" + name + ", birthday=" + birthday + "]";
	}	
}
2.5.2.6 如果我想给这个生日 java.util.Date类型的数据赋值,

此处我们应该怎么处理呢?

<?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="user1" class="com.hnxy.entity.UserInfo">
		<!-- 属性赋值 -->
		<property name="id" value="1" />
		<property name="name" value="赵文明" />
		<!-- 此处需要注意一个问题 日期不是字符串,所以用value赋值肯定会报错! -->
		<property name="birthday" value="2018-10-31" />
	</bean>	
</beans>

运行测试类看下效果

Caused by: java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'java.util.Date' for property 'birthday': no matching editors or conversion strategy found
	at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:299)
	at org.springframework.beans.AbstractNestablePropertyAccessor.convertIfNecessary(AbstractNestablePropertyAccessor.java:585)
	... 17 more

此处提示 birthday需要一个java.util.Date类型的值而不是java.lang.String类型的值,这时候我们该怎么办呢?
其实很简单,创建一个时间对象把时间对象赋值给birthday就好了呀

2.5.2.7 改造 spring配置文件
<?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">
	
	<!-- 创建一个date类型的对象 -->
	<bean id="now" class="java.util.Date" />	
	
	<bean id="user1" class="com.hnxy.entity.UserInfo">
		<!-- 属性赋值 -->
		<property name="id" value="1" />
		<property name="name" value="赵文明" />
		<!-- 此处需要注意一个问题 日期不是字符串,所以用value赋值肯定会报错! -->
		<property name="birthday" value="2018-10-31" />
	</bean>	
</beans>

此处我们已经创建了一个时间对象叫做now 那么怎么把这个对象和birthday联系在一起呢?
很简单 在birthday属性取值的时候使用 ref 这个属性 引用上面的now对象就可以了

<?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">
	
	<!-- 创建一个date类型的对象 -->
	<bean id="now" class="java.util.Date" />
	
	<bean id="user1" class="com.hnxy.entity.UserInfo">
		<!-- 属性赋值 -->
		<property name="id" value="1" />
		<property name="name" value="赵文明" />
		<!-- 通过ref属性引用上面bean的id然后获取上面bean的对象赋值给birthday -->
		<property name="birthday" ref="now" />
	</bean>	
</beans>

运行测试结果:
在这里插入图片描述
怎么样是不是很神奇,此处我们来简单总结一下DI
如果你给属性赋值的话有两种情况

1. 直接赋值
<property name="属性名称" value="属性值" />
2. 调用其他bean的对象给属性赋值
<property name="属性名称" ref="其他bean的ID" />
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值