JavaWeb - Sping & Sping IOC

JavaWeb - Sping & Sping IOC

目录

1 Sping

1.1 Sping 概念

Spring是分层的 Java SE/EE应用 full-stack(全栈式) 轻量级开源框架

  • 全栈式:

    • 对各种主流技术和框架都进进行了整合,同时对三层架构都提供了解决方案
  • 轻量级:

    • 轻量级和重量级的划分主要依据服务使用的多少,启动时资源加载的多少及耦合度等等

提供了表现层 SpringMVC

持久层 Spring JDBC Template

业务层事务管理,对象控制等众多的企业级应用技术

还能整合开源世界众多著名的第三方框架和类库

1.1.1 Sping 两大核心:
  • IOC(Inverse Of Control:控制反转),把对象的创建权交给sping

  • AOP(Aspect Oriented Programming:面向切面编程),在不修改源码的情况下对方法进行增强

1.1.2 耦合和解耦的概念

耦合:程序间的依赖关系

解耦:降低程序间的依赖关系,并不是完全消除

实例:

//1. 注册驱动 存在编译期依赖,这就是耦合重的体现
//new 关键字就会存在编译期依赖,解耦思路为去掉new改用反射
DriverManager.registerDriver(new com.mysql.jdbc.Driver());

解耦:使用反射机制让其编译期不依赖,运行期才依赖,但还存在硬编码问题
Class.forName("com.mysql.jdbc.Driver");

//最终解耦思路为:配置文件+反射,自定义IOC用到此思路
1.1.3 Sping 的优点
  • 方便解耦,简化开发

    • Spring就是一个容器,可以将所有对象创建和关系维护交给Spring管理
  • AOP编程的支持

    • Spring提供面向切面编程,方便实现程序进行权限拦截,运行监控等功能
  • 声明式事务的支持

    • 通过配置完成事务的管理,无需手动编程
  • 方便测试,降低JavaEE API的使用

    • Spring对Junit4支持,可以使用注解测试
  • 方便集成各种优秀框架

    • 不排除各种优秀的开源框架,内部提供了对各种优秀框架的直接支持

2 Spring IOC

2.1 IOC的概念

控制反转(Inverse Of Control)不是什么技术,而是一种设计思想。它的目的是指导我们设计出更加松耦合的程序

  • 控制:在java中指的是对象的控制权限(创建、销毁)
  • 反转:指的是对象控制权由原来 由开发者在类中手动控制 反转到 由Spring容器控制

2.2 自定义IOC容器

核心思想为将对象的创建通过反射机制来实现进而实现解耦,再通过配置文件的方式来实现反射创建对象时产生的硬编码问题

步骤:

  • 创建maven项目,导入需要使用包的坐标
<dependencies>
//dom4j 用来解析xml文件
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
  • 编写userDao接口及实现类,编写userService接口及实现类并在实现类中创建userDao实例对象并调用方法,最后编写测试类创建userService实例对象并调用方法
  • 编写bean.xml 文件,将需要容器管理类编写到该文件中
<beans>
    <bean id = "userDao" class = "com.lagou.dao.userDaoImpl"></bean>
    <bean id="userService" class="com.lagou.service.userServiceImpl"></bean>
</beans>
  • 编写BeanFactory工厂类,用来完成ioc容器的初始化加载和分配容器中的对象
    • 静态代码块用来加载配置文件并解析,获取到配置文件中类的路径和名称,再通过该路径来创建该类的对象
    • 声明一个Map类型的成员变量用来存储解类与其对应的对象
    • 声明一个方法用来通过类名获取对象
public class BeanFactory {
    private static Map<String, Object> ioc = new HashMap<>();

