返回

SSM框架01-IoC和DI

Spring简介、在IDEA使用Spring、Bean的使用

Spring简介

Spring是什么

Spring是分层的Java SE/EE应用full-stack轻量级开源框架,以IoC(Inverse Of Control:反转控制)和AOP(Aspect Oriented Programming:面向切面编程)为内核。

提供了展现层 SpringMVC 和持久层 Spring JDBCTemplate 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE 企业应用开源框架。

Spring的优势

  • 方便解耦,简化开发
    • 通过 Spring 提供的IoC容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
  • AOP 编程的支持
    • 通过 Spring的 AOP 功能,方便进行面向切面编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松实现。
  • 声明式事务的支持
    • 可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务管理,提高开发效率和质量。
  • 方便程序的测试
    • 可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情
  • 方便集成各种优秀框架
    • Spring对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的支持。
  • 降低 JavaEE API 的使用难度
    • Spring对 JavaEE API(如 JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些 API 的使用难度大为降低。
  • Java 源码是经典学习范例
    • Spring的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对Java 设计模式灵活运用以及对 Java技术的高深造诣。它的源代码无意是 Java 技术的最佳实践的范例。

Spring的体系结构

Spring入门

Spring程序开发步骤

之前开发是在业务层调用DAO层来new对象,现在借助Spring框架来实现解耦合:先将DAO配置到xml文件中,然后通过Spring框架来读取xml配置文件,最后在业务层通过Spring提供的API来得到对象。

简而言之,就是将对象的创建权交给了Spring。

  1. 导入 Spring 框架对应的jar包
  2. 编写 Dao 接口和实现类
  3. 创建 xml 配置文件
  4. 在 xml 配置文件中配置 UserDaoImpl
  5. 使用 Spring 的 API 获得 Bean 实例

解耦的意义:当业务对象改变时,只需要改变xml配置文件,而不需要修改业务层代码

使用IDEA实现Sping解耦

创建javaweb项目

借助Maven来进行项目构建和依赖管理。不会使用Maven的小伙伴,可以看我之前的Maven笔记:传送门

这里不做赘述

导入Spring框架对应的jar包

借助Maven来进行依赖管理,在pom.xml文件中写入spring开发的基本包坐标

1
2
3
4
5
6
7
  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.0.5.RELEASE</version>
    </dependency>
  </dependencies>

编写Dao接口和实现类

main\java下新建UserDao接口类

1
2
3
4
5
package com.wyatt.dao;

public interface UserDao {
    public void save();
}

com.wyatt.dao下新建UserDaoImpl实现类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package com.wyatt.dao.impl;

import com.wyatt.dao.UserDao;

public class UserDaoImpl implements UserDao {

    public void save() {
        System.out.println("save running...");
    }
}

在 xml 配置文件中配置 UserDaoImpl

main\resources下新建xml配置文件,取名叫applicationContext

1
2
3
4
5
6
7
8
<?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.wyatt.dao.impl.UserDaoImpl"></bean>
    
</beans>

使用 Spring 的 API 获得 Bean 实例

main\java中新建一个测试类UserDaoDemo,在该类的方法中使用 Spring 的 API 来获得 Bean 实例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package com.wyatt.demo;

import com.wyatt.dao.UserDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserDaoDemo {
    public static void main(String[] args) {
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = (UserDao) app.getBean("userDao");
        userDao.save();
    }
}

在控制台可以看到打印save running...,说明通过Spring框架提供的API调用UserDao对象的save()方法成功。

Spring配置文件

Bean标签基本配置

用于将配置对象交由Spring来创建。

默认情况下它调用的是类中的无参构造函数,如果没有无参构造函数则不能创建成功。

基本属性:

  • id:Bean实例在Spring容器中的唯一标识
  • class:Bean的全限定名称
1
<bean id="userDao" class="com.wyatt.dao.impl.UserDaoImpl"></bean>

Bean标签范围配置

scope:指对象的作用范围,取值如下:

取值范围 说明
singleton 默认值,单例的
prototype 多例的
request WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中
session WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中
global session WEB 项目中,应用在 Portlet 环境,如果没有 Portlet 环境那么globalSession 相当于 session

验证singleton

src\test\java下新建测试类SpringTest

借助junit来进行单元测试,在pom.xml中引入junit的相关依赖

1
2
3
4
5
6
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

SpringTest测试类中编写一个测试方法test1()测试singleton的作用范围

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class SpringTest {

    @Test
    public void test1(){
        // 获取Spring客户端
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 调用Spring客户端的getBean(id标识)方法
        UserDao userDao1 = (UserDao) app.getBean("userDao");
        UserDao userDao2 = (UserDao) app.getBean("userDao");
        System.out.println(userDao1);
        System.out.println(userDao2);
    }
}

从控制台的打印结果可以看出,两次getBean()获得的对象地址相同,是同一个对象。这意味如果Bean的作用范围配置为singleton,则在Spring容器中该Bean对象只有一个。

验证prototype

xml配置文件中配置Bean作用范围为prototype

1
<bean id="userDao" class="com.wyatt.dao.impl.UserDaoImpl" scope="prototype"></bean>

上面验证singleton的代码不变,重新执行

从控制台的打印结果可以看出,两次getBean()获得的对象地址不同,不是同一个对象。这意味如果Bean的作用范围配置为prototype,则在Spring容器中该Bean对象可以有多个。

小结

当scope的取值为singleton时

  • Bean的实例化个数:1个

  • Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例

  • Bean的生命周期:

    • 对象创建:当应用加载,创建容器时,对象就被创建了

    • 对象运行:只要容器在,对象一直活着

    • 对象销毁:当应用卸载,销毁容器时,对象就被销毁了

当scope的取值为prototype时

  • Bean的实例化个数:多个
  • Bean的实例化时机:当调用getBean()方法时实例化Bean
    • 对象创建:当使用对象时,创建新的对象实例
    • 对象运行:只要对象在使用中,就一直活着
    • 对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收了

Bean生命周期配置

  • init-method指定类中的初始化方法名称
  • destroy-method指定类中销毁方法

Bean实例化三种方式

  • 无参构造方法实例化
  • 工厂静态方法实例化
  • 工厂实例方法实例化

工厂是一个java类,专门用来生产对象

Bean的依赖注入入门

之前的实践都是借助Spring在Dao层产生对象,而在实际开发中,业务层和web层也需要产生对象。

main\java\com\wyatt下新建业务层中的UserService接口

1
2
3
4
5
package com.wyatt.service;

public interface UserService {
    public void getDaoObject();
}

main\java\com\wyatt\service下创建接口的实现类UserServiceImpl

在业务层中通过Spring获取dao层对象,并调用它的save()方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class UserServiceImpl implements UserService {

    public void getDaoObject() {
        // 获取Spring客户端
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 调用Spring客户端的getBean(id标识)方法
        UserDao userDao = (UserDao) app.getBean("userDao");
        userDao.save();
    }
}

main\java\com\wyatt\demo中新建一个模拟web层中的类UserController

在web层调用业务层来获取dao层对象

1
2
3
4
5
6
public class UserController {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        userService.getDaoObject();
    }
}

