Java开发Spring之IOC详解第一篇(xml开发常用APIben标签DI依赖注入)
Posted ahcfl
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java开发Spring之IOC详解第一篇(xml开发常用APIben标签DI依赖注入)相关的知识,希望对你有一定的参考价值。
文章目录
一、 Spring概述
## Spring是什么
Spring是分层的JavaSE/EE应用full-stack(全栈)轻量级开源框架;
Spring框架是J2EE企业级应用的轻量级开源框架,提供了表现层springmvc
和持久springJDBC(JDBCTemplate),以及业务层的事务管理等企业级应用解决方案;
Spring能将开源世界中众多优秀的第三方框架进行集成比如mybatis等
Spring是以IOC(Inversion Of Control)控制反转和AOP(Aspect Oriented Programming)面向切面编程为核心;
源码下载地址: https://repo.springsource.org/libs-release-local/org/springframework/spring/
##Spring的发展历史:
1997年IBM提出了EJB的思想
1998年,SUN制定开发标准规范EJB1.0
1999年,EJB1.1发布
2001年,EJB2.0发布
2003年,EJB2.1发布
2006年,EJB3.0发布
Rod Johnson(spring之父)
Expert One-to-One J2EE Design and Development(2002)
阐述了J2EE使用EJB开发设计的优点及解决方案
Expert One-to-One J2EE Development without EJB(2004)
阐述了J2EE开发不使用EJB的解决方式(Spring雏形)
2017年9月份发布了spring的最新版本--spring 5.0通用版(GA)
##Spring特点
【俗称Spring为开发架构的`粘合剂`。】
Spring 出现是为了解决JavaEE 实际问题:
1、方便解耦,简化开发 (IOC)
Spring就是一个大工厂【容器】,可以将所有对象创建和依赖关系维护交给Spring管理,
Spring工厂是用于生成bean
2、AOP编程的支持
Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能
3、声明式事务的支持
只需要通过配置就可以完成对事务的管理,而无需手动编程
4、方便程序的测试
Spring对Junit4支持,可以通过注解方便的测试Spring程序
5、方便集成各种优秀框架
Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz等)的直接支持
6、降低JavaEE API的使用难度
Spring 对JavaEE开发中非常难用的一些API,如:JDBC、JavaMail、远程调用等,都提供了封装,使这些API应用难度得到降低
##Spring体系结构
测试(Test)模块
核心容器(Core Container)
AOP(Aspect Oriented Programming)模块
数据访问/集成(Data Access/Integration)层
Web层
##Spring核心
Spring为企业级开发提供了丰富的功能,这些功能的底层都依赖于它的两个核心特性:
1、控制反转(Inversion of Control,**IOC**)
2、面向切面编程(aspect-oriented programming,**AOP**)
# 那么IOC和AOP是一种技术吗?不是的!他们是一种思想
二、高内聚低耦合【了解】
1、耦合与内聚概述
`代码的书写原则:高内聚低耦合
##耦合与内聚的概念
耦合(Coupling):代码书写过程中所使用技术的结合紧密度程序之间的依赖程度,
用于衡量软件中【各个模块之间的联系程度】。
内聚(Cohesion):代码书写过程中单个模块内部各组成部分间的联系,
用于衡量软件中各个【功能模块内部的功能联系】。
程序书写的原则:【高内聚,低耦合】。就是同一个模块内的各个元素之间要高度紧密,但是各个模块之间的相互依存度却不要那么紧密
##耦合的说明
耦合: 程序代码之间的依赖关系
低耦合: 降低程序代码之间的依赖关系,从而方便维护扩展和重用
解耦合: 在java程序代码中,耦合是不可能完全解开的,我们所说的"解耦合"指的是【解开程序编译期的耦合】
##耦合的弊端:
独立性差 可重用性不高 维护成本高
2、解耦过程(分析javaweb开发)
1、服务层与持久层存在紧耦合;
2、我们加入工厂模式后,服务层与持久层完成解耦,但是工厂类与持久层存在紧耦合;
3、于是通过工厂模式和静态资源配置将代码的耦合降到最低;
最终方案:工厂+反射+配置文件 --》解耦
3、解耦方案【工厂+反射+配置文件】
1)配置文件 bean.properties
# 格式:key=value
# key: 自定义,一般为当前类实现的接口的名称
# value: 当前需要使用的类的全限定名
userDao=com.exmple.dao.impl.UserDaoImpl
userService=com.exmple.service.UserServiceImpl
2)工厂模式-bean单实例 [饿汉模式/懒汉模式]
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
/**
* bean工厂: 用于创建Bean对象(java对象)
*/
public class BeanFactory1 {
// 用于存放创建的bean对象(容器)
private static Map<String,Object> beansMap = new HashMap<>();
// 用于存放解析到的全限定名
private static Map<String,String> urlMap = new HashMap<>();
static {
// 1.解析配置文件,获取配置文件中配置的全限定名
// ResourceBundle: jdk提供的工具,专门用于解析properties配置文件
ResourceBundle bundle = ResourceBundle.getBundle("beans");
// 获取properties配置文件中所有的key
Enumeration<String> keys = bundle.getKeys();
while (keys.hasMoreElements()){
// 获取下一个key
String key = keys.nextElement();
// 根据key获取对应的全限定名
String value = bundle.getString(key);
// 存放到map集合中
urlMap.put(key,value);
-------------------------------------------------------------
// 立即加载的思想创建bean对象 【饿汉模式】
Object obj = Class.forName(value).newInstance();
// 将创建好的对象存放到beansMap容器中
beansMap.put(key,obj);
-------------------------------------------------------------
}
// 测试是否解析到了数据
for (String key:urlMap.keySet()){
System.out.println(key+" : "+urlMap.get(key));
}
}
// 反射创建类对象 【懒汉模式】
public static Object getBean(String id) {
try {
// 先从bean容器中获取对象
Object obj = beansMap.get(id);
if(obj==null){
// 根据id获取对应的类的全限定名
String className = urlMap.get(id);
// 反射创建类对象
obj = Class.forName(className).newInstance();
// 将创建好的bean对象存放到beansMap容器中
beansMap.put(id,obj);
}
return obj;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
3)工厂类代码-bean多实例
/**
* bean工厂: 专门用于创建Bean对象(java对象)
*/
public class BeanFactory {
// 用于存放解析到的全限定名
private static Map<String,String> urlMap = new HashMap<>();
static {
// 1.解析配置文件,获取配置文件中配置的全限定名
// ResourceBundle: jdk提供的工具,专门用于解析properties配置文件
ResourceBundle bundle = ResourceBundle.getBundle("beans");
// 获取properties配置文件中所有的key
Enumeration<String> keys = bundle.getKeys();
while (keys.hasMoreElements()){
// 获取下一个key
String key = keys.nextElement();
// 根据key获取对应的全限定名
String value = bundle.getString(key);
// 存放到map集合中
urlMap.put(key,value);
}
// 测试是否解析到了数据
for (String key:urlMap.keySet()){
System.out.println(key+" : "+urlMap.get(key));
}
}
// 反射创建类对象
public static Object getBean(String id) {
try {
// 根据id获取对应的类的全限定名
String className = urlMap.get(id);
// 反射创建类对象
Object obj = Class.forName(className).newInstance();
return obj;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
三、Spring核心之IOC
1、IOC概念和作用
IOC 全称为 Inversion of Control,翻译为 “控制反转”。
【1】控制什么
控制对象的创建和销毁
【2】反转什么
反转创建对象的控制权,将(创建和销毁)对象的控制权交给IOC容器
【3】IOC的作用
解耦
传统方式创建对象: new 对象(); 【主动创建】
IOC方式创建对象: 找容器(本质上就是一个Map集合)【被动接收】
分析解耦的过程:
通过标记(标记就是配置文件中的key)找工厂,工厂帮我们创建对应的类对象,并返回给我们使用。
当前类可以选择主动出击(new的方式)创建对象,但是此时耦合度高。
此时把主动创建改成被动接收,由工厂对象为当前类生产所必须的关联对象,此时降低了两个类的依赖关系。
2、IOC解耦分析
我们知道在面向对象设计的软件系统中,它的底层都是由N个对象构成的,
各个对象之间通过相互合作,最终实现系统地业务逻辑 ,如图所示:
IOC理论提出的观点:借助于“第三方”实现具有依赖关系的对象之间的解耦。 如图所示:
由于引进了中间位置的“第三方”,也就是IOC容器,使得A、B、C、D这4个对象没有了耦合关系,
齿轮之间的传动全部依靠“第三方”了,全部对象的控制权全部上缴给“第三方”IOC容器。
所以,IOC容器成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,
把系统中的所有对象粘合在一起发挥作用,如果没有这个“粘合剂”,对象与对象之间会彼此失去联系,
这就是有人把IOC容器比喻成“粘合剂”的由来。
我们把上图中间的IOC容器隐藏,然后再来看看这套系统 :
我们现在看到的画面,就是我们要实现整个系统所需要完成的全部内容。这时候,A、B、C、D这4个对象之间已经没有了耦合关系,彼此毫无联系,这样的话,当你在实现A的时候,根本无须再去考虑B、C和D了,对象之间的依赖关系已经降低到了最低程度。
3、手写IOC【工厂模式->解耦】
我们通过使用工厂模式,实现了表现层——业务层、业务层——持久层的解耦。
【实现思路】:
工厂+反射+配置文件
【核心思想】:
【1】读取配置文件中类的全限定名通过反射机制创建对象。
【2】把创建出来的对象事先都存起来,当我们使用时可以直接从存储容器中获取。
存哪去?
由于我们是很多对象,肯定要找个集合来存。这时候有 Map 和 List 供选择。
到底选 Map 还是 List 就看我们有没有查找需求。有查找需求,选 Map。
所以我们的答案就是在应用加载时,创建一个 Map,用于存放bean对象。
我们把这个 map 称之为容器。
什么是IOC工厂
事先加载bean,并且提供一个直接获取bean的方法。
什么是控制反转
主动new对象方式--被动从容器中获取
代码演示
1、创建项目及J2EE三层架构
2、创建pojo对象
3、创建dao对象
4、创建service对象
5、创建controller对象
6、创建properties文件
7、factory工厂类【重点】
【1】事先创建集合容器
【2】事先加载properties文件内容
【3】反射机制实例化bean对象,并且放入集合容器
【4】公共的访问方法
projo
Account类
/**
* @Description:账户实体类
*/
public class Account {
//账户编号
private String Id;
//账户所有者
private String accountName;
//账户余额
private Float money;
public Account() {
}
public String getId() {
return Id;
}
public void setId(String id) {
Id = id;
}
public String getAccountName() {
return accountName;
}
public void setAccountName(String accountName) {
this.accountName = accountName;
}
public Float getMoney() {
return money;
}
public void setMoney(Float money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"Id='" + Id + '\\'' +
", accountName='" + accountName + '\\'' +
", money=" + money +
'}';
}
}
dao层
AccountDao接口
/**
* @Description:账户dao层
*/
public interface AccountDao {
/**
* @Description 新增
*/
void saveAccount();
/**
* @Description 删除
*/
void delAccount();
/**
* @Description 修改
*/
void updateAccout();
/**
* @Description 查询
*/
void findAccount();
}
AccountDaoImpl实现类
/**
* @Description:
*/
public class AccountDaoImpl implements AccountDao {
private static Account account;
static {
account = new Account();
account.setId("010101");
account.setAccountName("张三");
account.setMoney(2000F);
}
@Override
public void saveAccount() {
System.out.println("保存:"+account.toString());
}
@Override
public void delAccount() {
System.out.println("删除:"+account.toString());
}
@Override
public void updateAccout() {
System.out.println("修改:"+account.toString());
}
@Override
public void findAccount() {
System.out.println("查询:"+account.toString());
}
}
servic层
AccountService接口
/**
* @Description:用户业务层接口
*/
public interface AccountService {
/**
* @Description 新增
*/
void saveAccount();
/**
* @Description 删除
*/
void delAccount();
/**
* @Description 修改
*/
void updateAccout();
/**
* @Description 查询
*/
void findAccount();
void setAccountDao(AccountDao accountDao);
}
AccountServiceImpl实现类
/**
* @Description:用户业务层接口实现
*/
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
@Override
public void saveAccount() {
accountDao.saveAccount();
}
@Override
public void delAccount() {
accountDao.delAccount();
}
@Override
public void updateAccout() {
accountDao.updateAccout();
}
@Override
public void findAccount() {
accountDao.findAccount();
}
@Override
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
}
factory层
BeanFactory工厂类
在resources目录中建立db.properties
accountDao = com.example.spring.dao.impl.AccountDaoImpl
accountService = com.example.spring.service.impl.AccountServiceImpl
/**
* @Description:bean工厂
*/
public class BeanFactory {
//1、事先存储容器
private static Map<String, Object> map = new HashMap<>();
//2、加载配置文件
static {
Properties properties = new Properties();
try {
properties.load(BeanFactory.class.getClassLoader()
.getResourceAsStream("db.properties"));
Enumeration<?> enumeration = properties.propertyNames();
while (enumeration.hasMoreElements()) {
String key = (String) enumeration.nextElement();
String value = (String) properties.get(key);
//3、实例化bean
Object beanObject = Class.forName(value).newInstance();
//4、放入容器
map.put(key,beanObject);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//5、公共方法获得bean
public static Object getBean(String calssName){
return map.get(calssName);
}
}
controller层
ClientController测试类
/**
* @Description:
*/
public class ClientController {
@Test
public void saveAccount() {
AccountService accountService = (AccountService) BeanFactory.getBean("accountService");
accountService.setAccountDao((AccountDao) BeanFactory.getBean("accountDao"));
accountService.saveAccount();
}
}
4、IOC入门案例
案例分析
##思考
1、我们采用工厂+反射的方式实现了手写IOC工厂,那么spring-IOC的工厂是不是也类似?
spring框架提供了一个大工厂接口:ApplicationContext==》Beanfactroy
2、手写IOC中的配置文件类型是properties,那么spring-IOC的配置采取的是什么类型?
spring使用XML格式的文件存储配置
<bean id="唯一标识"
class="实现类的全限定名">
</bean>
3、spring-IOC是怎么加载配置文件的呢?
ApplicationContext工厂使用ClassPathXmlApplicationContext加载配置文件
4、手写IOC中的BeanFactory提供一个公共获得bean的方法,那spring-ioc是不是有类似的方法?
ApplicationContext工厂使用getBean以上是关于Java开发Spring之IOC详解第一篇(xml开发常用APIben标签DI依赖注入)的主要内容,如果未能解决你的问题,请参考以下文章
JAVAWEB开发之Spring详解之——Spring的入门以及IOC容器装配Bean(xml和注解的方式)Spring整合web开发整合Junit4测试