    static {
        try {
        //1.将xml文件读取,通过调用类加载器中的方法
            InputStream stream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
		//2.解析xml文件
            SAXReader saxReader = new SAXReader();

            Document document = saxReader.read(stream);
		//3.编写xpath来指定需要获取的标签
            String xpath = "//bean";
		//4.获取所有的bean 标签
            List<Element> list = document.selectNodes(xpath);
		//将标签中的元素取出并新建对象再放入集合中
            for (Element element : list) {
                ioc.put(element.attributeValue("id"),Class.forName(element.attributeValue("class")).newInstance());
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
//声明方法用来获取对象
    public static Object getBean(String id){
        return ioc.get(id);
    }
}
  • 编写测试类
userService service = (userService) BeanFactory.getBean("userService");
        service.save();

2.3 Sping 快速使用

  • 创建项目并导入相关坐标
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
  • 创建Sping 的核心配置文件,一般使用applicationContext.xml
// applicationContext.xml

<?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">

</beans>
  • 在核心配置文件中为Bean对象进行配置
<bean id="" class=""></bean>
  • 创建ApplicationContext对象,执行getBean
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
XXX xxx = (XXX) applicationContext.getBean("xxx");

2.4 Spring API

2.4.1 Spring 体系

JavaWeb - Sping & Sping IOC

2.4.2 BeanFactory 接口

BeanFactory是 IOC 容器的核心接口,它定义了IOC的基本功能

  • 通过XmlBeanFactory 实现类来加载核心配置文件
BeanFactory beanFactory =
new XmlBeanFactory(new
ClassPathResource("applicationContext.xml"));
2.4.3 ApplicationContext 接口

代表应用上下文对象,可以获得spring中IOC容器的Bean对象

常用实现类:

  • ClassPathXmlApplicationContext

    • 是从类的根路径下加载配置文件 推荐使用这种
  • FileSystemXmlApplicationContext

    • 从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置
  • AnnotationConfigApplicationContext

    • 使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解
ApplicationContext app =
new ClassPathXmlApplicationContext("applicationContext.xml");
2.4.4 BeanFactory 和 ApplicationContext 的区别
  • BeanFactory 在第一次调用 getBean() 方法的时候创建指定对象的实例
//该语句执行并没有创建实例
new XmlBeanFactory(new ClassPathResource("xxx.xml"));
  • ApplicationContext在启动容器时就加载和创建了所有对象的实例
2.4.5 常用方法 - getBean()
  • Object getBean(String name);

    • 根据Bean的id从容器中获得Bean实例,返回是Object,需要强转
  • T getBean(Class requiredType);

    • 根据类型从容器中匹配Bean实例,当容器中相同类型的Bean有多个时,则此方法会报错
    • 一个接口的每个实现类都需要在核心配置文件中配置
    • 对于有多个实现类的接口该方法不适用
  • T getBean(String name,Class requiredType);

    • 根据Bean的id和类型获得Bean实例,解决容器中相同类型Bean有多个情况
2.4.6 Bean 标签配置
2.4.6.1 Bean 标签的基本配置

基本属性:

  • id:Bean实例在Spring容器中的唯一标识
  • class:Bean的全限定名

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

2.4.6.2 Bean 标签的范围配置

该属性指对象的的作用范围,取值如下

  • singleton

    • 不配置时的默认值,代表单例的
  • prototype

    • 代表多例的
  • request

    • WEB项目中,Spring创建一个Bean的对象,将对象存入到request域中
  • session

    • WEB项目中,Spring创建一个Bean的对象,将对象存入到session域中
  • global session

    • WEB项目中,应用在Portlet环境,如果没有Portlet环境那么globalSession 相当于 session

单例和多例的比较:

当scope的取值为singleton时

  • Bean的实例化个数:1个
  • Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例
  • Bean的生命周期:
    • 对象创建:当应用加载,创建容器时,对象就被创建了
    • 对象运行:只要容器在,对象一直活着
    • 对象销毁:当应用卸载,销毁容器时,对象就被销毁了

当scope的取值为prototype时

  • Bean的实例化个数:多个
  • Bean的实例化时机:当调用getBean()方法时实例化Bean
  • Bean的生命周期:
    • 对象创建:当使用对象时,创建新的对象实例
    • 对象运行:只要对象在使用中,就一直活着
    • 对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收了
2.4.6.3 Bean 生命周期的配置
  • init-method:指定类中的初始化方法名称
  • destroy-method:指定类中销毁方法名称
2.4.6.4 Bean 实例化的三种方式

无参构造方法实例化:

工厂静态方法实例化:

工厂普通方法实例化:

2.4.7 Bean 依赖注入

依赖注入:DI,Dependency Injection,是 Spring 框架核心 IOC 的具体实现

将不同层之间调用的依赖关系交给Spring维护,及通过框架将层对象传入别的层

2.4.7.1 Bean 依赖注入方式 - 构造方法

(1)在Service 层里添加另一层接口的实例

  • private xxxDao xxxdao;

(2)为Service 层添加有参构造方法,将属性xxxdao传入

(3)在实现接口所重写的方法中适用另一层接口的实例(xxxdao)调用方法

(4)配置核心配置文件

<bean id = "xxxService" class = "service 层里的实现类">
	<constructor-arg index="参数下标从0开始" type="构造方法参数接口类型" ref="构造方法参数bean id"/>
	或
	<constructor-arg name="构造方法参数的形参名" ref="构造方法参数bean id"/>
<bean/>

2.4.7.2 依赖注入方式 - set方法

(1)在Service 层里添加另一层接口的实例

  • private xxxDao xxxdao;

(2)该方法使用默认的无参构造方法

(3)为该属性xxxdao 添加set 方法

(4)在实现接口所重写的方法中使用另一层接口的实例(xxxdao)调用方法

(5)配置核心配置文件

<bean id = "xxxService" class = "service层接口的实现类">
<property name = "set方法set后首字母小写" ref="属性对应接口Bean id"/>
<bean/>

set 方法 - 拓展 - P命名空间注入

在核心配置文件中文件头添加

xmlns:p="http://www.springframework.org/schema/p"

配置Bean

<bean id = "xxxService" class = "service层接口的实现类" p:xxxDao-ref="xxxDao/>
2.4.7.3 Bean 依赖注入的的数据类型
  • 普通数据类:基本数据类型及String
  • 引用数据类型
  • 集合数据类型:List,Map,Set,Array,Properties

List:

<property name="">
<list>
<value>基本数据类型的值<value/>
<ref bean = "引用数据类 Bean id"/>
<list/>
<property/>

Set:和List 配置基本一致,区别在于List集合和Set集合本身的区别

<property name="">
<set>
<value>基本数据类型的值<value/>
<ref bean = "引用数据类 Bean id"/>
<set/>
<property/>

Array:和前面基本一致,只是标签有区别

<property name="">
<array>
<value>基本数据类型的值<value/>
<ref bean = "引用数据类 Bean id"/>
<array/>
<property/>

Map:除了标签的区别,需要同时设置key 和 value 的值

<property name="">
<map>
<entry key="基本数据类型" value="基本数据类型">
<entry key-ref="引用数据类型Bean id" value-ref="引用数据类型Bean id">
<map/>
<property/>

Properties配置文件注入:key - value形式,值都是字符串

<property name="">
<props>
<prop key="">值</prop>
<props/>
<property/>
2.4.8 配置文件模块化

意义:Sping 配置内容多且都配置在核心配置文件造成繁杂,可将部分配置文件拆解到其他配置文件中,即配置文件模块化

拆分原则:

  • 按层进行拆分
  • 按业务模块进行拆分

拆分配置文件之后需要加载拆分后的配置文件,加载方式有两种:

(1)并列的多个配置文件

ApplicationContext act =
new
ClassPathXmlApplicationContext("ApplicationContext.xml","xxx.xml","...");

(2)主从配置文件

  • 在核心配置文件标签中添加
<import resource="applicationContext-xxx.xml"/>

注意:

  • 同一个xml中不能出现相同名称的bean,如果出现会报错
  • 多个xml如果出现相同名称的bean,不会报错,但是后加载的会覆盖前加载的bean
2.4.9 Bean 标签配置,依赖注入和配置文件模块化小结
<bean>标签:创建对象并放到spring的IOC容器
id属性:在容器中Bean实例的唯一标识,不允许重复
class属性:要实例化的Bean的全限定名
scope属性:Bean的作用范围,常用是Singleton(默认)和prototype

<constructor-arg>标签:属性注入
name属性:属性名称
value属性:注入的普通属性值
ref属性:注入的对象引用值

<property>标签:属性注入
name属性:属性名称
value属性:注入的普通属性值
ref属性:注入的对象引用值
<list>
<set>
<array>
<map>
<props>

<import>标签:导入其他的Spring的分文件

2.5 DbUtils (IOC 实战)

交给Spring 容器的类:

  • 各层接口实现类

  • DataSource

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"></property> //特别注意该地方的set方法
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
  • 使用properties 文件的方式进行数据库初始化配置
在核心配置文件文件头中添加:
命名空间
xmlns:context="http://www.springframework.org/schema/context"

约束路径
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd

标签内配置:
<context:property-placeholder location="classpath:jdbc.properties" />
  • QueryQunner 有参构造初始化(传递dataSource)
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>

2.6 Spring 注解开发

使用注解开发需要在核心配置文件中先添加context,再为需要注解扫描的包进行配置

<!--注解的组件扫描-->
<context:component-scan base-package="com.lagou"></context:component-scan>
2.6.1 Spring 注解开发常用注解
2.6.1.1 自定义类相关注解
对应标签的注解 value = "" 相当于 注解中 id = ""
@Component 使用在类上用于实例化Bean
@Controller 使用在Web层类上用于实例化Bean
@Service 使用在Service层类上用于实例化Bean
@Repository 使用在dao层类上用于实例化Bean

注解开发依赖注入使用的是反射机制,和构造方法和set方法传参不同

对于有多个实现类的接口,会先根据接口匹配,再根据参数名匹配实现类或设置@Qualifier 指定

JDK11以后完全移除了javax扩展导致不能使用@resource注解,使用需要导包

<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
对应标签注解 前三种对应ref引用数据类型
@Autowired 使用在字段上用于根据类型依赖注入/该方法可用于参数前
@Qualifier 必须结合@Autowired使用,根据名称依赖注入
@Resource 相当于@Autowired+@Qualifier,name="" 按名称
@Value 注入普通数据类型

配置Bean的作用范围和生命周期:

对应 标签
@Scope 标注Bean的作用范围
@PostConstruct 使用在方法上标注该方法时Bean的初始化方法
@PreDestroy 使用在方法上标注该方法是Bean的销毁方法
2.6.1.2 非自定义类和核心配置文件相关注解

(1)非自定义类,jar包中的源码类需要Spring 管理时

@Bean

  • 使用在方法上,标注将该方法的返回值存储到 Spring 容器中
  • value = "" 相当于 不写默认等于方法名

(2)加载properties文件的配置

加载properties文件的配置:<context:property-placeholder location="classpath:jdbc.properties">

@PropertySource

  • 用于加载 properties 文件中的配置
  • 传入: classpath: 文件位置

(3)组件扫描的配置

组件扫描的配置:<context:component-scan>

@ComponentScan

  • 用于指定 Spring 在初始化容器时要扫描的包

(4)引入其他文件

引入其他文件:<import>

@Import

  • 用于导入其他配置类
  • 传入Class 类

(5)使用核心配置类来替换核心配置文件

@Configuration

  • 用于指定当前类是一个Spring 配置类,当创建容器时会从该类上加载注解

在使用春注解开发时

new AnnotationConfigApplicationContext(核心配置文件.class);
2.6.2 Spring 整合Junit

整合Junit的含义是在测试类中无需手动加载核心配置文件并手动获取目标类,将其交给Spring 来创建

(1)导入Spring 集成 Juint 的坐标

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>

注意:spring5 及以上版本要求 junit 的版本必须是 4.12 及以上

(2)测试类上添加@Runwith注解

@RunWith(SpringJUnit4ClassRunner.class)

(3)加载核心配置文件或核心配置类

@ContextConfiguration(classes = {SpringConfig.class}) // 加载spring核心配置类

或

@ContextConfiguration(value = {"classpath:applicationContext.xml"}) //加载spring
核心配置文件

(4)使用@Autowired 注入对象

(5)测试方法中直接调用被注入对象

测试时出现异常:

javax.net.ssl.SSLException: closing inbound before receiving peer's close_notify

解决方法:

在url 后加上 &useSSL=false

上一篇:sping随笔


下一篇:Java Sping 第四章——Sping AOP