Spring5学习笔记
# 一、Spring 概述
Spring诞生之初,主要目的是用来替代更加重量级的企业级技术 尤其是EJB,相对于EJB来说Spring提供了更加轻量级和简单的编程模型。它增强了简单老式的Java对象POJO的功能,使其具备了之前只有EJB和其他企业级Java规范才有的功能。
# 1、Spring的优点
- Spring是一个开源的免费的框架(容器)
- Spring是一个轻量级、非入侵式的框架
- 控制反转(IOC)、面向切面(AOP)
- 支持事务处理、对框架整合的支持!
总结:Spring就是一个轻量级的控制反转(IOC)和面向切面(AOP)的框架!
非常轻量级的容器,低侵入,代码污染低。独立于各种应用服务器.DI(IOC)降低了业务对象替换复杂性
# 2、为什么使用Spring?
下面列出的是使用 Spring 框架主要的好处:
- Spring 可以使开发人员使用 POJOs 开发企业级的应用程序。只使用 POJOs 的好处是你不需要一个 EJB 容器产品,比如一个应用程序服务器,但是你可以选择使用一个健壮的 servlet 容器,比如 Tomcat 或者一些商业产品。
- Spring 在一个单元模式中是有组织的。即使包和类的数量非常大,你只需要选择你需要的部分,而忽略剩余的那部分。
- Spring 不会让你白费力气做重复工作,它真正的利用了一些现有的技术,像几个 ORM 框架、日志框架、JEE、Quartz 和 JDK 计时器,其他视图技术。
- 测试一个用 Spring 编写的应用程序很容易,因为 environment-dependent 代码被放进了这个框架中。此外,通过使用 JavaBean-style POJOs,它在使用依赖注入注入测试数据时变得更容易。
- Spring 的 web 框架是一个设计良好的 web MVC 框架,它为 web 框架,比如 Structs 或者其他工程上的或者很少受欢迎的 web 框架,提供了一个很好的供替代的选择。
- 为将特定技术的异常(例如,由 JDBC、Hibernate,或者 JDO 抛出的异常)翻译成一致的, Spring 提供了一个方便的 API,而这些都是未经检验的异常。
- 轻量级的 IOC 容器往往是轻量级的,例如,特别是当与 EJB 容器相比的时候。这有利于在内存和 CPU 资源有限的计算机上开发和部署应用程序。
- Spring 提供了一个一致的事务管理界面,该界面可以缩小成一个本地事务(例如,使用一个单一的数据库)和扩展成一个全局事务(例如,使用 JTA)。
# 3、Spring能做什么?
Spring能程序员简化开发:
- Spring根据配置文件来进行创建及组装对象间依赖关系(IOC),只需要改配置文件即可,无需重新编译。Spring能帮我们根据配置文件创建及组装对象之间的依赖关系。
- 重复业务逻辑的处理,在AOP思想中,Spring 面向切面编程能帮助我们无耦合的实现日志记录,性能统计,安全控制等。
- 原始的支持jdbc事务处理繁琐,Spring能非常简单的帮我们管理数据库事务。
- Spring还提供了与第三方ORM框架无缝集成,而且自己也提供了一套JDBC访问模板,来方便数据库访问。
- Spring还提供与第三方Web(如Struts、JSF)框架无缝集成,而且自己也提供了一套Spring MVC框架,来方便web层搭建。
- Spring能方便的与Java EE(如Java Mail、任务调度)整合,与更多技术整合(比如缓存框架)。
# 4、Spring核心思想
Spring 最核心的两个技术思想是:
IoC
即Inversion of Control
,意为控制反转。Spring 最认同的技术是控制反转的**依赖注入(DI)**模式。控制反转(IoC)是一个通用的概念,它可以用许多不同的方式去表达,依赖注入仅仅是控制反转的一个具体的例子。
当编写一个复杂的 Java 应用程序时,应用程序类应该尽可能的独立于其他的 Java 类来增加这些类可重用可能性,当进行单元测试时,可以使它们独立于其他类进行测试。依赖注入(或者有时被称为配线)有助于将这些类粘合在一起,并且在同一时间让它们保持独立。
到底什么是依赖注入?让我们将这两个词分开来看一看。这里将依赖关系部分转化为两个类之间的关联。例如,类 A 依赖于类 B。现在,让我们看一看第二部分,注入。所有这一切都意味着类 B 将通过 IoC 被注入到类 A 中。
依赖注入可以以向构造函数传递参数的方式发生,或者通过使用 setter 方法 post-construction。由于依赖注入是 Spring 框架的核心部分,所以我将在一个单独的章节中利用很好的例子去解释这一概念。
面向方面的程序设计(
AOP
)框架Spring 框架的一个关键组件是**面向方面的程序设计(AOP)*框架。一个程序中跨越多个点的功能被称为*横切关注点,这些横切关注点在概念上独立于应用程序的业务逻辑。有各种各样常见的很好的关于方面的例子,比如日志记录、声明性事务、安全性,和缓存等等。
在 OOP 中模块化的关键单元是类,而在 AOP 中模块化的关键单元是方面。AOP 帮助你将横切关注点从它们所影响的对象中分离出来,然而依赖注入帮助你将你的应用程序对象从彼此中分离出来。
Spring 框架的 AOP 模块提供了面向方面的程序设计实现,允许你定义拦截器方法和切入点,可以实现将应该被分开的代码干净的分开功能。我将在一个独立的章节中讨论更多关于 Spring AOP 的概念。
# 5、Spring体系结构
Core模块
:封装了框架依赖的最底层部分,包括资源访问、类型转换及一些常用工具类。Beans模块
:提供了框架的基础部分,包括反转控制和依赖注入。其中Bean Factory是容器核心(工厂),本质是“工厂设计模式”的实现,而且无需编程实现“单例设计模式”,单例完全由容器控制**,而且提倡面向接口编程,而非面向实现编程;所有应用程序对象及对象间关系由框架管理,从而真正把你从程序逻辑中把维护对象之间的依赖关系提取出来,所有这些依赖关系都由BeanFactory来维护。**Context模块
:以Core和Beans为基础,集成Beans模块功能并添加资源绑定、数据验证、国际化、Java EE支持、容器生命周期、事件传播等;核心接口是ApplicationContext。EL模块
:提供强大的表达式语言支持,支持访问和修改属性值,方法调用,支持访问及修改数组、容器和索引器,命名变量,支持算数和逻辑运算,支持从Spring 容器获取Bean,它也支持列表投影、选择和一般的列表聚合等。AOP模块
:Spring AOP模块提供了符合 AOP Alliance规范的面向方(切)面的编程(aspect-oriented programming)实现,提供比如日志记录、权限控制、性能统计等通用功能和业务逻辑分离的技术,并且能动态的把这些功能添加到需要的代码中;这样各专其职,降低业务逻辑和通用功能的耦合。DataACCESS模块
:Spring本身的 JDBC, 和其他的框架整合 ORM(Mybatis Hibernate)WEB模块
: Web的部分(Spring MVC),和其他框架如:Struts2整合的部分;

