Spring IoC快速入门

【目录】
2.1 Spring的开发包
2.2 开发环境测试搭建
2.3 业务代码编写
2.4 IoC和DI
  2.4.1 最基本的IoC控制反转的实现
  2.4.2 DI依赖注入的实现
2.5 Spring的工厂

2 Spring IoC快速入门

Spring核心内容的基本开发步骤:

  • 下载开发包,导入jar包
  • 编写代码(基础代码和调用代码)
  • 编写配置文件

2.1 Spring的开发包

下载网址

开发包目录结构

2.2 开发环境测试搭建

回顾利用Maven搭建第一个Spring测试项
新建Web工程:spring_day1。pom.xml代码如下。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.test</groupId>
  <artifactId>spring_day1</artifactId>
  <packaging>war</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>spring_day1 Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
   <!-- Spring依赖 -->
    <!-- 1.Spring核心依赖 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>4.3.22.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.3.22.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>4.3.22.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-expression</artifactId>
        <version>4.3.22.RELEASE</version>
    </dependency>
  <!-- 2.Spring 日志依赖 -->
    <!-- 日志框架 -->
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.1.3</version>
    </dependency>
    <!-- 日志 -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
  </dependencies>
  <build>
    <finalName>spring_day1</finalName>
  </build>
</project>

在接下来的学习中,日志统一采用log4j,日志框架根据整合情况再决定。

2.3 业务代码编写

采用传统方式模拟用户登录,思路如下:

示意图

下面开始具体编写步骤。


在src/main/java目录下创建包和类

具体代码见下:
数据层接口 UserDAO.java

package dao;
//用户操作dao层
public interface UserDAO {
    //从数据库查询数据
    public void findUserByUsernameAndPassword();
}

数据层实体类UserDAOImpl.java

package dao;

public class UserDAOImpl implements UserDAO{
    @Override
    public void findUserByUsernameAndPassword() {
        System.out.println("dao层,向数据库查询数据了");
    }

}

业务层接口UserService .java

package service;
//用户操作
public interface UserService {
    //模拟用户登录
    public void login();
}

业务层实体类UserServiceImpl.java

package service;

import dao.UserDAO;
import dao.UserDAOImpl;

public class UserServiceImpl implements UserService{
public void login() {
    System.out.println("service层被调用了");
    UserDAO userDAO = new UserDAOImpl();
    userDAO.findUserByUsernameAndPassword();
    }
}

测试类SpringTest.java

package test;

import org.junit.Test;

import service.UserService;
import service.UserServiceImpl;

public class SpringTest {
    @Test
    public void test(){
        //测试serivce
        UserService userService = new UserServiceImpl();
        userService.login();
    }
}

完成以上代码以后,进行测试。

测试结果

虽然完成了功能,但是可以看到,代码过于耦合,上层代码过度依赖于下一层代码的实现。例如,通过业务层去查询存在于数据层的数据,先必须现在业务层准备一个数据层的实例对象。

UserDAO userDAO = new UserDAOImpl(); 

