spring框架的概述以及spring中基于XML的IOC配置
1. 什么是spring?
来自百度:
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。
轻量——从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类。
控制反转——Spring通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。
面向切面——Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。
2.Spring的优势
1.方便解耦,简化开发
2.AOP编程支持
3.声明式事务的支持
4.方便程序的测试
5.方便集成各种优秀框架
6.降低JavaEE API的使用难度
这里一说可能大家都看不懂优点都是什么意思,不过没关系,这些优势我们会在后来的博客中一点一点总结深入,今天我们主要详细了解的就是IOC,所以AOP我们暂时不谈,先讲一讲为什么需要引入IOC。
上图讲的就是Spring框架的结构体系,今天我们详细讲解的就是 Core Container部分,就是Spring的核心容器,IOC相关的内容,Spring任何的其他部分要运行,必须得有核心容器的支持。
我们先用一个案例来入门,了解一下Spring框架的其中一个核心内容IOC的作用
正常情况下我们连接数据库进行查询操作的代码如下:
package com.jack.jdbc;
import java.sql.*;
/**
* @Author: Jack
* @date: 2019/10/21 19:25
*/
public class JdbcDemo01 {
public static void main(String[] args) throws SQLException {
//1.注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//2.获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql:///day23","root","root");
//3.创建执行sql语句的statement对象
PreparedStatement preparedStatement = connection.prepareStatement("select * from account");
//4.执行sql语句,获得结果集
ResultSet resultSet = preparedStatement.executeQuery();
//5.遍历结果集
while (resultSet.next()){
System.out.println(" id:"+resultSet.getString(1));
System.out.println(" uid:"+resultSet.getString(2));
System.out.println("Money:"+resultSet.getString(3));
}
//6.释放资源
resultSet.close();
preparedStatement.close();
connection.close();
}
}
代码写完了我们可以从代码中看出
没有注册驱动,就不能获得连接,没有获得连接就不能创建PreparedStatement ,不能创建PreparedStatement 就不能执行sql语句。。。一环套一环,缺一不可。
所以一个功能的实现都是各种类和方法之间的依赖,这就叫做耦合性,分为类之间依赖和方法间依赖。
解耦:降低程序间的依赖关系
在实际开发中,真正的做到没有依赖完全独立不太可能,所以实际开放中的解耦就是要做到
编译期间不依赖,运行时才依赖
解耦的思路:
第一步:使用反射来创建对象,而不是使用new关键字。
//1.注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
Class.forName("com.mysql.jdbc.Driver");
这样做的好处就是,我们注册驱动的时候不再是需要一个具体的类,而是只要有一段正确的描述类名的字符串就行了。
但是也存在他的问题,我们注册驱动的时候,使用反射就已经把要注册的驱动类型写死,以后在使用其他数据库的时候,会相当的麻烦。
解决方案:使用读取配置文件来获取 要创建的对象的全限定类名
在我们平时做服务端开发的时候,我们一般都使用的是三层架构,也就是界面层,业务逻辑层,数据访问层三层,一般处理的业务逻辑就是界面层调用业务逻辑层,业务逻辑层调用数据访问层这样的逻辑,具有很强的耦合性,三层之间缺一不可,我们使用spring框架的核心内容IOC就是解决了这三层之间的耦合性的问题。
inversion of control,把对象创建的权力叫给框架,是框架的重要特征,大幅度降低了耦合。注意不是完全的消除耦合。今天我们主要介绍的就是spring的ioc。
入门案例:
1.导入jar包:
因为我使用的是maven项目,这里我就把坐标贴一下
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
2.创建一个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
https://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
3.把对象的创建交给spring来管理
<bean id="accountService" class="com.jack.service.imp.AccountServiceImpl"/>
<bean id="accountDao" class="com.jack.dao.impl.AccountDaoImpl"/>
4.代码测试一下
//获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//根据id获取bean对象
AccountService accountService = (AccountService) ac.getBean("accountService");
AccountDao accountDao = ac.getBean("accountDao",AccountDao.class);
System.out.println(accountService);
System.out.println(accountDao);
根据运行结果我们可以发现,在操作过程中,我们并没有使用new关键字实例化任何一个对象,但是在打印结果中我们可以看到,两个对象都被实例化了,这就是springIOC的基本操作。
配置文件 -> 解析配置文件获得全类名 -> 反射创建对象。
符合我们通过解析配置文件,获取要创建对象的全限定类名,通过反射创建对象的降低耦合度的思路
所以IOC的就是一个容器,我们把需要用到的类全部都放在ioc中,在spring中登记,在适当的时候,spring会把你需要的类给你,并且同时也在适当的时候,把你交给其他需要你的类,一个类的生命周期不再决定于引用他的对象,而是全部交spring框架管理,对于对象来讲,以前是对象控制其他的对象,但是现在,是spring控制所有的对象,这就叫做控制反转。
核心容器的两个接口:
1.ApplicationContext(单例对象适用)
构建核心容器时,创建对象采取的策略是立即加载的方式,也就是说,一读取完配置文件,就立刻创建配置文件中注册的对象。
2.BeanFactory(多例对象适用)
在构建核心容器的时,创建对象采取的是延迟加载的策略,什么时候根据id获取对象的时候,才真正的创建对象。
但是我们思考一个问题,ApplicationContext在我们配置文件读取的时候就把对象创建了,但是如果我们要第二次使用某个对象,肯定要再次的调用创建对象的方法,这样看来,我们还不如不着急的创建对象,干脆等到什么时候用,什么时候创建对象。所以根据应用场景来看,ApplicationContext和BeanFactory都有使用的必要,不过在实际情况中,spring框架具有强大的功能,非常智能,可以根据我们配置的不同,去改变创建对象的策略,所以两个接口创建对象的策略显得不是那么的重要,但是因为BeanFactory是一个顶级的接口,功能不够完善,所以在日常的开发中,多数情况下还是选择使用ApplicationContext接口来定义容器对象。
创建Bean对象的方式(三种):
1.使用目标类中的默认构造函数来创建对象:
<bean id="accountService" class="com.jack.service.imp.AccountServiceImpl"/>
采用的是目标类中的构造方法来创建对象,如果目标类中没有默认的构造方法(空参构造方法),则无法创建对象。
2.使用某个类中的方法来创建目标类对象
某个类(工厂类)
public class ServiceFactory {
public AccountService getAccountService(){
return new AccountServiceImpl();
}
}
spring配置文件:
<bean id="factory" class="com.jack.Utils.ServiceFactory"/>
<bean id="accountService" factory-bean="factory" factory-method="getAccountService"/>
首先在配置文件中注册工厂类,也就是说实例化了工厂类,然后再注册要实例化的目标类(AccountService ),factory-bean指定哪个类中的方法可以创建目标对象,factory-method指定工厂类中的哪个方法来创建对象。
3.使用工厂类中的静态方法来创建目标对象
工厂类和静态方法
public class StaticFactory {
public static AccountService getAccountService(){
return new AccountServiceImpl();
}
}
spring配置文件
<bean id="accountService" class="com.jack.Utils.StaticFactory" factory-method="getAccountService"/>
bean的作用范围
这个就非常简单了,只要设置一下scope标签就好了,我们来挨个说。
1.singleton:顾名思义,就是设置spring创建对象为单例的。
spring配置文件:
<bean id="accountService" class="com.jack.service.imp.AccountServiceImpl" scope="singleton"/>
测试类:
//获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
AccountService accountService1 = ac.getBean("accountService", AccountService.class);
AccountService accountService2 = ac.getBean("accountService", AccountService.class);
System.out.println(accountService1 == accountService2);
执行结果如下:
可以看到AccountService的构造函数仅执行了一次,并且两个AccountService对象的比较打印结果为true
2.prototype:多例的
执行结果如下:
AccountService的构造函数执行了两次,也就是说,AccountService创建了两次。
3.request
作用域为 web应用的请求范围
4.session
作用域为 web应用的会话范围
5.global-session
作用域为集群web应用的会话范围
bean对象的生命周期
分为单例对象和多例对象
单例对象的生命周期为:
出生:容器创建时,对象创建
生存:只要容器存在,对象一直存在
死亡:容器销毁时,对象死亡
单例对象和容器的生命周期相同,同生共死。
多例对象的生命周期:
出生:使用对象时,spring才为我们创建对象
生存:对象只要是在使用过程中就一直生存。
死亡:当对象长时间不用,并且没有别的对象引用的时候,java的垃圾回收机制把对象消亡。