# 6、Spring弊端
发展的太久了,违背的原来的理念,配置太繁琐了,简称:配置地狱!
# 7、拓展
- Spring Boot
- 一个快速开发的脚手架
- 基于Spring-Boot可以快速开发单个微服务。
- 和Maven一样、是约定大于配置。
- Spring Cloud
- 基于SpringBoot实现的。
现在大部分公司都在使用Spring Boot进行快速开发,学习SpringBoot的前提是需要完全掌握Spring及Spring MVC,是承上启下的作用!
# 二、Spring-IOC理论推导
# 1、理论推导
- UserDao接口
- UserDaoImpl实现类
- UserService业务接口
- UserServiceImpl业务实现类
在我们之前的业务中,用户的需求可能会影响到我们原来的代码,我们需要根据用户的需求去改写代码,修改代码量十分多,修改一次的成本与代价十分昂贵!
我们使用一个Set接口实现,就会发生革命性的变化!
package com.singerw.dao.impl;
import com.singerw.dao.UserDao;
public class UserDaoImpl implements UserDao {
private UserDao userDao;
//利用Set进行动态实现值得注入!
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
2
3
4
5
6
7
8
9
10
11
- 之前,程序是主动创建对象!控制权在程序员受伤!
- 使用了Set注入后,程序不再具有主动权,而是变成了被动的接受对象!
- 这种思想,从本质上解决了问题,我们程序员不用再用管理对象的创建了,系统的耦合性大大的降低,可以更加专注在业务的实现上!这就是IOC的原型!
# 2、IOC本质
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
- IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。
- Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。
- 采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
- 控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
# 3、HelloSpring!
package com.singerw.pojo;
public class Hello {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
public Hello(String str) {
this.str = str;
}
public Hello() {
}
@Override
public String toString() {
return "Hello{" +
"str='" + str + '\'' +
'}';
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?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创建我们的对象
Hello hello = new Hello();
bean = 对象 new Hello();
id = 变量名
class = new 的对象
property 相当于给对象中的属性设值。
-->
<bean id="hello" class="com.singerw.pojo.Hello">
<property name="str" value="Spring"/>
</bean>
</beans>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import com.singerw.pojo.Hello;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void Test() {
// 获取Spring上下文的对象!
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
//对象现在都在Spring中管理了,我们要使用,就直接去里面取出来就行了。
Hello hello = (Hello) applicationContext.getBean("hello");
System.out.println(hello.toString());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Hello{str='Spring'}
- Hello对象是谁创建的?
hello对象是由Spring创建的
- hello对象的属性是怎么设置的?
hello对象的属性使用Spring容器设置的
这个过程就叫做控制反转:
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring之后,对象是由Spring创建的
反转:程序本身不创建对象,而变成被动的接受对象。
依赖注入:就是利用set方法来进行注入的
IOC是一种编程思想,由主动的编程变成被动的接收。
不用再程序中去改动了,想要实现不同的操作,只需要再XML配置文件中进行修改,所谓的IOC,一句话搞懂就是:对象由Spring来创建,管理和装配!
# 4、IOC入门案例
# 步骤一:导入Jar包
注 : spring 需要导入commons-logging进行日志记录 . 我们利用maven , 他会自动下载对应的依赖项 .
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
2
3
4
5
# 步骤二:编写代码
【接口】userDao.java
public interface userDao {
public void getUser();
}
2
3
【接口实现类】UserDaoImpl.java
public class UserDaoImpl implements UserDao {
@Override
public void getUser() {
System.out.println("获取用户数据");
}
}
2
3
4
5
6
7
【接口实现类】UserDaoMySqlImpl.java
public class UserDaoMySqlImpl implements UserDao {
@Override
public void getUser() {
System.out.println("MySql获取用户数据");
}
}
2
3
4
5
6
7
【接口实现类】UserDaoOracleImpl.java
public class UserDaoOracleImpl implements UserDao {
@Override
public void getUser() {
System.out.println("Oracle获取用户数据");
}
}
2
3
4
5
6
7
【业务层接口】UserService.java
public interface UserService {
public void getUser();
}
2
3
【业务层接口】UserServiceImpl.java
public class UserServiceImpl implements UserService {
private UserDao userDao;
// 利用set实现
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
【Spring配置文件】beans.xml
编写spring配置文件 , 这里我们命名为beans.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">
<bean id="MysqlImpl" class="com.singerw.dao.impl.UserDaoMySqlImpl"/>
<bean id="OracleImpl" class="com.singerw.dao.impl.UserDaoOracleImpl"/>
<bean id="ServiceImpl" class="com.singerw.service.impl.UserServiceImpl">
<!--注意: 这里的name并不是属性 , 而是set方法后面的那部分 , 首字母小写-->
<!--引用另外一个bean , 不是用value 而是用 ref
ref: 引用Spring容器中创建好的对象
value:具体的值,基本数据类型!
-->
<property name="userDao" ref="MysqlImpl"/>
</bean>
</beans>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ref
: 引用Spring容器中创建好的对象value
:具体的值,基本数据类型!
【测试】MyTest.java
public void MyTest(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserServiceImpl serviceImpl = (UserServiceImpl) context.getBean("ServiceImpl");
serviceImpl.getUser();
}
2
3
4
5
输出:MySql获取用户数据
同理,在web.xml中改为oracle,输出就变成oracle了
<bean id="ServiceImpl" class="com.singerw.service.impl.UserServiceImpl">
<!--注意: 这里的name并不是属性 , 而是set方法后面的那部分 , 首字母小写-->
<!--引用另外一个bean , 不是用value 而是用 ref-->
<property name="userDao" ref="Oracle"/>
</bean>
2
3
4
5
输出:Oracle获取用户数据
# 4、总结
所谓的IOC,就是对象由Spring创建、管理、装配!
# 三、Spring配置
# 1、别名alias
<alias name="userT" alias="userNew"/>
# 2、bean
<bean id="hello" name="hello2 h2,h3;h4" class="com.kuang.pojo.Hello">
<property name="name" value="Spring"/>
</bean>
2
3
4
5
- bean就是java对象,由Spring创建和管理
- id 是bean的标识符,要唯一,如果没有配置id,name就是默认标识符
- 如果配置id,又配置了name,那么name是别名
- name可以设置多个别名,可以用逗号,分号,空格隔开
- 如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象,class是bean的全限定名=包名+类名
# 3、import
<import resource="{path}/beans.xml"/>
团队的合作通过import来实现 .
使用 @Bean
、@Component
、@Import
注解注册 Spring Bean。
这个import,一般用户团队开发使用,他可以将多个配置文件,导入合并为一个
假设,现在项目中有多个人同时开发,这桑耳复制不用的类开发,不同的类需要注册不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的!
- 张三 zhangsan.xml
- 李四 lisi.xml
- 王五 wangwu.xml
- applicationContext.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">
<import resource="zhangsan.xml"></import>
<import resource="lisi.xml"></import>
<import resource="wangwu.xml"></import>
</beans>
2
3
4
5
6
7
8
9
10
11
12
使用的时候,使用总的配置文件xml就可以了,会自动合并xml和xml中重名的配置。
# 四、Spring-IOC
控制反转(IOC),不是什么技术,而是一种设计思想,其作用是实例化具体的bean,动态装配bean。
- 类的一些属性: (例如UserService中的userDao成员属性)是由当前类(UserService)自己控制其实例化,现在不是由当前类(UserService)自己控制。现在Spring是利用接口来控制的。由原来控制实现转为spring现在来控制接口(向上反转).
- 我们可以使用xml或者注解来进行相关配置,spring会根据配置和约定,对独享进行实例化和属性装配。
好处:IOC是Spring的核心机制,可以使Spring的bean以配置文件组织在一起,而不是硬编码方式耦合,耦合性相对来降低了;另外,注入是使用配置文件来实现,这样修改来非常的方便。
# 1、Spring IOC容器解析
Spring Ioc
容器的代表就是org.springframework.beans
包中的BeanFactory
接口,BeanFactory
接口提供了IoC
容器最基本功能;而org.springframework.context
包下的ApplicationContext
接口扩展了BeanFactory
,还提供了与Spring AOP
集成、国际化处理、事件传播及提供不同层次的context
实现 (如针对web
应用的WebApplicationContext
)。简单说, BeanFactory
提供了IoC
容器最基本功能,而 ApplicationContext
则增加了更多支持企业级功能支持。ApplicationContext
完全继承BeanFactory
,因而BeanFactory
所具有的语义也适用于ApplicationContext
。
# 2、ICO创建对象的方式
1、使用无参构造创建对象,默认!
public class User {
private String name;
public User() {
System.out.println("user无参构造方法");
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name="+ name );
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
user无参构造方法
2、假设要使用有参构造创建对象!
public class UserT {
private String name;
public User() {
System.out.println("user无参构造方法");
}
public UserT(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name="+ name );
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- 下标赋值
<!-- 第一种根据index参数下标设置 -->
<bean id="userT" class="com.singerw.pojo.User">
<!-- index指构造方法 , 下标从0开始 -->
<constructor-arg index="0" value="singerw"/>
</bean>
2
3
4
5
6
- 类型赋值【不推荐使用】
<bean id="userT" class="com.singerw.pojo.User">
<!-- name指参数名 -->
<constructor-arg name="name" value="singerw"/>
</bean>
2
3
4
- 参数名赋值【重点掌握】
<!-- 第三种根据参数类型设置 -->
<bean id="userT" class="com.singerw.pojo.User">
<constructor-arg type="java.lang.String" value="singerw"/>
</bean>
2
3
4
结论:配置文件中创建bean后,在配置文件加载的时候。其中管理的对象都已经初始化了!
# 五、DI依赖注入
依赖注入是一种消除类之间依赖关系的设计模式。例如,A类要依赖B类,A类不再直接创建B类,而是把这种依赖关系配置在外部xml文件(或java config文件)中,然后由Spring容器根据配置信息创建、管理bean类。
- 依赖注入(Dependency Injection,DI)
- 依赖 : 指Bean对象的创建依赖于容器 . Bean对象的依赖资源
- 注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配
# 1、构造器注入
<bean id="p" class="com.singerw.entity.Person">
<constructor-arg index="0" type="java.lang.String" value="singerw"></constructor-arg>
<constructor-arg index="1" type="java.lang.Integer" value="18"></constructor-arg>
<constructor-arg index="2" type="java.lang.Integer" value="男"></constructor-arg>
</bean>
2
3
4
5
# 2、set注入
<bean id="address" class="com.singew.pojo.Address"></bean>
<bean id="student" class="com.singew.pojo.Student">
<!--第一种,普通值的注入,value-->
<property name="name" value="张欣"></property>
<!--第二种,Bean注入,ref-->
<property name="address" ref="address"/>
<!--第三种,数组的注入-->
<property name="books">
<array>
<value>红楼梦</value>
<value>西游记</value>
<value>三国演义</value>
<value>水浒传</value>
</array>
</property>
<!--第四种,List注入-->
<property name="hobbys">
<list>
<value>听歌</value>
<value>掉代码</value>
<value>看电影</value>
</list>
</property>
<!--第五种,Map注入-->
<property name="card">
<map>
<entry key="key" value="value"/>
<entry key="身份证" value="5464556454645651415"/>
<entry key="银行卡" value="8795461525678954645459845415"/>
</map>
</property>
<!--第六种,Set注入-->
<property name="games">
<set>
<value>LOL英雄联盟</value>
</set>
</property>
<!--第七种,props空值注入-->
<property name="wife" value=""/>
<property name="info">
<props>
<prop key="学号">20171649</prop>
<prop key="性别">男</prop>
<prop key="姓名">小明</prop>
</props>
</property>
</bean>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# 3、拓展注入
- P命名空间
导入约束 : xmlns:p="http://www.springframework.org/schema/p"
<!--P(属性: properties)命名空间 , 属性依然要设置set方法-->
<bean id="user" class="com.singerw.pojo.User" p:name="singerw" p:age="18"/>
2
- C命名空间
导入约束 : xmlns:p="http://www.springframework.org/schema/p"
<!--C(构造: Constructor)命名空间 , 属性依然要设置set方法-->
<bean id="user" class="com.singerw.pojo.User" c:name="singerw" c:age="18"/>
2
发现问题:爆红了,是因为要写有参构造!
解决:把有参构造器加上,这里也能知道,c 就是所谓的构造器注入!
# 4、Bean的作用域
- Singleton
当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域。要在XML中将bean定义成singleton,可以这样配置:
<bean id="ServiceImpl" class="com.singerw.service.ServiceImpl" scope="singleton">
@Test
public void MyTest(){
ApplicationContext context = newClassPathXmlApplicationContext("applicationContext.xml");
User user1 = (User) context.getBean("user");
User user2 = (User) context.getBean("user");
System.out.println(user1==user2);
}
2
3
4
5
6
7
8
true
- Prototype
当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。在XML中将bean定义成prototype,可以这样配置:
<bean id="account" class="com.foo.DefaultAccount" scope="prototype"/>
或者
<bean id="account" class="com.foo.DefaultAccount" singleton="false"/>
测试:
@Test
public void MyTest(){
ApplicationContext context = newClassPathXmlApplicationContext("applicationContext.xml");
User user1 = (User) context.getBean("user");
User user2 = (User) context.getBean("user");
System.out.println(user1==user2);
}
2
3
4
5
6
7
8
flase
- Request
当一个bean的作用域为Request,表示在一次HTTP请求中,一个bean定义对应一个实例;即每个HTTP请求都会有各自的bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:
<bean id="loginAction" class=com.singerw.LoginAction" scope="request"/>
针对每次HTTP请求,Spring容器会根据loginAction bean的定义创建一个全新的LoginAction bean实例,且该loginAction bean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例的内部状态,而其他请求中根据loginAction bean定义创建的实例,将不会看到这些特定于某个请求的状态变化。当处理请求结束,request作用域的bean实例将被销毁。
- Session
当一个bean的作用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:
<bean id="userPreferences" class="com.singerw.UserPreferences" scope="session"/>
针对某个HTTP Session,Spring容器会根据userPreferences bean定义创建一个全新的userPreferences bean实例,且该userPreferences bean仅在当前HTTP Session内有效。与request作用域一样,可以根据需要放心的更改所创建实例的内部状态,而别的HTTP Session中根据userPreferences创建的实例,将不会看到这些特定于某个HTTP Session的状态变化。当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。
# 六、Bean的自动装配
自动装配说明:
- 自动装配是使用spring满足bean依赖的一种方法
- spring会在应用上下文中为某个bean寻找其依赖的bean。
- 自动装配是Spring满足bean依赖的一种方式
- Spring会在上下文中自动寻找,并且自动给bean装配属性!
Spring中bean有三种装配机制,分别是:
在xml中显式配置;
在java中显式配置;
隐式的bean发现机制和自动装配。
这里我们主要讲第三种:
自动化的装配bean。Spring的自动装配需要从两个角度来实现,或者说是两个操作:
- 组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean;
- 自动装配(autowiring):spring自动满足bean之间的依赖,也就是我们说的IoC/DI;
组件扫描和自动装配组合发挥巨大威力,使得显示的配置降低到最少。
**推荐不使用自动装配xml配置 , 而使用注解 **
# 1、测试环境搭建
【实体类】Cat.java
Dog.java
User.java
public class Cat {
public void shout() {
System.out.println("miao~");
}
}
2
3
4
5
6
public class Dog {
public void shout() {
System.out.println("wang~");
}
}
2
3
4
5
6
public class User {
private Cat cat;
private Dog dog;
private String str;
}
2
3
4
5
6
【Spring配置文件】applicationContext.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">
<bean id="dog" class="com.singerw.pojo.Dog"/>
<bean id="cat" class="com.singerw.pojo.Cat"/>
<bean id="user" class="com.singerw.pojo.User">
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
<property name="str" value="singerw.com"/>
</bean>
</beans>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
【测试】UserTest.java
public class MyTest {
@Test
public void testMethodAutowire() {
ApplicationContext context = newClassPathXmlApplicationContext("beans.xml");
User user = (User) context.getBean("user");
user.getCat().shout();
user.getDog().shout();
}
}
2
3
4
5
6
7
8
9
10
wang~
miao~
结果正常输出,环境OK
# 2、byName自动装配
autowire byName (按名称自动装配)
由于在手动配置xml过程中,常常发生字母缺漏和大小写等错误,而无法对其进行检查,使得开发效率降低。
采用自动装配将避免这些错误,并且使配置简单化。
<bean id="user" class="com.singerw.pojo.User" autowire="byName">
<property name="str" value="singerw.com"/>
</bean>
2
3
wang~
miao~
再次测试,结果依旧成功输出!
小结:
当一个bean节点带有 autowire byName的属性时。
- 将查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。
- 去spring容器中寻找是否有此字符串名称id的对象。
- 如果有,就取出注入;如果没有,就报空指针异常。
# 3、byType自动装配
autowire byType (按类型自动装配)
使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。
<bean id="dog" class="com.singerw.pojo.Dog"/>
<bean id="cat" class="com.singerw.pojo.Cat"/>
<bean id="cat2" class="com.singerw.pojo.Cat"/>
<bean id="user" class="com.singerw.pojo.User" autowire="byType">
<property name="str" value="singerw.com"/>
</bean>
2
3
4
5
6
测试,报错:NoUniqueBeanDefinitionException
删掉cat2,将cat的bean名称改掉!测试!因为是按类型装配,所以并不会报异常,也不影响最后的结果。甚至将id属性去掉,也不影响结果。
<bean class="com.singerw.pojo.Dog"/>
<bean class="com.singerw.pojo.Cat"/>
<bean id="user" class="com.singerw.pojo.User" autowire="byType">
<property name="str" value="singerw.com"/>
</bean>
2
3
4
5
这就是按照类型自动装配!
# 七、Annotation注解实现自动装配
要使用注解前提须知:
导入约束:
xmlns:context="http://www.springframework.org/schema/context"
配置开启注解的支持:
<!--配置开启注解的支持-->
<context:annotation-config></context:annotation-config>
2
# 1、@Autowired
- 直接在属性上
- 也可以在Set方式上使用。
- 使用@Autowired 我们可以不用编写Set方法了,前提是这个自动装配的属性在IOC容器中存在,且符合名字ByName
如果显示的定义了@Autowired (required = false)说明这个对象可以为null,否则不容许为空!
public class UsersEntity {
@Autowired(required = false)
private String username;
}
2
3
4
如果@Autowired 自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired 】完成的时候,我们可以使用@Qualifier(value = "zx")去配合@Autowired的使用,指定一个唯一的bean对象注入!
public class UsersEntity {
@Qualifier(value = "singerw.com")
private String username;
}
2
3
4
# 2、@Resource注解
@Resource默认通过ByName的方式实现,如果找不到名字,则通过byType实现,如果两个都找不到的情况下,就会报错
public class UsersEntity {
@Resource(value = "singerw.com")
private String username;
}
2
3
4
# 3、@Autowired和@Resource的区别
- 都是用来自动装配的,都可以放在属性的字段上。
@Autowired
都过ByType
的方式实现,而且必须要去这个对象存在,不然就会空指针异常!【常用的】@Resource
默认通过ByName
的方式实现,如果找不到名字,则通过byType实现,如果两个都找不到的情况下,就会报错!
# 八、Spring使用注解开发
# 1、注解开发前提
注意点1:在Spring4后,要使用注解开发,必须要保证导入spring-aop
的jar包
<!--依赖注入包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.6</version>
</dependency>
2
3
4
5
6
注意点2:在Spring4后,要使用注解开发,必须要保证让注解生效,就需要开启注解的支持!
<!--注解的支持-->
<context:annotation-config/>
2
注意点3:设置组件的扫描路径,指定要扫描的包,这个包下的注解就会生效
<!--指定要扫描的包,这个包下的注解就会生效-->
<context:component-scan base-package="com.singerw"/>
2
# 2、常用注解说明
# 1、@Autowired
自动装配通过类型,名字。
如果@Autowired 自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired 】完成的时候,我们可以使用@Qualifier(value = "zx")去配合@Autowired的使用,指定一个唯一的bean对象注入!
# 2、 @Resource
自动装配通过名字。类型
# 3、@Nullable
字段标记了这个注解,说明这个字段可以为null
# 4、@Component
组件。放在类上,说明这个类被Spring管理了。就是bean注入!
相当于<bean id="address" class="com.singew.pojo.Address"></bean>
# 5、 @Value(value = "XXX")
给值注解,相当于<property name="name" value="张欣"></property>
也可以放在set方法上
# 6、@Component
的衍生注解
我们在web开发中,通常会采用MVC三层架构分层,所有每层的注解都不一样,但是作用都一样。
#
@Component
# dao:
@Repository(value = "usersDao")
# service:
@Service(value = "usersService")
# controller:
@Controller
# controller:
@RestController
这四个注解的功能都是一样的。都是代表将类注册到Spring容器中,装配Bean。
# 7、Scope(" singleton")
关于作用域的注解,可以设置Scope(" singleton")
单例模式和Scope(" prototype")
原型模式
# 3、 小结
# XML和注解开发的区别:
- XML更加万能,适合于任何场合,维护简单!
- 注解不是自己类使用不了,维护比较复杂!
# XML和注解的最佳实践:
一般来说,XML和注解的最佳实践,就是用XML来管理bean,用注解来完成属性的注入!
# 九、Spring-AOP
- 面向切面的程序设计框架
Aspect Oriented Programming(面向切面编程或面向方面编程)
AOP
:Aspect Oriented Programming
(面向切面编程或面向方面编程),是一种编程范式,提供从另一个角度来考虑程序结构从而完善面向对象编程(OOP)。AOP
为开发者提供一种进行横切关注点(比如日志关注点横切了支付关注点)分离并织入的机制,把横切关注点分离,然后通过某种技术织入到系统中,从而无耦合的完成了我们的功能。- 面向切面的编程和面向对象并不矛盾,是对面向对象的思维方式的有效补充。主要将程序中涉及公共问题集中解决,可以解决面向对象和过程化方法中不能很好解决的横切问题如:事务,安全,日志,异常处理等横切关注。
# 1、AOP概念
# 1.1 方面/切面(Aspect)
方面/切面(Aspect)是切面的具体实现,在连接点上执行的行为,通知提供了在AOP中需要在切入点所选择的连接点处进行扩展现有行为的手段。以目标方法为参照点,根据放置的地方不同,可分为前置通知(Before)、后置通知(AfterReturning)、异常通知(AfterThrowing)、最终通知(After)与环绕通知(Around)5种。在实际应用中通常是切面类中的一个方法,具体属于哪类通知,同样是在配置中指定的。做什么?
# 1.2 连接点(Joinpoint)
连接点(Joinpoint)表示需要在程序中插入横切关注点的扩展点,连接点可能是类初始化、方法执行、方法调用、字段调用或处理异常等等. 在哪里(很多可以连接的部分)做什么?
# 1.3 切入点(Pointcut)
选择一组相关连接点的模式,即可以认为连接点的集合,Spring支持perl5正则表达式和AspectJ切入点模式,Spring默认使用AspectJ语法,在AOP中表示为“在哪里(确定下来)做的集合”.
# 1.4 目标对象(Target Object)
需要被织入横切关注点的对象,即该对象是切入点选择的对象,需要被通知的对象,从而也可称为“被通知对象”;由于Spring AOP 通过代理模式实现,从而这个对象永远是被代理对象.对谁做这件事?
# 1.5 代理对象(AOP Proxy Object)
AOP框架使用代理模式创建的对象,从而实现在连接点处插入通知(即应用切面),就是通过代理来对目标对象应用切面。在Spring中,AOP代理可以用**JDK动态代理【返回接口实例】或CGLIB[可以是类实例]**代理实现,而通过拦截器模型应用切面。
# 1.6 织入(Weaving)
将切面应用到目标对象从而创建一个新的代理对象的过程。这个过程可以发生在编译期、类装载期及运行期,当然不同的发生点有着不同的前提条件。譬如发生在编译期的话,就要求有一个支持这种AOP实现的特殊编译器;发生在类装载期,就要求有一个支持AOP实现的特殊类装载器;只有发生在运行期,则可直接通过Java语言的反射机制与动态代理机制来动态实现。
# 1.7 通知类型
- 前置通知(Before Advice):在切入点选择的连接点处的方法之前执行的通知,该通知不影响正常程序执行流程(除非该通知抛出异常,该异常将中断当前方法链的执行而返回)。
- 后置通知(After Advice): 在切入点选择的连接点处的方法之后执行的通知,包括如下类型的后置通知:
- 后置返回通知(After returning Advice):在切入点选择的连接点处的方法正常执行完毕时执行的通知,必须是连接点处的方法没抛出任何异常正常返回时才调用后置通知。
- 后置异常通知(After throwing Advice): 在切入点选择的连接点处的方法抛出异常返回时执行的通知,必须是连接点处的方法抛出任何异常返回时才调用异常通知。
- 后置最终通知(After finally Advice): 在切入点选择的连接点处的方法返回时执行的通知,不管抛没抛出异常都执行,类似于Java中的finally块。
- 环绕通知(Around Advices):环绕着在切入点选择的连接点处的方法所执行的通知,环绕通知可以在方法调用之前和之后自定义任何行为,并且可以决定是否执行连接点处的方法、替换返回值、抛出异常等.
# 2、基于XML的Spring AOP配置
# 3、代理模式—静态代理
代理模式是SpringAOP的底层!【SpringAOP和SpringMVC面试必问】
优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。 缺点:
- 代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
- 如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
# 4、代理模式—动态代理
代理模式是SpringAOP的底层!【SpringAOP和SpringMVC面试必问】
Spring默认使用动态代理,要使用静态代理需要在XML中配置。
# 十三、Spring 提供了哪些IOC容器配置方式?
# 1、基于 applicationContext.xml
实现配置
bean 所需的依赖项和服务在XML格式的配置文件中指定。这些配置文件通常包含许多
bean定义和特定于应用程序的配置选项。它们通常以
bean` 标签开头。
【例如】:
<bean id="address" class="com.singew.pojo.Address"></bean>
<bean id="student" class="com.singew.pojo.Student">
<!--第一种,普通值的注入,value-->
<property name="name" value="张欣"></property>
<!--第二种,Bean注入,ref-->
<property name="address" ref="address"/>
<!--第三种,数组的注入-->
<property name="books">
<array>
<value>红楼梦</value>
<value>西游记</value>
<value>三国演义</value>
<value>水浒传</value>
</array>
</property>
<!--第四种,List注入-->
<property name="hobbys">
<list>
<value>听歌</value>
<value>掉代码</value>
<value>看电影</value>
</list>
</property>
<!--第五种,Map注入-->
<property name="card">
<map>
<entry key="key" value="value"/>
<entry key="身份证" value="5464556454645651415"/>
<entry key="银行卡" value="8795461525678954645459845415"/>
</map>
</property>
<!--第六种,Set注入-->
<property name="games">
<set>
<value>LOL英雄联盟</value>
</set>
</property>
<!--第七种,props空值注入-->
<property name="wife" value=""/>
<property name="info">
<props>
<prop key="学号">20171649</prop>
<prop key="性别">男</prop>
<prop key="姓名">小明</prop>
</props>
</property>
</bean>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# 2、基于注解实现配置
可以通过在相关的类,方法或字段声明上使用注解,将 bean
配置为组件类本身,而不是使用XML
来描述 bean
装配。默认情况下,Spring
容器中未打开注解装配。因此,需要在使用它之前在Spring
配置文件中启用它。
【例如】:
<!--注解的支持-->
<context:annotation-config></context:annotation-config>
2
@Repository
public class BlogDaoImpl implements BlogDao {
@Override
public PageData getBlogList(int page, int pageSize) {
String sql = "SELECT g_article.* FROM g_article WHERE acticle_status = 1";
return DBUtil.exQueryByPage(sql, BlogEntity.class, page, pageSize);
}
}
2
3
4
5
6
7
8
9
# 3、基于Java API/JavaConfig
实现配置
完全使用
Java
的方式配置Spring
,年轻人喜欢这么玩,上了年龄的程序员不想瞎搞。但以后看到Spring开源项目,这么没有bean
,怎么没有xml
?别说这不是Spring
的,丢人了。
Spring
的 Java
配置是通过使用 @Bean
和 @Configuration
来实现。
@Bean
注解扮演与<bean />
元素相同的角色。@Configuration
类允许通过简单地调用同一个类中的其他@Bean
方法来定义bean
间依赖关系。
【例如】:
UserEntity:
@Component
public class UserEntity {
private String name;
public String getName() {
return name;
}
@Value("张欣")
public void setName(String name) {
this.name = name;
}
public UserEntity() {
}
public UserEntity(String name) {
this.name = name;
}
@Override
public String toString() {
return "UserEntity{" +
"name='" + name + '\'' +
'}';
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
SingerwConfig:
@Configuration
public class SingerwConfig {
@Bean
public UserEntity getUser(){
return new UserEntity();
}
}
2
3
4
5
6
7
8
TestUser:
public class TestUser {
@Test
public void getUserTest() {
ApplicationContext context = new AnnotationConfigApplicationContext(SingerwConfig.class);
UserEntity getUser = context.getBean("getUser", UserEntity.class);
System.out.println(getUser.getName());
}
}
2
3
4
5
6
7
8