在传统的代码中,经常是这样的情况:如果有某个对象A依赖于其他对象B,在使用时,需要先创建所依赖的对象B,传入对象A,然后才能对对象A进行操作。这种方式提高了代码的耦合度,十分不利于重构和维护。(为什么要高内聚低耦合?

为了解决这种耦合,Spring采用了IoC(Inverse of Control,控制反转)的思想。这种思想,简单来说,就是引入工厂(也称第三者/IoC容器),将原来在程序中手动创建管理的对象,交给工厂来创建管理。在Spring框架中,这个工厂就是Spring中的工厂。

示意图

2.4 IoC和DI

提到IoC(控制反转),就不得不提到DI(依赖注入)。
以下是常见的两个问题。

Q1:怎样理解DI?
依赖:所有类的对象,都依赖于IoC容器来进行管理。
注入:类中的某个对象,需要某种外部资源(包括对象、资源、常量数据),无需自己去引用,IoC容器直接注入给它。这个对象不需要关心具体的资源来怎么来的,反正是IoC容器统统搞定。(提一句:Spring通过反射机制来实现注入。)
综上,下个定义,应用程序依赖于IoC容器来注入某个对象所需要的外部资源(包括对象、资源、常量数据),这就是DI(通过配置的方式为对象赋值/初始化/注入)。

Q2:许多人把IoC和DI这两个术语理解为同一个事物。怎样恰当的理解它们的关系?
笔者认为,可以这样理解:
IoC是一种思想,也是一种设计模式——把我们自己创建对象的控制权交给IoC容器,让IoC容器负责创建对象。
在IoC容器创建过程中,有一个重点——在系统运行中,动态的向某个对象提供它所需要的其他外部资源(包括对象、资源、常量数据)。
怎样能完成这个重点?可以通过DI来实现——给对象动态的注入外部资源(包括对象、资源、常量数据)。
综上,DI和IoC的关系就是这样——IoC的实现需要DI技术的支持。

PS:给对象注入一个对象,就实现了对对象间依赖关系的“一键”管理,不需要多个相互依赖的类new来new去。什么是依赖关系,看下面的示意代码。

class B {
           private A a;   //A是B的依赖对象,两者间存在依赖关系
}

在spring_day1工程中,userService依赖于userDAO。现在,仍然使用spring_day1工程来进行下面两个小节的演示。
2.4.1小节展示最基本的IoC控制反转的实现,通过Spring的工厂获取一个Bean,不实现动态的资源管理(UserService得到Spring创建好的bean对象userDAO,而SpringTest仍然使用传统方式新建对象userService)。
2.4.2小节展示DI依赖注入的实现,通过Spring的工厂获取自动注入好的资源的Bean,依赖关系的管理由配置文件实现。(SpringTest直接获取bean对象userService,而在userService这个bean里,已经注入好了userDAO)

2.4.1 最基本的IoC控制反转的实现

效果:UserService得到Spring创建好的bean对象userDAO,而SpringTest仍然使用传统方式新建对象userService。
过程:先添加配置文件,让Spring工厂创建一个bean对象userDAO。再修改UserServiceImpl实体类,从Spring工厂获取bean对象userDAO。

有两种spring配置方式:基于XML配置方式、基于注解配置方式。我们在这里采用前一种进行演示。对于这两种配置方式,后续会有更详细的讲解。

下面开始具体的演示。

1.新建配置文件:applicationContext.xml(位置:src/main/resources目录或者 WEB-INF目录、习惯性命名:applicationContext.xml)。

ApplicationContext直译为应用上下文,用来加载Spring框架配置文件,构建Spring的工厂对象,它也称之为Spring容器的上下文对象,也称之为Spring的容器。

引入xml的头部信息bean schema约束,可以参考规范文档中的的xsd-config.html。在引入之前先本地配置开发包中的xsd文件。

配好以后,在xml文件内的<beans></beans>标签内,按alt加斜杠,会出现提示,说明配置成功了。

i出现提示

也可以参看这里Eclipse安装STS插件使用更便捷的导入方式。

这里提供applicationContext.xml代码:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:p="http://www.springframework.org/schema/p"
       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">
<!-- 快速入门 -->
<!-- IoC的配置 -->
<bean id="userDAO" class="dao.UserDAOImpl"></bean>
</beans>

在后续讲解中,将详细说明配置文件xml的编写。在这里可以先了解一下Bean元素基本属性

2.通过Spring的工厂获取Bean完成相关操作,基本过程是:在程序中读取Spring配置文件,得到Spring的Bean工厂,通过Spring框架获得Bean,完成相应操作。

修改UserServiceImpl.java代码如下:

package service;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import dao.UserDAO;


public class UserServiceImpl implements UserService{

    @Override
    public void login() {
        System.out.println("service层被调用了");
        //传统方式
        //UserDAO userDAO = new UserDAOImpl();
        
       //IoC方式
       //ApplicationContext认为就是Spring工厂
        //ClassPathXmlApplicationContext:参数就是spring的核心配置文件的名字(我们写的那个xml),这个函数的作用就是自动寻找xml文件
        ApplicationContext applicationContext =new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取bean对象(spring帮忙new的对象),参数就是我们写的bean的id
        //applicationContext.getBean("userDAO");然后换对象格式
       UserDAO userDAO=(UserDAO) applicationContext.getBean("userDAO");
       //消除警告: - Resource leak: 'applicationContex' is never closed的处理
       //导入包import org.springframework.context.ConfigurableApplicationContext;
       //添加这条语句
       ((ConfigurableApplicationContext)applicationContext).close();       
       userDAO.findUserByUsernameAndPassword();

    }
}

提示:出现new ClassPathXmlApplicationContext错误,是因为Eclipse默认为我们导入一个不符合当前创建容器所需的ApplicationContext 包,需要手动替换后解除错误。
默认导入的包:

import org.apache.catalina.core.ApplicationContext;

需要手动替换的包:

import org.springframework.context.ApplicationContext;

添加log4j.properties文件后(内容参考这里),进行测试。

测试结果

2.4.2 DI依赖注入的实现

  • 注入方式:set方法属性注入、构造器参数注入、接口注入 (后续再详细展开讲解);
  • 注入类型:值类型(八大基本数据类型)、引用类型(String、自定义对象等)、复杂类型(Array、List、Set、Map、Properties)。

在spring_day1工程中,userService依赖于userDAO,也就说依赖对象是userDAO、注入对象userService,现在用set方法实现DI依赖注入,把bean对象userDAO注入给bean对象userService。

效果:SpringTest直接获取bean对象userService,而在userService这个bean里,已经注入好了userDAO。
过程:先在UserServiceImpl实体类中加一个setUserDAO(),在SpringTest中获取spring工程加工好的userService。然后通过配置文件,让Spring工厂实例化好一个bean对象userDAO、一个bean对象userService,同时,用属性把bean对象userDAO注入给bean对象userService,就结束了。

下面开始具体的演示。

修改业务层代码UserServiceImpl.java如下:

package service;

import dao.UserDAO;


public class UserServiceImpl implements UserService{
      //这里写依赖对象的set方法
    private UserDAO userDAO;
    
    public void setUserDAO(UserDAO userDAO){
        this.userDAO=userDAO;
}
            
    @Override
    public void login() {
        System.out.println("service层被调用了");
       //传统方式
        //UserDAO userDAO = new UserDAOImpl();
        
       //IoC方式
       //ApplicationContext认为就是Spring工厂
        //ClassPathXmlApplicationContext:参数就是spring的核心配置文件的名字(我们写的那个xml),这个函数的作用就是自动寻找xml文件
        //ApplicationContext applicationContext =new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取bean对象(spring帮忙new的对象),参数就是我们写的bean的id
        //applicationContext.getBean("userDAO");然后换对象格式
       //UserDAO userDAO=(UserDAO) applicationContext.getBean("userDAO");
       //消除警告: - Resource leak: 'applicationContex' is never closed的处理
       //导入包import org.springframework.context.ConfigurableApplicationContext;
       //添加这条语句
       //((ConfigurableApplicationContext)applicationContext).close();       
       userDAO.findUserByUsernameAndPassword();
    }
}

修改测试类SpringTest.java如下:

package test;

import org.junit.Test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;

import service.UserService;

public class SpringTest {
    @Test
    public void test(){
        //传统创建
        //UserService userService = new UserServiceImpl();
        
        //IoC方式
        //1.获取Spring工厂
         ApplicationContext applicationContext =new ClassPathXmlApplicationContext("applicationContext.xml");
        //2.获取工厂制造的bean对象
        UserService userService = (UserService) applicationContext.getBean("userService");
        //消除警告: - Resource leak: 'applicationContex' is never closed的处理
        //导入包import org.springframework.context.ConfigurableApplicationContext;
        //添加这条语句
       ((ConfigurableApplicationContext)applicationContext).close();
        userService.login();
    }
}

修改applicationContext.xml如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:p="http://www.springframework.org/schema/p"
       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">
<!-- 快速入门 -->
<!-- IoC的配置 -->
<bean id="userDAO" class="dao.UserDAOImpl"></bean>
<!-- 将DAO传入Service -->
    <!-- 将service也交给spring管理(new) -->
    <bean id="userService" class="service.UserServiceImpl">
        <!-- 依赖关系,在这里维护-->
        <!-- userService依赖于userDAO,userService需要用到userDAO,那么创建一个userDAO的bean,并注入给userService的bean里
        name:UserServiceImpl类中被注入对象的setter的名字:例如:setXxx,名字xxx(小写的)
        ref:要注入的具体bean对象的引用,写被依赖的bean的名字
         -->
        <property name="userDAO" ref="userDAO"/>
    </bean>
</beans>

进行测试。

测试结果
示意图

2.5 Spring的工厂

Spring的工厂

前文我们用到的ApplicationContext,直译为应用上下文,它被用来加载Spring框架配置文件,构建Spring的工厂对象。它也称之为Spring容器的上下文对象、Spring的容器。
从图中可以看到,ApplicationContext 只是BeanFactory(Bean工厂,Bean就是一个java对象) 一个子接口,但由于ApplicationContext比它的顶层接口对象BeanFactory 更加强大, 所以现在开发基本没人使用它的顶层接口对象BeanFactory。
提示:后面还有个FactoryBean,注意区别。

Spring工厂有两种直接获取,见下图:

Spring工厂的直接获取(两种方式)

补充:bean的获取除了根据名称获取,也可以根据类型(具体类型、接口类型)获取,见下图:

bean获取的两种方式
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容