什么是Spring?
In a word:Spring是一个轻量级的控制反转(IoC-Inversion of Control)和
面向切面(AOP-Aspect Oriented Programming)的容器(框架)。
特点
(1)方便解耦,简化开发
(2)Aop 编程支持
(3)方便程序测试
(4)方便和其他框架进行整合
(5)方便进行事务操作
(6)降低 API 开发
1. 体系结构
![](https://img-blog.csdnimg.cn/47f3fe0d77894d6a93f30571edbbc6ac.bmp?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAR2VudGlsaXR5ICBkZW1vbiAgLlpZ,size_14,color_FFFFFF,t_70,g_se,x_16)
1.1 体系结构图
Spring框架至今已集成了20多个模块,这些模块分布在以下模块中:
- 核心容器(Core Container)
- 数据访问/集成(Data Access/Integration)层
- Web层
- AOP(Aspect Oriented Programming)模块
- 植入(Instrumentation)模块
- 消息传输(Messaging)
- 测试(Test)模块
1.2 核心容器(Core Container)
Spring的核心容器是其他模块建立的基础,有Spring-core、Spring-beans、Spring-context、Spring-context-support和Spring-expression(String表达式语言)等模块组成。
- Spring-core模块:提供了框架的基本组成部分,包括控制反转(Inversion of Control,IoC)和依赖注入(Dependency Injection,DI)功能。
- Spring-beans模块:提供了BeanFactory,是工厂模式的一个经典实现,Spring将管理对象称为Bean。它移除了编码式单例的需要,并且可以把配置和依赖从实际编码逻辑中解耦。
- Spring-context模块:建立在Core和Beans模块的基础之上,提供一个框架式的对象访问方式,它以一种类似于 JNDI 注册的方式访问对象。是访问定义和配置的任何对象的媒介。ApplicationContext接口是Context模块的焦点。
- Spring-context-support模块:支持整合第三方库到Spring应用程序上下文,特别是用于高速缓存(EhCache、JCache)和任务调度(CommonJ、Quartz)、邮件(JavaMail)、调度(CommonJ, Quartz)、模板引擎(FreeMarker, JasperReports, Velocity)的支持。
- Spring-expression模块:提供了强大的表达式语言去支持运行时查询和操作对象图。这是对JSP2.1规范中规定的统一表达式语言(Unified EL)的扩展。该语言支持set和get属性值、属性分配、方法调用、访问数组、集合和索引器的内容、逻辑和算术运算、变量命名以及通过Spring的IOC容器中以名称检索对象。它还支持列表投影、选择以及常用的列表聚合。
依赖图
依赖图1.2.1
依赖图1.2.2
实例依赖图1.2.3
注:其中spring框架不知道aop是注解方式中必须加的一个依赖
1.3 Spring 配置文件
--》applicationContext.xml
1.2.1 控制反转和依赖注入(IOC-DI)
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。DL
依赖注入(DI),是指程序运行过程中,如果需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部的注入。Spring的依赖注入对调用者和被调用者几乎没有任何要求,完全支持对POJO之间依赖关系的管理。
- 依赖 : 指Bean对象的创建
依赖于Spring容器
- 注入 : 指Bean对象的属性(例如
pojo类对象属性
、其它需要依赖的Bean对象
) , 由容器来设置
和装配
.
传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象
;
IOC是由专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建,程序被动接受对象
,这就叫控制反转:将对象的创建权反转给(交给)Spring
在Spring中,我们可以把IOC容器与Spring容器
等同于一个东西,及IOC容器=Spring容器
,我们也可以把Spring容器看做配置文件xxx.xml来简单记忆
容器:一个Java 所编写的程序,可以管理对象的生命周期、对象与对象之间的依赖关系,在启动容器之后,所有的对象都可以直接取用,可以直接产生对象,或是建立对象与对象之间的依赖关系(不用编写任何程序代码)。
(注::框架:是开发程序的一部分,没有框架是必须存在的。)
- IOC -- Inverse of Control, 控制反转,将对象的创建权反转给Spring!!
- DI -- Dependency Injection,依赖注入,在Spring框架负责创建Bean对象时,动态的将依赖对象注入到Bean组件中!!IoC的技术实现
DI和IOC的关系: DI不能单独存在,DI需要在IOC的基础上来完成.
这样做得好处:做到了单一职责,并且提高了复用性,解耦了之后,任你如何实现,使用接口的引用调用的方法,永远不需要改变
解耦
以下是一个使用xml配置文件实现IOC的图例:
1.2.2 DI:给属性赋值
spring调用类的无参构造方法,创建对象。对象创建后给属性赋值。
给属性赋值可以使用
1)xml配置文件中的标签和属性;
2)使用注解。
DI分类 :1 set注入,也叫设值注入;
2 构造注入。
package com.bjpowernode;
import com.bjpowernode.service.SomeService;
import com.bjpowernode.service.impl.SomeServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppMain {
public static void main(String[] args) {
// SomeService service = new SomeServiceImpl();
// service.doSome();
//1.指定spring配置文件:从类路径(classpath)之下开始的路径
String config="beans.xml";
//2.创建容器对象。 ApplicationContext 表示spring容器对象。通过ctx来获取某个java对象
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
//这是个接口 然后通过这个实现类ClassPathXmlApplicationContext的构造方法中负责读这个配置文件 当遇到bean标签后 就创建对象 从这个类路径下寻找这个内容
//3.从容器中获取指定名称的对象 使用getBean方法 //又因为是SomeService接口类型的对象所以还要转一下
SomeService service = (SomeService) ctx.getBean("someService");
//拿到这个someservice对象然后用其方法
//4.调用对象的方法,接口的方法
service.doSome();
}
}
package com.bjpowernode.service;
public interface SomeService {
void doSome();
}
package com.bjpowernode.service;
public class OtherService {
public void doOther(){
System.out.println("执行otherService的doOther()");
}
}
resources下配置文件
<?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">
<!-- 声明对象
id:自定义对象名称,唯一值。(可以没有,Spring可提供默认名称)
class:类的全限名称:spring 通过反射机制创建对象,不能是接口
spring根据id,class创建对象,把对象放入到spring的一个map对象
map.put(id,对象) map(key,value)
-->
<bean id="someService" class="com.bjpowernode.service.impl.SomeServiceImpl"></bean>
<bean id="someService1" class="com.bjpowernode.service.impl.SomeServiceImpl"></bean>
<bean id="otherService" class="com.bjpowernode.service.OtherService"></bean>
<!--创建非自定义对象 -->
<bean id="mydate" class="java.util.Date"></bean>
</beans>
测试文件
package com.bjpowernode;
import com.bjpowernode.service.OtherService;
import com.bjpowernode.service.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Date;
public class Mytest {
/**
* spring创建对象,调用的是类的哪个方法?
* 答:默认是调用了无参数构造方法 。
*/
@Test
public void test01(){
//1.先指定配置文件路径
String config="beans.xml";
//2.创建容器对象
//ApplicationContext是个接口 用他的这个ClassPathXmlApplicationContext这个实现类 从类路径中来读取加载配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
// SomeService service = ctx.getBean(SomeService.class);
// service.doSome();
SomeService service=(SomeService)ctx.getBean("someService");
service.doSome();
}
/**
* spring在什么时候创建对象?
* 答:创建spring容器对象的时候创建的,会读取配置文件,创建文件中声明的java对象。
* 优点:获取对象速度快,因为对象已经创建好了
* 缺点:占用内存
*/
@Test
public void test02(){
//1.先指定配置文件路径
String config="beans.xml";
//2.创建容器对象
//ApplicationContext是个接口 用他的这个ClassPathXmlApplicationContext这个实现类 从类路径中来读取加载配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
// SomeService service=(SomeService)ctx.getBean("someService");
// service.doSome();
}
/**
* spring容器创建对象 一次创建几个?
* 在创建容器对象时,会把配置文件中所有对象创建出来(spring默认规则)
*/
@Test
public void test03(){
//1.先指定配置文件路径
String config="beans.xml";
//2.创建容器对象
//ApplicationContext是个接口 用他的这个ClassPathXmlApplicationContext这个实现类 从类路径中来读取加载配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
// SomeService service=(SomeService)ctx.getBean("someService");
// service.doSome();
}
/**
* 获取容器中对象的信息
*/
@Test
public void test04(){
String config="beans.xml";
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
//获取容器中定义对象的数量
int nums = ctx.getBeanDefinitionCount();
System.out.println("容器中定义对象的数量=="+nums);
//获取容器中定义的对象名称
String names[] = ctx.getBeanDefinitionNames();
for(String name:names){
System.out.println("容器中对象的名称=="+name);
}
// new java.util.Date();
}
// 让spring创建非自定义类的对象
// 有class就能让spring创建对象
@Test
public void test05() {
//1.先指定配置文件路径
String config = "beans.xml";
//2.创建容器对象
//ApplicationContext是个接口 用他的这个ClassPathXmlApplicationContext这个实现类 从类路径中来读取加载配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
Date date =(Date) ctx.getBean("mydate");
System.out.println("date=="+date);
// 从容器中拿对象
OtherService service =(OtherService) ctx.getBean("otherService");
service.doOther();
}
}
测试结果 解释 如下
解释:spring默认使用无参构造方法,创建对象 若定义了有参一定要定义无参
因为配置文件中有两个bean标签
<bean id="someService" class="com.bjpowernode.service.impl.SomeServiceImpl"></bean> <bean id="someService1" class="com.bjpowernode.service.impl.SomeServiceImpl"></bean>
所以创建了两个对象
//2.创建容器对象 //ApplicationContext是个接口 用他的这个ClassPathXmlApplicationContext这个实现类 从类路径中来读取加载配置文件 把对象创建出来 ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
实现类
package com.bjpowernode.service.impl;
import com.bjpowernode.service.SomeService;
public class SomeServiceImpl implements SomeService {
/**
* spring默认使用无参构造方法,创建对象
* 如果定义了有参 一定要调用无参
*/
public SomeServiceImpl(){
System.out.println("SomeServiceImpl的无参数构造方法");
//无参构造执行了说明对象创建了 意味着spring框架默认执行的是无参构造方法
}
@Override
public void doSome(){
System.out.println("执行了业务方法doSome");
}
}
看注释无参构造执行了说明对象创建了 意味着spring框架默认执行的是无参构造方法
SomeService service=(SomeService)ctx.getBean("someService"); service.doSome();
从容器中获取指定名称的对象,使用getBean()
getBean(括号里是"引号中的是对象的名字 也是就<Bean id="对象的名字">")
spring在什么时候创建对象?Test02
创建spring容器对象的时候创建的,会读取配置文件,创建文件中声明的java对象。 * 优点:获取对象速度快,因为对象已经创建好了 * 缺点:占用内存
spring容器创建对象 一次创建几个? Test03
* 在创建容器对象时,会把配置文件中所有对象创建出来(spring默认规则)
获取容器中对象的信息Test04 如图
/**
* 获取容器中对象的信息
*/
@Test
public void test04(){
String config="beans.xml";
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
//获取容器中定义对象的数量
int nums = ctx.getBeanDefinitionCount();
System.out.println("容器中定义对象的数量=="+nums);
//获取容器中定义的对象名称
String names[] = ctx.getBeanDefinitionNames();
for(String name:names){
System.out.println("容器中对象的名称=="+name);
}
// new java.util.Date();
}
控制台: 为什么输出前两行看实现类写了一个无参构造里sout
spring创建非自定义类的对象
// 让spring创建非自定义类的对象
// 有class就能让spring创建对象
@Test
public void test05() {
//1.先指定配置文件路径
String config = "beans.xml";
//2.创建容器对象
//ApplicationContext是个接口 用他的这个ClassPathXmlApplicationContext这个实现类 从类路径中来读取加载配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
Date date =(Date) ctx.getBean("mydate");
System.out.println("date=="+date);
// 从容器中拿对象
OtherService service =(OtherService) ctx.getBean("otherService");
service.doOther();
}
Spring属性赋值的分类
pojo类
因为是set注入所以只要set方法就行
package com.bjpowernode.ba01;
public class Student {
private String name;
private int age;
public Student() {
System.out.println("Student无参数构造方法");
}
public void setName(String name) {
System.out.println("setName=="+name);
this.name ="Hello"+name;
}
public void setAge(int age) {
System.out.println("setAge=="+age+"也就是先创建对象再进行set注入");
this.age = age;
}
public void setEmail(String email){
// email属性 <property name="email" value="list@qq.com"/>
System.out.println("setEmail==="+email);
}
public void setGender(String gender){
System.out.println("setGender=="+gender);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
配置文件
<?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">
<!--声明bean -->
<!--
DI:给属性赋值
简单类型:java中的基本数据类型和String
1.set注入:spring调用类的set方法,通过set方法完成属性赋值
简单类型的set注入:
语法:<bean id="xxx" class="yyy">
<property name="pojo属性名" value="简单类型属性值"/>
...
</bean>
-->
<!--简单类型set注入-->
<bean id="myStudent" class="com.bjpowernode.ba01.Student">
<property name="name" value="李四"></property><!--setName("李四") -->
<property name="age" value="22"></property><!-- setAge("20")-->
<property name="email" value="list@qq.com"></property><!-- setEmail()-->
<property name="gender" value="男"></property>
</bean>
<!--声明日期类 -->
<bean id="mydate" class="java.util.Date">
<property name="time" value="9990000000000"></property><!--setTime() -->
</bean>
</beans>
Test测试类
package com.bjpowernode.ba01;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Date;
public class MyTest01 {
@Test
public void test01(){
String config = "ba01/applicationContext.xml";
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
Student student = (Student) ctx.getBean("myStudent");
System.out.println("student=="+student);
Date date = (Date) ctx.getBean("mydate");
System.out.println("date=="+date);
}
}
运行结果
解释分析
第一行是因为spring默认使用无参构造方法,创建对象
第三行是因为spring和咱们约定的是只调用set方法 而set方法里由自己决定 我在里面加了一条sout如图b
2-5行都是set注入
第六行 在测试类中进行的输出 Hello李四是我在setName()方法中this.name=name;改成了
this.name="Hello"+name;
spring创建对象自动调用set方法 set方法里面的内容加不加都可以 在set代码里面可以对赋值进行改变加东西
给属性赋值看得是set方法和有没有属性无关
名
第七行是 给非自定义类属性赋值 只要能找到该class就可以对其在配置文件中配置然后进行赋值
前三行是因为 配置文件中有三个Bean标签需要创建三个对象
<!-- 引用类型自动注入:spring根据byName,byType规则给引用类型赋值
1.byName(按名称注入):java类中引用类型的属性名称和spring容器中bena的id名称一样,
且数据类型一样的,这样的bean能够赋值给引用类型。
语法:
<bean id="xxx" class="yyy" autowire="byName">
简单类型属性赋值
</bean>
2.1.byType(按类型注入):java类中引用类型的数据类型和bean的class是同源的,
这些bean能够赋值给引用类型
同源关系:
1.java中引用类型的数据类型和bean的class值是一样的。
2.java中引用类型的数据类型和bean的class值是父子类关系的。
3.java中引用类型的数据类型和bean的class值是接口和实现类关系的。
语法:
<bean id="xxx" class="yyy" autowire="byType">
简单类型属性赋值
</bean>
注意:在xml配置文件中,符合条件的对象,只能有一个。
多余一个是报错的。
-->
<!--School相关类声明-->
<bean id="mySchool" class="com.ziyang.diEnd.School">
<property name="name" value="哈尔滨工程大学"></property><!---->
<property name="address" value="哈尔滨的学院路"></property><!---->
</bean>
<!-- 学生模块的bean -->
<bean id="myStudent" class="com.ziyang.diEnd.Student" autowire="byType">
<property name="name" value="张冉"></property><!--setName("李四") -->
<property name="age" value="666"></property><!-- setAge("20")-->
</bean>
给引用类型赋值
测试类
byName()自动注入
怎么实现的 按名称来进行比较来完成属性赋值
String config="applicationContext,xml"
当我们执行ApplicationContext ctx = new ClassPathXmlApplicationContext(config);时
会去识别这个配置文件
读到<bean >标签时先去创建这个对象 当看见autowire="byName"时会自动把 Student类中所有引用类型赋值 那么他就找这个类 找引用类型找School school 这个对象名字 找到之后 拿着这个名字和配置文件中的每一个<bean >标签的id比
然后再给两个属性赋值
byType()自动注入
怎么实现的 按类型来进行比较来完成属性赋值
java类中引用类型的数据类型和spring容器中的bean的class值是同源关系的,这样的bean赋值给引用类型。
同源关系:
1.java中引用类型的数据类型和bean的class值是一样的。
2.java中引用类型的数据类型和bean的class值是父子类关系的。
3.java中引用类型的数据类型和bean的class值是接口和实现类关系的。
语法:
<bean id="xxx" class="yyy" autowire="byType">
简单类型属性赋值
</bean>
注意:在xml配置文件中,符合条件的对象,只能有一个。
多余一个是报错的。
然后再给两个属性赋值
缺点:(引入注解的原因)
一个大型项目中 应用多个配置文件 主要目的是 能够减小单一文件大小 让编写配置文件更多一点更方便
2.2 基于注解的DI
基于注解的DI:使用spring提供的注解,完成java对象创建,属性赋值。
注解使用的核心步骤:
1)在源代码中加入注解,eg:Component
2)在spring的配置文件,加入组件扫描器的标签
注解是对程序代码的一个说明
1.创建对象的注解
@Component
@Respository
@Service
@Controller
2.简单类型属性赋值
@Value
3.引用类型赋值
@Autowired:Spring提供的注解,支持byName,byType
@Autowired:默认是byType
或者
@AutoWired
@Qualifier:使用byName
@Resource:来自jdk中的注解,给引用类型赋值的,默认是byName
@Resource:先使用byName,在byType
若@Resource(name="bean的名称"):只能使用byName注入
2.3IoC总结
IoC:管理对象的,把对象放在容器中,创建,赋值,管理依赖关系。
IoC:通过管理对象,实现解耦合。IoC解决处理业务逻辑对象之间的解耦合关系,也就是Servlet和dao之间的解耦合。
spring作为容器管理什么对象?
1) service对象,dao对象。
2)工具类对象。
不适合交给spring的对象?
1)实体类
2)servlet,listener,filter等web中的对象。他们上tomcat创建和管理的。
代码和配置文件分离 若对象要调整只需该配置文件