Spring解析
一、Spring是什么?
关于这个问题,咱们不妨先看看百度中如何描述:
Spring框架是一个开放源代码的J2EE应用程序框架,由Rod Johnson发起,是针对bean的生命周期进行管理的轻量级容器(lightweight container)。 Spring解决了开发者在J2EE开发中遇到的许多常见的问题,提供了功能强大IOC、AOP及Web MVC等功能。Spring可以单独应用于构筑应用程序,也可以和Struts、Webwork、Tapestry等众多Web框架组合使用,并且可以与 Swing等桌面应用程序AP组合。因此, Spring不仅仅能应用于J2EE应用程序之中,也可以应用于桌面应用程序以及小应用程序之中。Spring框架主要由七部分组成,分别是 Spring Core、 Spring AOP、 Spring ORM、 Spring DAO、Spring Context、 Spring Web和 Spring Web MVC。
由上我们可以得知:
-
Spring是一个开源框架。并且是在2002年被一个名叫Rod Johnson的人创建的。创建的目的呢,就是是为了解决企业级开发的复杂性。
-
Spring是一个针对与Baen(组件)进行管理的一个轻量级容器。
-
Spring能够使用基本的JavaBaen来完成以前只能由EJB所完成的事情。
-
同时呢,Spring还提供了IOC、AOP及Web MVC等强大的功能
-
Spring不仅可以单独应用于构筑应用程序,还可以于其他的框架组合使用
-
Spring由七个部分组成,分别是 Spring Core、 Spring AOP、 Spring ORM、 Spring DAO、Spring Context、 Spring Web和 Spring Web MVC。
Spring框架七个组成部分的图解
在看过百度上的描述后,咱们再来对它进行一些补充
1.Spring的描述
Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器框架。
在解释这句话之前,咱们先看上面Spring的模块图解,Spring一共具有七个模块,其中最为重要的即是Spring Core,也就是Spring的核心容器,在Spring当中所有模块都是基于Spring的核心容器之上的,所以Spring被称之为容器框架。而IOC与AOP呢,分别是Spring框架中避免依赖的关键技术,也是Spring当中最大的特色,在下文会对这两个技术进行分析。
在Spring当中提供着一种贯穿始终的解决方案,贯穿了视图层,业务逻辑层,甚至到数据访问层,但Spring的核心目的却并不是想着去替代那些专用的框架,而是去整合它们。拿SSM(Spring+SpringMVC+Mybatis)打个比方,Spring在其中的角色就像是一个集成主板,用于将其他的专用框架集成,集成为一个整体。ssh,ssh2等等,都需要使用到Spring框架进行集成。
2.Spring的特点
- 对主流框架提供了集成支持,如(Mybatis,hibernate,JPA, Struts等)。
- 降低了Baen之间的耦合度,实现了软件各个层的解耦。
- 提供了事务管理,消息管理等服务。
- 将“针对接口编程”的成本降到了最低。
- Spring大小与开发都是轻量级的。
- 配件灵活且统一,便于单元测试。
二、Spring核心容器
1.运行时图解
以下是Spring运行时结构图解
2.核心容器解析
Spring核心容器中包含四个模块:Beans、Core、Context、SpEL
1.Core和Beans提供了整个框架最基础的部分,包括了IoC(控制反转)和DI(依赖注入)。
2.Conetext也就是上下文,它建立在Core和Beans模块提供的基础之上,它提供了框架式访问对象的方式
3.Core、Beans、Context三个模块构成了Spring的骨架
4.SpEL:提供了一种强大的运用于在运行时操作对象的表达式语音。
3.IOC控制反转
IOC(Inversion of Control)控制反转,就是将创建对象的权利,及对象生命周期的管理的过程交给Spring来处理。在之后的开发过程中就不再需要关注对象的创建和生命周期的管理,而是在需要的时候由Spring框架提供。
简单来说就是将原来需要开发人员完成的一个操作,反转过来,交给Spring框架来完成,这个过程呢,就叫做控制反转。
IOC还有一种描述:因为当初对控制反转的概念比较含糊,只能让人联想到通过容器控制对象这一说法,却很难想到到底由谁来维护这个对象的关系,于是便有人提出了DI(Dependency Injection)依赖注入,即由容器动态的将某种依赖关系注入到组件之中。
4.IOC使用示例
示例一
1.使用Maben创建一个Web工程,并将web版本改至3.0或3.1即可。
2.在pom文件中导入Spring框架所需要的依赖
以下是Spring所需的所有依赖
<!-- ********************** spring依赖 ********************** -->
<!-- 版本号 -->
<spring.version>5.0.2.RELEASE</spring.version>
<!--1)spring核心依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!--spring ioc依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<!--spring 扩展依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!--2)spring dao层依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<!--3)aop相关依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<!--4)spring web相关依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<!--5) spring test相关依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
3.创建包,用于存放相关的类,详情如下。
4.在resources目录下创建一个spring.xml的配置文件,进行spring的配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
</beans>
5.在model层中创建一个例子的实体类。
代码如下(示例):
public class Example {
private int eid;
private Spring ename;
public int getEid() {
return eid;
}
public void setEid(int eid) {
this.eid = eid;
}
public Spring getEname() {
return ename;
}
public void setEname(Spring ename) {
this.ename = ename;
}
@Override
public String toString() {
return "Example{" +
"eid=" + eid +
", ename=" + ename +
'}';
}
}
6.在dao层实现一个interface接口,并在接口中写一个List方法,然后再写一个实现类,用于实现这个方法。
代码如下
public interface IExampleDao {
List<Example> getExample(Example example);
}
由于还未与Mybatis进行集成,在这里先使用模拟数据
public class ExampleDao implements IExampleDao {
@Override
public List<Example> getExample(Example example) {
List<Example> list=new ArrayList<>();
//模拟数据
Example exmp = new Example();
exmp.setEid(1);
exmp.setEname("例子");
list.add(exmp);
return list;
}
}
7.在service层中调用已经定义的dao层接口,并进行实现。
与实现dao接口没有太多的差异,只要改写下后缀名即可。
public interface IExampleService {
List<Example> getExample(Example example);
}
实现方法
public class ExampleService implements IExampleService {
private ExampleDao exampleDao;
public void setExampleDao(ExampleDao exampleDao) {
this.exampleDao = exampleDao;
}
@Override
public List<Example> getExample(Example example) {
return exampleDao.getExample(example);
}
}
在还没有使用Spring之前,使用最原始的方式获取的dao层中已经定义的方法的话,不利于代码的修改,所以就得创建一个Factory工厂类,从而来获取dao层中已经定义的方法,需要修改时直接在工厂中进行修改即可,这样可以降低代码的一个耦合度。
但是在我们使用Spring框架之后,这一步就变得简单起来,直接在service层中声明一个变量即可,因为所谓依赖注入,即是通过外部文件进行注入,外部文件指的则是先前配置的Spring.xml。
8.在Spring配置文件中进行配置,也就是在Spring容器中,获取到需要关系的bean
<!--获取到dao层已经定义方法的类-->
<bean id="exampleDao" class="com.spring.dao.ExampleDao">
</bean>
<!----------------------------------------------------->
<!--将上边获取到的dao层方法注入到service层中-->
<bean id="IExampleService" class="com.spring.service.ExampleService">
<property name="exampleDao">
<ref bean="exampleDao"></ref>
</property>
</bean>
- id:在容器中查找Bean的id,不可重复。
- class:bean的完整类名。
- name:在容器中bean的名字,不可重复。
在以上代码中,可以看到在代码的中间有一个分隔符,这是为了方便能更通俗的去解释,就是将上边的那个bean注入到下边的那个bean里边。
在没使用Spring框架之前呢,是创建一个Factory工厂在service层中获取dao层中已经定义的方法。而在使用Spring之后呢,就是将这一步交给Spring的配置文件,也就是在Spring上下文(容器)中,在Spring容器之中进行一个注入。
9.随后写一个测试类,通过ApplicationContext获取到Bean,进行测试
public class Test {
public static void main(String[] args) {
//获取Spring的配置文件,也就是获取Spring的上下文(容器)
ApplicationContext ctx= new ClassPathXmlApplicationContext("spring.xml");
IExampleService iExampleService =(IExampleService) ctx.getBean("IExampleService");
List<Example> list= iExampleService.getExample(null);
//Lambda表达式
list.forEach(t-> System.out.println(t));
}
}
10.结果
示例二
在Spring容器之中配置继承关系。
1.创建实体类,在这里咱们先创建两个继承类,一个学生类,和一个人类,由学生类来继承我们的人类。
人类中只需要一个性别即可
public class Person{
private String sex;
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"sex='" + sex + '\'' +
'}';
}
}
学生类继承人类,并在toString中返回人类中的性别。
public class Student extends Person{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
"sex='" + this.getSex() + '\'' +
'}';
}
}
3.在创建好两个类之后,咱们开始配置Spring配置文件。
<bean id="person" class="com.spring.model.Person">
<!--获取属性民称,进行赋值-->
<property name="sex">
<value>男</value>
</property>
</bean>
<bean id="student" class="com.spring.model.Student" parent="person">
<property name="name">
<value>张三</value>
</property>
</bean>
在这段代码中,可以看到,有两个bean,这两个bean是继承关系,parent是表示父类,第二个bean中标记的则是表示它的父类是parent。
4.进行测试
同样还是通过ApplicationContext获取到bean,进行测试。
ApplicationContext ctx= new ClassPathXmlApplicationContext("spring.xml");
Student student =(Student) ctx.getBean("student");
System.out.println(student);
得出打印结果:
示例三
1.使用有参的构造方法创建bean。
根据上边的基础,咱们在Student类中加一个有参的方法
public Student(String name) {
this.name = name;
}
接下来就是在Spring中配置他,如果继续使用原来的方式获取bean的话,就会显示报错。
如下图所示
在这里,我们就需要换一种方式,不再使用property获取name,而是使用constructor-arg获取。
代码如下
<bean id="student" class="com.spring.model.Student" parent="person">
<constructor-arg name="name">
<value>张三</value>
</constructor-arg>
</bean>
2.init-method:指定bean的初始化方法。
在方才创建的Student类中加一个init方法
private void init(){
System.out.println("------bean的初始化方法------");
}
在Spring配置文件中,所需要指定初始化方法的bean那里加上init-method,并指定方法。
<bean id="student" class="com.spring.model.Student" parent="person" init-method="init">
<constructor-arg name="name">
<value>张三</value>
</constructor-arg>
</bean>
随后便可测试打印即可,还是获取到ApplicationContext中的bean进行测试。
结果如下
3.复杂属性的配置
1.自定义属性JavaBean属性注入。
在这里咱们需要在方才的基础上再新建一个site地址的实体类。
public class Site {
private String in;
public String getIn() {
return in;
}
public void setIn(String in) {
this.in = in;
}
}
我们先在上个示例中创建的Student类中添加一个新的属性,属性名与先前创建实体类名一致。这个属性不再是Java中的简单定义类型,而是我们自定义的类型,定义完之后,再生成他的Get与Set方法,并在它的toString方法中加入这个Site中的方法。
以下代码为Student类中所有代码
public class Student extends Person{
private String name;
private Site site;
public Site getSite() {
return site;
}
public void setSite(Site site) {
this.site = site;
}
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
"sex='" + this.getSex() + '\'' +
"in='" + this.site.getIn() + '\'' +
'}';
}
private void init(){
System.out.println("------bean的初始化方法------");
}
咱们新建的这个属性的对象需要进行依赖注入,所以肯定得交给Spring来托管,代码如下
<bean id="site" class="com.spring.model.Site">
<property name="in">
<value>长沙</value>
</property>
</bean>
<bean id="student" class="com.spring.model.Student" parent="person" init-method="init">
<constructor-arg name="name">
<value>张三</value>
</constructor-arg>
<property name="site">
<ref bean="site"/>
</property>
</bean>
在Spring容器里面获取到Site里的方法,并给他赋值,然后再放到Student中,打印结果如下。
2.List或数组的注入。
声明属性,并生成Get与Set方法。
public class Demo {
private int[] arr;
private List list;
public int[] getArr() {
return arr;
}
public void setArr(int[] arr) {
this.arr = arr;
}
public List getList() {
return list;
}
public void setList(List list) {
this.list = list;
}
}
在这里咱们是新建了一个demo类,之后便是在Spring容器中通过bean的方式获取到,然后进行配置。
<bean id="demo" class="com.spring.model.Demo">
<property name="list">
<list>
<value>111</value>
<value>222</value>
<value>333</value>
<value>444</value>
</list>
</property>
</bean>
在Spring容器中通过List标签对它进行赋值,随后便可进行测试。测试数组只需要在获取属性名称将list改为arr即可进行测试。
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
Demo demo =(Demo) ctx.getBean("demo");
//List方法测试
demo.getList().forEach(t-> System.out.println(t));
//数组测试
int[] arr = demo.getArr();
for (int a:arr){
System.out.println(a);
}
}
3.Map的注入,Spring配置如下。
<bean id="demo" class="com.spring.model.Demo">
<property name="map">
<map>
<entry key="aa" value="bb"/>
<entry key="cc" value="dd"/>
<entry key="ee" value="ff"/>
</map>
</property>
</bean>
4.多模块配置文件
在版本控制软件中也为了减少冲突,系统一般会被分成多个模块,可以为每个模块配置一个配置文件,以便于管理。以下咱们有两个配置文件。
配置文件spring-a.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" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<bean id="demo" class="com.spring.model.Demo">
<property name="map">
<map>
<entry key="aa" value="bb"/>
<entry key="cc" value="dd"/>
<entry key="ee" value="ff"/>
</map>
</property>
</bean>
</beans>
总配置文件spring.xml,我们只需要在这个总的配置文件中引入上边的分模块即可,在需要使用Spring时,直接获取Spring.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" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<import resource="spring-a.xml"/>
</beans>