Spring之IOC(一)

1、Spring之IOC(一)

1.1、简介

  • 2002,首次推出了Spring框架的雏形:interface21框架!

  • Spring框架以interfance21框架为基础,经过重新设计,并不断丰富器内涵,于2004年3月24日,发布了1.0正式版。

  • Spring框架是一个开放源代码J2EE应用程序框架,由[Rod Johnson](https://baike.baidu.com/item/Rod Johnson/1423612)发起,是针对bean的生命周期进行管理的轻量级容器(lightweight container)

  • Spring理念:使用现有技术更加容易使用,本身就是一个大杂烩,整合了现有的技术框架!

    • SSH:Struct2+Spring+Hibernate
    • SSM: SpringMVC+Sping+Mybatis
  • 官方网址:https://spring.io/projects/spring-framework#overview

  • 官方下载地址:https://repo.spring.io/release/org/springframework/spring/

  • 官方仓库地址(依赖):https://mvnrepository.com/tags/spring

  • GitHub 源码: https://github.com/spring-projects/spring-framework

  • <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.0.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.0.RELEASE</version>
    </dependency>
    
    

1.2、优点

  • Spring是一个开源的免费的框架(容器)
  • Spring是一个轻量级的、非入侵式的框架
  • 控制反转(IOC),面向切面编程(AOP)
  • 支持(生命式)事务的处理,对框架整合的支持!
  • 适用范围:任何 Java 应用
  • Spring 的根本使命:简化 Java 开发

总结:Spring就是一个轻量级的控制反转(IOC)和面向切片编程(AOP)的框架!

1.3、组成

image-20210228145701631

1、SpringCore spring的核心功能:IOC容器,解决对象的创建及依赖关系

2、SpringWeb :spring对Web模块的支持

3、SpringDAO :spring对JDBC的支持

4、SpringORM :spring对ORM的支持

5、SpringAOP :切面编程

6、SpringEE: spring对JavaEE其他模块的支持

  • Data Access/Integration层包含有JDBC、ORM、OXM、JMS和Transaction模块。
  • Web层包含了Web、Web-Servlet、WebSocket、Web-Porlet模块。
  • AOP模块提供了一个符合AOP联盟标准的面向切面编程的实现。
  • Core Container(核心容器):包含有Beans、Core、Context和SpEL模块。
  • Test模块支持使用JUnit和TestNG对Spring组件进行测试。

1.4 拓展

image-20210228150903271
  • Spring Boot
    • 一个快速开发的脚手架
    • 基于Spring Boot可以快速的开发单个微服务。
    • 约定大于配置!
  • Spring Cloud
    • Spring Cloud 是基于SpringBoot实现的。

大多数公司都在使用Spring Boot进行快速开发,学习SpringBoot的前提,需要完全掌握Spring及SpringMVC!具有承上启下的作用。

弊端:发展太久,违背了原来的理念!配置十分繁琐,人称“地狱配置”!

2、IOC理论

原来写法:例子


创建一个Manven项目导入SpaingMVC的依赖

Dao层

  • UserDao接口

    package com.study.dao;
    //定义UserDao接口
    public interface UserDao {
     void getUser();
    }
    
    
  • UserDaoImpl实现类

    package com.study.dao;
    //实现UserDao接口的实现类
    public class UserDaoImpl implements UserDao{
     @Override
     public void getUser() {
         System.out.println("默认获取用户的数据!");
    
     }
    }
    

Service层

  • UserService 业务接口

    package com.study.service;
    
    public interface UserService {
     void getUser();
    
    
    }
    
    
  • UserServiceImpl 业务实现类

    • 使用组合方式业务层Service调用Dao层(把Dao引入Service层),但这是固定写死的(程序控制创建对象)
    package com.study.service;
    
    import com.study.dao.UserDao;
    import com.study.dao.UserDaoImpl;
    public class UserServiceImpl implements UserService{
     //使用组合方式业务层Service调用Dao层(把Dao引入Service层),但这是固定写死的(程序控制创建对象)
     private UserDao userDao = new UserDaoImpl();
     public void getUser() {
         userDao.getUser();
    
     }
    }
    
    

测试类Test

  • Test类

    import com.study.dao.UserDaoImpl;
    import com.study.service.UserService;
    import com.study.service.UserServiceImpl;
    
    //测试类
    public class MyTest {
     public static void main(String[] args) {
       //调用真实的业务(用户实际调用的是业务层,dao层不需要接触)
         UserService userService = new UserServiceImpl();
        //输出dao层的真正执行方法不会执行Service层,Service层只做的是调用dao层去查询结果
         userService.getUser();
    
    
     }
    }
    /*考虑如果用户需要增加多个需求如何处理?*/
    
    

    输出:默认获取用户的数据

考虑如果用户需要增加多个需求如何处理?

Dao层在加入MySql,Oracle需求

  • UserDaoMysqlImpl实现类

  • package com.study.dao;
    //增加MySql获取数据需求
    public class UserDaoMysqlImpl  implements UserDao{
     @Override
     public void getUser() {
         System.out.println("MySql获取用户数据!");
     }
    }
    
    
  • UserDaoOracleImpl 实现类

    package com.study.dao;
    //增加Oracle获取数据需求
    public class UserDaoOracleImpl implements UserDao{
     @Override
     public void getUser() {
         System.out.println("Oracle获取用户数据!");
     }
    }
    
    

此时Service层中增加MySql、业务需求是就需要更改Service调用Dao层(这是就会修改service层的代码)

  • package com.study.service;
    
    import com.study.dao.UserDao;
    import com.study.dao.UserDaoImpl;
    import com.study.dao.UserDaoMysqlImpl;
    import com.study.dao.UserDaoOracleImpl;
    public class UserServiceImpl implements UserService{
    //使用组合方式业务层Service调用Dao层(把Dao引入Service层),但这是固定写死的(程序控制创建对象)
     //private UserDao userDao = new UserDaoImpl();
     //增加MySql业务需求是就需要更改Service调用Dao层(这是就会修改service层的代码)
     private UserDao userDao = new UserDaoMysqlImpl();
     //增加Oracle业务需求又要改变service层
     //private UserDao userDao = new UserDaoOracleImpl();
     public void getUser() {
         userDao.getUser();
    
     }
    
     }
    }
    
    image-20210228214311564

