Java开发Spring之IOC详解第一篇(xml开发常用APIben标签DI依赖注入)

Posted ahcfl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java开发Spring之IOC详解第一篇(xml开发常用APIben标签DI依赖注入)相关的知识,希望对你有一定的参考价值。

文章目录

一、 Spring概述

Sping官网

## 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依赖注入)的主要内容,如果未能解决你的问题,请参考以下文章

spring之IOC实现的其他方式

JAVAWEB开发之Spring详解之——Spring的入门以及IOC容器装配Bean(xml和注解的方式)Spring整合web开发整合Junit4测试

(转)java之Spring(IOC)注解装配Bean详解

Spring框架 之IOC容器 和AOP详解

spring之旅第一篇-初识spring

Spring学习IOC详解