【Spring框架】Spring核心思想IoC以及依赖注入DI详解
目录
- Spring框架
- 前言 服务端三层开发
- 表现层
- 业务层
- 持久层
- Spring框架的概述
- Spring框架的优点
- Spring核心——IoC
- 什么是IoC?O.o
- 什么是耦合度?
- 创建第一个IoC程序
- 导入必要依赖
- 编写接口和实现类
- 编写Spring核心配置文件
- 测试类进行测试
- Spring配置文件
- Bean对象的创建和销毁的两个属性配置
- 实例化Bean对象的三种方式
- DI依赖注入
- 属性的set方法注入值
- 属性构造方法方式注入值
- 数组,集合(List,Set,Map),Properties等的注入
- 多配置文件的加载方式
- 主配置文件中直接添加
- 工厂创建的时候直接加载多个配置文件
Spring框架
前言 服务端三层开发
表现层
表现层负责处理用户界面和用户交互。它接收用户的请求,进行初步处理后转发给业务层,并将业务层返回的数据呈现给用户。常见的表现层技术包括 Web 框架(如 Spring MVC、Struts)、前端框架(如 React、Vue.js)等。
- Servlet
- SpringMVC
- Struts
业务层
业务层是系统的逻辑核心,负责处理业务规则和业务流程。它接收来自表现层的请求,调用持久层进行数据操作,并返回处理结果给表现层。业务层通常包含服务类和服务接口,实现复杂的业务逻辑和事务管理。常见的业务层技术包括 Spring、Hibernate 等。
- service
- Spring
持久层
持久层负责数据的存储和检索,主要与数据库进行交互。它提供了数据访问对象(DAO)或仓库(Repository)来封装数据访问逻辑,确保数据的一致性和完整性。持久层通常使用 ORM(对象关系映射)框架(如 Hibernate、MyBatis)来简化数据库操作。
- JDBC
- MyBatis
- JDBC Template
Spring框架的概述
Spring是一个开放源代码的设计层面框架,它解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。Spring是于2003年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。
Spring是于2003 年兴起的一个轻量级的Java开发框架,由Rod Johnson在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。
它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。
Spring的核心是控制反转(IoC控制反转)和面向切面(AOP)。简单来说,Spring是一个分层的JavaSE/EEfull-stack(一站式) 轻量级开源框架。
Spring框架的优点
-
方便解耦,简化开发,Spring就是一个大工厂,可以将所有对象创建和依赖关系维护,交给Spring管理。
-
AOP编程的支持,Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能。(可扩展性)
-
声明式事务的支持,只需要通过配置就可以完成对事务的管理,而无需手动编程。
-
方便程序的测试,Spring对Junit4支持,可以通过注解方便的测试Spring程序。
-
方便集成各种优秀框架,Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts2、Hibernate、MyBatis、Quartz等)的直接支持。
-
降低JavaEE API的使用难度,Spring 对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低。
Spring核心——IoC
什么是IoC?O.o
IOC——Inverse of Control,控制反转,将对象的创建权力反转给Spring框架!!
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。如下图(来自百度百科)
那么涉及到第二个问题:
什么是耦合度?
可以看看这篇文章:聊聊什么是耦合度-CSDN博客,简而言之就是各个对象或者类或者是模块之间的依赖程度,然而我们并不希望我们的程序是一个高耦合度的程序,因为这样各个模块之间紧密相连,会导致可扩展性、可维护性变差。
我们希望我们的程序是低耦合高内聚的程序,也就是模块内部的元素依赖关系紧密,而模块之间并不存在很紧密的依赖关系,高内聚度的模块会使得代码更加清晰、易于理解和维护,同时可以提高代码的复用性。
创建第一个IoC程序
这里还是说一下我用到的工具:
- IDE:IntelliJ IDEA 2024.2.3
- Maven:3.9.8
导入必要依赖
<dependencies><!-- Spring核心 --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.0.2.RELEASE</version></dependency><!-- 日志接口 --><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency><!-- 日志核心 --><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.12</version></dependency><!-- JUnit测试 --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency>
</dependencies>
编写接口和实现类
// 接口类
public interface UserService {public void hello();
}// 实现类
public class UserServiceImpl implements UserService {@Overridepublic void hello() {System.out.println("Hello IOC!!!");}
}
编写Spring核心配置文件
在src目录下创建applicationContext.xml的配置文件,名称是可以任意的,但是一般都会使用默认名称。我们通过这个配置文件来实现我们IoC的核心操作:
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!--IOC管理bean--><!--一个bean相当于一个对象bean的意义是将该类的创建对象的权利授予spring容器,让spring容器来创建对象,--><bean id="userService" class="com.qcby.service.Impl.UserServiceImpl" />
</beans>
前面我们提到,Spring设计核心就是IoC,这里配置文件中就是在由我们的IoC容器创建对象,我们将由IoC容器管理的Java对象称为Spring Bean,我们可以在编译器的对应的类文件中看到一个小咖啡豆的标志
测试类进行测试
下面我们写一个最简单的测试方法来测试我们写好的UserServiceImpl实现类:
@Testpublic void run1(){// 使用Spring的工厂ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");// 通过工厂获得类:UserService userService = (UserService) applicationContext.getBean("userService");userService.hello();}
输出结果:
这里简单穿插一条题外话:Spring的默认设计模式是单例模式——单例模式(Singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点。感兴趣的可以看看这篇文章:一文带你彻底搞懂设计模式之单例模式!!由浅入深,图文并茂,超超超详细的单例模式讲解!!-CSDN博客,介绍的很详细不再过多介绍。
Spring配置文件
-
id属性:Bean起个名字,在约束中采用ID的约束,唯一,取值要求:必须以字母开始,可以使用字母、数字、连字符、下划线、句话、冒号 id:不能出现特殊字符。
-
class属性:Bean对象的全路径。
-
scope属性:scope属性代表Bean的作用范围,其中的属性值如下:
取值范围 说明 singleton 默认值,单例模式 prototype 多例模式 request WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中 session WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中 global session WEB 项目中,应用在 Portlet 环境,如果没有 Portlet 环境那么globalSession 相当于 session
Bean对象的创建和销毁的两个属性配置
说明:Spring初始化bean或销毁bean时,有时需要作一些处理工作,因此spring可以在创建和拆卸bean的时候调用bean的两个生命周期方法
-
init-method标签:当bean被载入到容器的时候调用init-method属性指定的方法
-
destroy-method标签:当bean从容器中删除的时候调用destroy-method属性指定的方法
实例化Bean对象的三种方式
-
默认是无参数的构造方法(默认方式,基本上使用)
<bean id="userService" class="com.qcby.service.Impl.UserServiceImpl" />
-
静态工厂实例化方式
/*** 静态工厂方式*/ public class StaticFactory {// 静态工厂方式public static UserService createUs(){System.out.println("通过静态工厂的方式创建UserServiceImpl对象...");// 编写很多业务逻辑 权限校验return new UserServiceImpl();}}<bean id="us" class="com.qcby.demo1.StaticFactory" factory-method="createUs" />
-
实例工厂实例化方式,因为类中的方法非静态,所以需要先实例化类,再去创建对象,因此得名(说实话这个一开始还真没注意到)
/*** 动态工厂方式*/ public class Dfactory {public UserService createUs(){System.out.println("实例化工厂的方式...");return new UserServiceImpl();}}<bean id="dfactory" class="cn.tx.demo1.Dfactory" /> <bean id="us" factory-bean="dfactory" factory-method="createUs" />
DI依赖注入
依赖注入(Dependency Injection)
是在Spring框架负责创建Bean对象时,动态的将依赖对象注入到Bean组件中!!这么说可能有点难懂,可以理解为:你的类中有很多属性,比如name、age等,我们需要给这些属性赋值,但是我们又不想通过调用构造器或者是调用setter()
方法去赋值,这时我们就可以用DI,来给这些属性赋值,或者是创建依赖对象。
**依赖注入是实现控制反转的一种常见方式。**目的是处理依赖于对象的关系
属性的set方法注入值
编写实现类,其中包括要实现的对象或属性,最后通过配置文件调用setter()
方法进行注入:
// 实现类
public class OrderServiceImpl implements OrderService {// 编写成员属性,一定需要提供该属性的set方法private OrderDao orderDao;// 一定需要提供该属性的set方法,IOC容器底层就通过属性的set方法方式注入值public void setOrderDao(OrderDao orderDao) {this.orderDao = orderDao;}private String name;private Integer age;public void setName(String name) {this.name = name;}public void setAge(Integer age) {this.age = age;}@Overridepublic void saveOrder() {System.out.println("业务层,保存订单,姓名:" + name + "-年龄:" + age);orderDao.saveOrder();}
}public class OrderDaoImpl implements OrderDao {@Overridepublic void saveOrder() {System.out.println("持久层:保存订单...");}}
编写配置文件:
<!--DI:依赖注入-->
<bean id="orderService" class="com.qcby.service.Impl.OrderServiceImpl"><!-- 将实例化的对象注入到orderDao --><property name="orderDao" ref="orderDao" /><property name="name" value="Ray" /><property name="age" value="23" />
</bean>
<!-- 这里先将OrderDaoImpl实例化,再注入到orderService中 -->
<bean id="orderDao" class="com.qcby.DAO.Impl.OrderDaoImpl"/>
<bean id="orderDao" class="com.qcby.DAO.Impl.OrderDaoImpl"/>
这条语句其实就是创建对象,并在<property name="orderDao" ref="orderDao" />
这里将对象用orderDao
接口接收
编写测试方法:
@Test
public void run2(){// 使用Spring的工厂ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");// 通过工厂获得类:OrderService orderService = (OrderService) applicationContext.getBean("orderService");orderService.saveOrder();
}
属性构造方法方式注入值
编写类以及对应的带参的构造方法
public class Car {// 名称private String cname;// 金额private Double cmoney;public Car(String cname, Double cmoney) {this.cname = cname;this.cmoney = cmoney;}@Overridepublic String toString() {return "Car{" +"cname='" + cname + '\'' +", cmoney=" + cmoney +'}';}
}
编写配置文件,注入属性值:
<bean id="car" class="com.qcby.Demo.Car"><constructor-arg name="cname" value="Porsche"/><constructor-arg name="cmoney" value="40000000" />
</bean>
测试:
@Test
public void run3(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");Car car = (Car) applicationContext.getBean("car");System.out.println(car.toString());
}
数组,集合(List,Set,Map),Properties等的注入
编写实现类,实现对应属性:
public class CollectionBean {// 数组private String [] strs;public void setStrs(String[] strs) {this.strs = strs;}private List<String> list;public void setList(List<String> list) {this.list = list;}private Map<String,String> map;public void setMap(Map<String, String> map) {this.map = map;}private Properties properties;public void setProperties(Properties properties) {this.properties = properties;}@Overridepublic String toString() {return "CollectionBean{" +"strs=" + Arrays.toString(strs) +", list=" + list +", map=" + map +", properties=" + properties +'}';}
}
编写配置文件,注入值:
<!--给集合属性注入值-->
<bean id="collectionBean" class="com.qcby.Demo.CollectionBean"><property name="strs"><array><value>美美</value><value>小凤</value></array></property><property name="list"><list><value>熊大</value><value>熊二</value></list></property><property name="map"><map><entry key="aaa" value="老王"/><entry key="bbb" value="小王"/></map></property><property name="properties"><props><prop key="username">root</prop><prop key="password">123456</prop></props></property>
</bean>
编写测试方法:
@Test
public void run4(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");CollectionBean collectionBean = (CollectionBean) applicationContext.getBean("collectionBean");System.out.println(collectionBean.toString());
}
多配置文件的加载方式
一共两种方式:
主配置文件中直接添加
<import resource="applicationContext2.xml"/>
工厂创建的时候直接加载多个配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml","applicationContext2.xml");