这时就会出现由于客户的需求修改原有的代码的问题(程序适应不了用户的变更)

如何解决这一问题!思路:程序不动让客户端增调用(设计模式==接口思想)

利用set进行动态实现值得注入!

  • package com.study.service;
    
    import com.study.dao.UserDao;
    import com.study.dao.UserDaoImpl;
    import com.study.dao.UserDaoMysqlImpl;
    import com.study.dao.UserDaoOracleImpl;
    
    public class UserServiceImpl implements UserService{
    
     /*这时就会出现由于客户的需求修改原有的代码的问题(程序适应不了用户的变更)*/
     /*如何解决这一问题!思路:程序不动让客户端增调用(设计模式==接口思想)*/
     private UserDao userDao;
     //利用set进行动态实现值得注入!
     public void setUserDao(UserDao userDao) {
         this.userDao = userDao;
     }
     public void getUser() {
         userDao.getUser();
    
     }
    }
    
    

在Test类用户端创建对象

  • import com.study.dao.UserDaoImpl;
    import com.study.dao.UserDaoMysqlImpl;
    import com.study.dao.UserDaoOracleImpl;
    import com.study.service.UserService;
    import com.study.service.UserServiceImpl;
    
    //测试类
    public class MyTest {
     public static void main(String[] args) {
       //调用真实的业务(用户实际调用的是业务层,dao层不需要接触)
         UserService userService = new UserServiceImpl();
    
         ((UserServiceImpl) userService).setUserDao(new UserDaoOracleImpl());
    
         //输出dao层的真正执行方法不会执行Service层,Service层只做的是调用dao层去查询结果
         userService.getUser();
    
    
     }
    }
    
    
    

    输出:MySql获取用户数据!

    image-20210228214420146

在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求修改源代码!如果程序代码量十分大,修改一次的成本代价昂贵!

主观性发生改变:

  • 之前,程序是主动创建对象!控制权在程序员手上!
  • 使用set注入后,程序不再具有主动性,而是变成了被动的接受对象!这样就控制反转思想!

这种思想,从本质上解决了问题,我们程序员不用再去管理对象的创建了,系统的耦合性大大降低了,可以更加专注的在业务的实现上(直接在Dao层实现业务横向扩展,不用再管service层,在用户端直接创建对象调用)!这是IOC的原型

IoC:Inverse of Control(控制反转)

  • 读作“反转控制”,更好理解,不是什么技术,而是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由Spring框架来管理。

  • 传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建

谁控制谁?

  • 当然是IoC 容器控制了对象;

控制什么

  • 那就是主要控制了外部资源获取(不只是对象包括比如文件等)。

  • 正控:若要使用某个对象,需要自己去负责对象的创建

  • 反控:若要使用某个对象,只需要从 Spring 容器中获取需要使用的对象,不关心对象的创建过程,也就是把创建对象的控制权反转给了Spring框架;反转则是由容器来帮忙创建及注入依赖对象

为何是反转?

  • 因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;

哪些方面反转了?

  • 依赖对象的获取被反转了。

3、IOC本质

控制反转IOC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC 的一种方法,也有人认为DI只是IOC的另一种说法。没有IOC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了

image-20210228215556397

IOC是Spring框架的核心内容,使用多种方式完美的实现IOC,可以使用XML配置,也可以使用注解,新版本的Sping也可以零配置实现IOC。

作用:

  • 把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
  • IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。
  • IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。

4、DI依赖注入

DI—Dependency Injection,即“依赖注入”组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中

目的:

  • 并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。

 谁依赖于谁:

  • 当然是应用程序依赖于IoC容器

 为什么需要依赖:

  • 应用程序需要IoC容器来提供对象需要的外部资源

 谁注入谁:

  • 很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象

  注入了什么:

  • 就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)

Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器,程序使用时再从IOC容器中取出需要的对象。

