Spring入门介绍及Spring IOC
1. 概述
引言:
众说周知,spring是一门框架,那么什么是框架?我们以生活中的汽车制造为例子,以前,要造一辆汽车,我们得造轮子、车体、挡风屏等,然后再进行拼装和加工,而有了框架之后,这些繁杂的步骤都有第三方为我们制造,我们只需要关注拼装怎么做,做成什么样的汽车。spring框架同理,以前在java开发中我们需要事必躬亲,创包、类,新建对象,再使用,现在有了框架,我们只管使用对象就好了。
补充:
说明: 将公共的模块(功能) 进行高级的抽取(接口/父级)形成了通用的代码体.
使用: 引入特定的jar包/class/方法 既可以使用框架中的功能.
实际意义: 简化代码的开发.提高软件的扩展性.
2. Spring框架:
官方说明:
Spring框架为任何类型的部署平台上的基于Java的现代企业应用程序提供了全面的编程和配置模型。
Spring的一个关键元素是在应用程序级别的基础架构支持:Spring专注于企业应用程序的“管道”,以便团队可以专注于应用程序级别的业务逻辑,而不必与特定的部署环境建立不必要的联系。
总结:
Spring的主要的作用将其他框架进行整合,以一种统一的通用的方法进行管理(“角色:框架的大管家”)
Spring框架是一个开放源代码的J2EE应用程序框架,由Rod Johnson发起,是针对bean的生命周期进行管理的轻量级容器(lightweight container)。 Spring解决了开发者在J2EE开发中遇到的许多常见的问题,提供了功能强大IOC、AOP及Web MVC等功能。Spring可以单独应用于构筑应用程序,也可以和Struts、Webwork、Tapestry等众多Web框架组合使用,并且可以与 Swing等桌面应用程序AP组合。因此, Spring不仅仅能应用于JEE应用程序之中,也可以应用于桌面应用程序以及小应用程序之中。Spring框架主要由七部分组成,分别是 Spring Core、 Spring AOP、 Spring ORM、 Spring DAO、Spring Context、 Spring Web和 Spring Web MVC。 [1]
知识讲解: Bean: 被spring容器管理的对象称之为Bean
2.1 SpringMVC:
说明:
以前我们在Java编程的过程中,需要写大量的代码,那个时候我们采用的方式是将代码放在不同的包,不同的class文件,而在做大型项目开发中,Spring为了解决这一问题,引入SpringMVC思想
总结:
核心:解耦!!!
实质就是分层思想,利于维护
实现步骤:分为3层(三层结构)
Model层(持久层):代码与数据库进行交互的代码(Mybatis-dao层)
View(视图层):一般指用户看到的内容(页面)
Controller(控制层): 完成某项业务的具体操作过程(Controller层----Service层)
具体实现:
说明:
Controller:负责接收页面传递过来的参数
Service:负责程序执行的具体过程
Dao:负责与数据库的交互
优势:
- 代码出了问题找对应的模块
- 便于管理
能够进行松耦合
面试题:说说什么是SpringMVC?
片面回答:SpringMVC就是Controller、Service、Dao(太片面了,MVC是一种思想,Controller、Service、Dao是思想的具体实现)
回答:SpringMVC是一种思想,能够实现程序的分层设计,便于项目维护,进行解耦,MVC的具体实现是创建Controller、Service、Dao
2.2 SpringMVC模拟测试:
类:com.jt.dao.UserDao
public class UserDao {
//1.创建方法
public void addUser(){
System.out.println("新增一个用户:"+"XXX");
}
}
有了Dao层分层后需要以一种统一的方式进行调用,要做到统一的规范,就要抽取共性==>使用接口!
类:com.jt.service.UserService
public interface UserService {
//2.定义行为规范
void addUser();
}
创建实现类,执行程序
类:com.jt.service.UserServiceImpl
public class UserServiceImpl implements UserService{
//8.向上提取,将来每个功能可以直接使用对象
//问题:耦合性太高!!!!!
/*
优化:9.将UserDao换成接口,创建实现类 这样每次创建对象,只需要
根据需求更换实现类,也叫面向接口开发!!
只是初步优化,本质没有改变,还是要new,但是还是要优化!!!
*/
//如何优化new????=>SpringIOC!!
private UserDao dao = new UserDao();
//3.重写抽象方法
@Override
public void addUser() {
//4要使用dao的功能添加数据,先创建对象
//UserDao dao = new UserDao();
//5.调用方法
dao.addUser();
}
//6.假设还有一个功能updateUser
/*@Override
public void updateUser() {
//7.创建对象
UserDao dao = new UserDao();
dao.updateUser();
}*/
}
2.3 Spring IOC(核心机制)
2.3.1 问题说明:
传统的代码是直接通过new的方式创建对象. 这样的方式将对象与对象紧紧的绑定到一起,不便于代码的扩展.所以需要进行松耦合处理.
2.3.2 IOC说明:
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
说明: 将对象的创建的权利交给Spring管理,由Spring(第三方)管理对象的生命周期(创建/初始化/使用/销毁).
原理说明:
说明:
将对象创建的权利交给Spring容器管理,对象由容器创建:UserDao = new UserDao,这就是IOC的过程,接着Service层的对象就和Spring容器对象进行绑定,这就是DI:依赖注入,相当于service层和容器耦合了
好处:
原来只有2者(Dao、Service)没办法松耦合,现在引入第三方容器,无论以后我们创的是UserDao、User2Dao、3Dao,我们都不需要担心,我们只管引用,因为容器会帮我们创建。
类似于租房的中介,我们只需要跟中介说要什么样的房子,中介会帮我们找,无论将来我的需求发生何种变化,中介都能满足我们,反正就只管住就对了!
3.SpringIOC入门测试:
3.1 环境搭建
3.1.1 创建Maven工程
3.1.2 添加依赖
<dependencies>
<!--Spring核心包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.6</version>
</dependency>
<!--引入SpringBean-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.6</version>
</dependency>
<!--引入context包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.6</version>
</dependency>
<!--引入表达式jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.3.6</version>
</dependency>
<!--引入日志依赖-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!--引入测试包-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
spring版本说明:带GA字样的是稳定版(推荐!)
3.2 具体过程
类:com.jt.pojo.User
package com.jt.pojo;
public class User {
//1.新建方法
public void say(){
System.out.println("您好Spring框架");
}
}
application.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">
<!--动态生成Spring配置文件 注意头标签-->
<!--2.Spring容器管理对象
id: Spring容器中对象的唯一标识 不能重复 一般类名首字母小写
class: 类的全路径 包名.类名 需要Spring容器管理的类
-->,
<bean id="user" class="com.jt.pojo.User"></bean>
</beans>
测试类:com.jt.TestUser
package com.jt;
import com.jt.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestUser {
@Test
public void testSpring(){
/*3.
通过加载配置文件,创建Spring容器对象 容器创建对象创建
新建ClassPathXmlApplicationContext对象,
参数:写application.xml的文件名,
返回值:返回一个ApplicationContext对象
*/
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
//4.从容器中获取对象 通过bean的Id获取对象
//调用id的方式获取对象
/*User user = (User) applicationContext.getBean("user");
5.通过类获取对象
User user2 = (User)applicationContext.getBean(User.class);*/
//6.通过id + 类 获取对象
User user3 = (User)applicationContext.getBean("user", User.class);
//7.调用方法
user3.say();//您好Spring框架
}
}
4.Spring IOC原理:
4.1 Spring容器如何管理对象:
1).当Spring程序执行时,首先会根据配置文件的内容进行解析
2).当程序解析到bean标签时,则会根据反射的机制实例化对象
@Test
public void demo2() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
//1.通过类的路径实例化类型...
Class userClass = Class.forName("com.jt.pojo.User");
//2.实例化对象
User user = (User) userClass.newInstance();
user.say();
}
3).将实例化好的对象保存到超大的Map集合中<K,V> bean中的Id当做map的Key,实例化好的对象当做Value
Map<id,对象>
4). 从容器中获取对象. 则从Map集合中通过id获取对象即可.
User user = (User) context.getBean("user");
4.2 关于反射机制的补充
反射机制 必然调用对象的无参构造方法,所以特别注意!!!
5.Spring创建bean对象的常用方式
5.1 通过构造器
<bean id="" class=""/>
5.2 工厂模式
5.2.1 静态工厂模式:
问题说明:
实际开发过程中,我们会遇到一些特殊情况,例如,想使用抽象类和接口对象,但是抽象类和接口是不能实例化的,因此,构造器方式无法为我们提供bean对象,需要使用静态工厂模式,接下来以Calendar(日历)类的使用作为例子:
工程目录结构如下:
类:StaticFactory
package cn.jt.factory;
import java.util.Calendar;
public class StaticFactory {
/*通过静态工厂获取数据*/
public static Calendar getCalendar(){
return Calendar.getInstance();
}
}
application.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">
<!--静态工厂实例化对象的写法 方法必须是static-->
<bean id="calendar1" class="com.jt.factory.StaticFactory" factory-method="getCalendar"/>
</beans>
总结:
通过静态工厂方法
方式: <bean id="" class=“工厂类” factory-method=“静态工厂方法”/>
注: 工厂类实例没有创建
实质就是通过class获取到的类 通过点调用factory-method的方法,因此被调用的方法必须是静态的
测试类:TestSpring
@Test
public void testStatic(){
ApplicationContext context =
new ClassPathXmlApplicationContext("application.xml");
Calendar calendar1 = (Calendar) context.getBean("calendar1");
System.out.println("获取当前时间:"+calendar1.getTime());
}
5.2.2 实例化工厂模式
问题说明:
在静态工厂模式开发过程中,有时候为了提高程序的安全性,不允许外界通过类名访问,会要求去除static关键字,这个时候,我们就不能够使用静态工厂模式了,因此引入实例化工厂模式。
接下来还是以Calendar(日历)类的使用作为例子:
编辑实例化工厂:
public class InstanceFactory {
//获取Calendar对象(不带static)
public Calendar getCalendar(){
return Calendar.getInstance();
}
}
application.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">
<!--实例化工厂
步骤1:将工厂交给spring容器管理
步骤2: 通过对象调用方法 -->
<bean id="instanceFactory" class="com.jt.factory.InstanceFactory"></bean>
<bean id="calendar2" factory-bean="instanceFactory" factory-method="getCalendar"></bean>
</beans>
总结:
通过实例工厂方法(非静态方法)
方式:
<bean id=“factory” class=“工厂类”/>
<bean id="" factory-bean=“factory” factory-method=“实例工厂方法”/>
注: 工厂类实例被创建
5.3 Spring工厂模式
说明: 如何需要创建复杂对象 首选工厂模式
类:cn.jt.factory.SpringFactory
package cn.jt.factory;
import org.springframework.beans.factory.FactoryBean;
import java.util.Calendar;
public class SpringFactory implements FactoryBean<Calendar> {
//工厂模式实例化对象的方法
@Override
public Calendar getObject() throws Exception {
return Calendar.getInstance();
}
//获取类型
@Override
public Class<?> getObjectType() {
return Calendar.class;
}
//默认条件下spring容器中都是单例对象 节省空间 单例对象 多例对象
//多线程问题 多例对象 ????? 安全性问题!
//多个资源对共享数据进行操作!!!!
//对象.方法() 没有安全性问题 提倡使用单例
@Override
public boolean isSingleton() {
return true;
}
}
application.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">
<!--spring工厂模式 -->
<bean id="calendar3" class="cn.jt.factory.SpringFactory"></bean>
</beans>
测试类:
@Test
public void testSpringFactory(){
ApplicationContext context =
new ClassPathXmlApplicationContext("application.xml");
Calendar calendar1 = (Calendar) context.getBean("calendar3");
System.out.println("获取当前时间:"+calendar1.getTime());
}
应用场景:
FactoryBean 通常是用来创建比较复杂的bean,一般的bean 直接用xml配置即可,但如果一个bean的创建过程中涉及到很多其他的bean 和复杂的逻辑,用xml配置比较困难,这时可以考虑用FactoryBean。
实现方法:
自己写个MyFactoryBean 实现FactoryBean接口
总结:
相对于静态工厂、实例化工厂模式,Spring工厂模式不用考虑是否为静态,而是实现FactoryBean,我们只需要实现两个方法即可。
6.Spring的执行原理(回调执行原理)
说明:
- 首先会根据程序加载配置文件application.xml
- 当扫描到文件中的bean标签时,会根据bean的配置,实例化对象
- 假设此时正在实例一个spring工厂模式的bean对象,此时会去实现一些特殊的定制功能(指的是实现了一些接口)
- 例如实现了FactoryBean,此时会去执行接口中实现的方法(称为钩子函数或回调),执行这些方法获取结果后再继续向下执行。。。
7. 关于Spring单例多例的那点事
7.1 问题说明:
前面我们在使用Spring工厂模式创建bean对象时,实现了一个FactoryBean的接口,这个接口里有一个方法:isSingleton,是否为单例,底层是true,默认为单例,那么,在实际的开发需求中,如果我想要给一个bean对象设置单例多例,需要怎么操作呢?利用bean对象的scope属性
补充:
单例和多例的区别:
单例的话同一对象只创建一次,多例的话同一对象反复创建,单例可以减少一些空间资源的浪费,节省内存,但是,如果有一些特殊的需求,例如数据源连接这些需求,为了提升用户使用效率,会使用多例模式
7.2 scope属性的使用
<!--测试单例多例 通过scope属性控制对象的单例和多例
scope="prototype" 多例设置
scope="singleton" 缺省值 单例
-->
<bean id="user" class="com.jt.pojo.User" scope="prototype"></bean>
编辑实例类
class User{
public User(){
System.out.println("创建对象");
}
public void say(){
System.out.println("我是用户");
}
}
编辑测试方法
总结:
单例模式在容器创建时对象只会创建一次,而多例模式在容器创建时会反复创建对象
8. 懒加载
8.1 问题说明:
单例多例解决了创建几个对象的问题,但是对象创建对象的时机是我们也必须关注的一个问题,在java部分的学习中,我们曾经学习过单例设计模式,其中有两种:饿汉式和懒汉式(懒加载)
知识回顾:
- 饿汉式:不管你用不用这个类的对象,都会直接先创建一个
- 懒汉式:先不给创建这个类的对象,等你需要的时候再创建–延迟加载的思想
- 延迟加载的思想:是指不会在第一时间就把对象创建好占用内存,而是什么时候用到,什么时候再去创建对象
所以说懒加载,在我不需要使用对象的时候,可以节省空间,但是在面对一些复杂的项目时,如果等到需要使用时再创建对象,有可能面临效率低的问题,因此面对不同的场景使用不同的加载机制才是上策。
8.2 懒加载的使用
懒加载: 当用户需要获取对象时,容器才创建对象,称之为懒加载
说明: Spring容器中默认的规则是:容器创建则对象创建.
如果需要配置懒加载 则需要添加额外的属性
8.2.1 lazy-init的使用
<!--测试懒加载 通过lazy-init属性控制是否开启懒加载
lazy-init="true" 开启懒加载
lazy-init="false"/lazy-init="default" 懒加载不生效
原则: 只要是多例对象 都是懒加载. 懒加载只对单例对象有效
关于懒加载说明: 一般服务器对象应该先行创建,用户直接使用即可.
多例对象: 用户使用时创建,同时将对象的生命周期交给使用者管理,
Spring不负责维护对象的生命周期
(随用随销)
-->
<bean id="user" class="com.jt.pojo.User" scope="prototype" lazy-init="true"></bean>
总结:
多例对象 都是懒加载. 懒加载只对单例对象有效
Spring创建对象默认是单例,非懒加载
9. Spring生命周期
9.1 问题引入:
人总有生老病死的时候,对象也同理,我们只有了解了一个对象的前世今生,我们才能更好地把握、了解对象。
9.2 生命周期的过程:
1.实例化对象
2.初始化操作 (一般对对象的属性赋值)
3.用户使用对象(调用其中的方法)
4.对象销毁 (一般都是释放资源)
9.3 测试
编辑实体类
package cn.jt.pojo;
public class User {
private String conn;
public User(){
System.out.println("创建对象");
}
//此方法需要自动触发
public void init(){
this.conn = "数据库连接";
System.out.println("初始化完成");
}
public void say(){
System.out.println("我是用户");
}
//此方法需要自动触发
public void destory(){
conn = null;
System.out.println("资源释放:"+conn+"~~~~~~~~~~");
}
}
编辑xml配置文件
<!--
测试对象的生命周期
init-method="init" 初始化方法
destroy-method="destroy" 销毁方法
-->
<bean id="user" class="com.jt.pojo.User"
init-method="init" destroy-method="destroy"/>
编辑测试案例
//测试生命周期运行
@Test
public void testlife(){
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("application.xml");
//获取对象
User user = context.getBean(User.class);
//用户调用方法
user.say();
//只要容器关闭,则对象销毁
context.close();
}
10. 关于IOC小结
核心: 对象的创建
- 通过bean标签进行创建 属性 id/class
- 工厂模式
- 单例多例/懒加载
- 生命周期
个人理解:IOC叫控制反转,分为两个含义:控制 and 反转
- 控制:控制对象(就是需要被Spring容器管理的对象)
- 反转: 反转对象(将被容器管理的对象给予调用者)
言外之意就是调用者不用自己去创建对象,只需要获取就好了!
核心点是:解耦