更进一步,将业务层的对象也配置到xml文件中

1
<bean id="userService" class="com.wyatt.service.impl.UserServiceImpl"></bean>

在web层借助Spring来获取service层对象,并调用service层对象的getDaoObject()方法,在service层借助Spring来获取dao层对象,并调用dao层对象的save()方法

1
2
3
4
5
6
7
public class UserController {
    public static void main(String[] args) {
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) app.getBean("userService");
        userService.getDaoObject();
    }
}

Bean的依赖注入分析

上例中的UserService实例和UserDao实例都存在于Spring容器中,当前的做法是在容器外部获得UserService实例和UserDao实例,然后在容器外部将Dao组装到Service中。

因为UserService和UserDao都在Spring容器中,而web层最终程序直接使用的是UserService,所以可以在Spring容器中,将UserDao设置到UserService内部。

Bean的依赖注入概念

依赖注入(Dependency Injection):它是 Spring 框架核心 IOC 的具体实现。

在编写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。IOC 解耦只是降低他们的依赖关系,但不会消除。例如:业务层仍会调用dao层的方法。

那这种业务层和dao层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了。简单的说,就是坐等框架把dao层对象传入业务层,而不用我们自己去获取。

Bean的依赖注入方式

将UserDao注入到UserService内部

  • 构造方法
  • set方法