image-20210228220233098

采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到零配置的目的。

<?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
     https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用Spring来创建对象,在Spring这些都成为Bean
类型  变量名 = new 类型();
Hello hello = new Hello()

id = 变量名
class = new的对象
property 相当于给对象中的属性设置一个值 —>对应的是set方法
没有set对应的方法是会报错的
在配置文件加载时,容器中的所有对象已经初始化了

-->
<bean id="helloSpring" class="com.study.dao.HelloSpring">
 <property name="name" value="Spring"></property>
</bean>
</beans>

控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式,在Sping中实现控制反转的是IOC容器,其实实现方法是依赖注入(Dependency Injection,DI)。

5、IOC创建对象案例:
  • 1、通过无参构造方法创建对象,默认的!
package com.study.dao;

public class HelloSpring {
 private String name;

 //使用无参构造
 public HelloSpring() {
     System.out.println("我是无参构造");
 }
 //使用有参构造
 public HelloSpring(String name) {
     this.name = name;
 }
 public String getName() {
     return name;
 }
 public void setName(String name) {
     this.name = name;
 }
 @Override
 public String toString() {
     return  "HellO!{"+
              "name'"+name+'\''+
              '}';
 }
}
public class MyTest {
 public static void main(String[] args) {
    //获取Spring的上下文对象!
    ApplicationContext context = new FileSystemXmlApplicationContext("spring-02-helloSpring/src/beans.xml");
     //我们的对象现在都在Spring中管理了,我们要是用,直接去里面取出来就可了!
    HelloSpring hs= context.getBean("helloSpring", HelloSpring.class);
    System.out.println(hs.toString());
 }
}

beans.xml配置如上

  • 2、有参构造方法创建对象

    • 第一种下标赋值
    <bean id="helloSpring" class="com.study.dao.HelloSpring">
     <!--<property name="name" value="Spring"></property>-->
     <!--有参构造使用第一种下标赋值-->
     <constructor-arg index="0" value="yyb"></constructor-arg>
    </bean>
    
    • 第二种使用通过类型(不建议使用)
    <bean id="helloSpring" class="com.study.dao.HelloSpring">
     <!--有参构造使用第二种类型赋值-->
    	<constructor-arg type="java.lang.String" value="yyb2"></constructor-arg>
    </bean>
    
    • 第三种通过参数名赋值
    <bean id="helloSpring" class="com.study.dao.HelloSpring">
    <!--有参构造使用第三种通过参数名赋值-->
     <constructor-arg name="name" value="yyb3"></constructor-arg>
    </bean>   
    
  • 3.工厂创建对象

    • 工厂类:非静态方法创建对象

    1、 添加创建工厂类

    package com.study.dao;
    //创建工厂类
    public class ObjectFactory {
        //实例方法创建对象
        public HelloSpring getInstance(){
            return new HelloSpring("调用实例方法");
        }
         
        public  static HelloSpring getStaticInstance(){
            return new HelloSpring("调用静态方法");
        }
        
    }
    
    
    </bean>
         <!--工厂类先创建对象-->
        <bean id="factory" class="com.study.dao.ObjectFactory"></bean>
        <!--再创建HelloSpring类对象-->
        <bean id="hellospring" factory-bean="factory" factory-method="getInstance"></bean>
    </beans>
    
    public class MyTest {
        public static void main(String[] args) {
           //获取Spring的上下文对象!
           ApplicationContext context = new FileSystemXmlApplicationContext("spring-02-helloSpring/src/beans.xml");
            //我们的对象现在都在Spring中管理了,我们要是用,直接去里面取出来就可了!
           HelloSpring hs= context.getBean("helloSpring2", HelloSpring.class);
            System.out.println(hs.toString());
        }
    }
    

    输出:HellO!{name'调用实例方法'}

    • 工厂类静态方法创建对象

      <beans>
      <!--工厂静态方法-->
         <bean id="staticmethod" class="com.study.dao.ObjectFactory" factory-method="getStaticInstance">     </bean>
      </beans>
      
      public class MyTest {
          public static void main(String[] args) {
             //获取Spring的上下文对象!
             ApplicationContext context = new FileSystemXmlApplicationContext("spring-02-helloSpring/src/beans.xml");
              //我们的对象现在都在Spring中管理了,我们要是用,直接去里面取出来就可了!
              HelloSpring hs= context.getBean("staticmethod", HelloSpring.class);
              System.out.println(hs.toString());
          }
      }
      
      

      输出:HellO!

注意点:

  • 使用Spring来创建对象,在Spring这些都成为Bean

  • 类型 变量名 = new 类型();
    Hello hello = new Hello()

  • id = 变量名
    class = new的对象

  • property 相当于给对象中的属性设置一个值 —>对应的是set方法

  • 没有set对应的方法是会报错的

  • 在配置文件加载时,容器中的所有对象已经初始化了

posted @ 2021-03-06 17:10  yyb1024  阅读(222)  评论(0编辑  收藏  举报