set方法注入

  1. UserServiceImpl内部创建一个UserDao属性,设置该属性的set()方法。
  2. 因为在依赖注入后,Spring容器内部会自动把dao层对象传入业务层,而不用我们自己去获取,所以删除业务层手动获取dao对象的语句。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void getDaoObject() {
        userDao.save();
    }
}

xml配置文件中进行依赖注入,告诉Spring各个对象间的依赖关系

1
2
3
4
<bean id="userDao" class="com.wyatt.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.wyatt.service.impl.UserServiceImpl">
    <property name="userDao" ref="userDao"></property>
</bean>

构造方法注入

UserServiceImpl内写一个有参构造方法和一个无参构造方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    public UserServiceImpl() {
    }

    public void getDaoObject() {
        userDao.save();
    }
}

xml配置文件中进行依赖注入,告诉Spring各个对象间的依赖关系

1
2
3
4
<bean id="userDao" class="com.wyatt.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.wyatt.service.impl.UserServiceImpl">
    <constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>

Bean依赖注入的数据类型

上面的操作,都是注入的引用Bean,除了对象的引用可以注入,普通数据类型集合等都可以在容器中进行注入。

可以注入三种数据类型

  • 引用数据类型
  • 普通数据类型
  • 集合数据类型

之前的操作都是对UserDao对象的引用进行注入的。下面将以set方法注入为例,演示普通数据类型集合数据类型的注入。

普通数据类型的注入

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
public class UserDaoImpl implements UserDao {

    private String username;
    private int age;

    public void setUsername(String username) {
        this.username = username;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void save() {
        System.out.println(username+"===="+age);
        System.out.println("save running...");
    }
}
1
2
3
4
<bean id="userDao" class="com.wyatt.dao.impl.UserDaoImpl">
    <property name="username" value="wyatt"></property>
    <property name="age" value="20"></property>
</bean>

集合数据类型的注入

方法大体同上例,不再赘述。具体代码可以在我的GitHub仓库中查看。

引入其他的配置文件

实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,而在Spring主配置文件通过import标签进行加载。

1
<import resource="applicationContext-xxx.xml"/>

Spring配置文件小结

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<bean>标签
	id属性:在容器中Bean实例的唯一标识,不允许重复
	class属性:要实例化的Bean的全限定名
	scope属性:Bean的作用范围,常用是Singleton(默认)和prototype
	<property>标签:属性注入
		name属性:属性名称
		value属性:注入的普通属性值
		ref属性:注入的对象引用值
		<list>标签
		<map>标签
		<properties>标签
	<constructor-arg>标签
<import>标签:导入其他的Spring的分文件

Spring相关API

ApplicationContext的继承体系

1
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
  • ApplicationContext:接口类型,代表应用上下文,可以通过其实例获得 Spring 容器中的 Bean 对象

ApplicationContext的三个实现类

  • ClassPathXmlApplicationContext:它是从类的根路径下加载配置文件,推荐使用这种。
  • FileSystemXmlApplicationContext:它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
  • AnnotationConfigApplicationContext:当使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。

getBean方法的使用

1
2
3
4
5
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
// 方法1 容器中的一个全限定名可以有多个Bean
UserService userService1 = (UserService) applicationContext.getBean("userService");// id标识
// 方法2 容器中的一个全限定名只能有一个Bean
UserService userService2 = applicationContext.getBean(UserService.class); // 对象类型
Built with Hugo
Theme Stack designed by Jimmy