Spring学习笔记(一)

Spring概述

  • 目的:

    • 解决企业应用开发的复杂性
  • 范围:任何java的应用

  • 重点:

    • **IoC(控制反转)**:促进了松耦合,对象初始化时不等对象请求就主动将依赖传递
    • **AOP(面向切面编程)**:允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发
  • 优点:

    • Spring是一个开源容器

    • 从大小与开销两方面而言Spring都是轻量的框架

    • 所有Spring的这些特征使你能够编写更干净、更可管理、并且更易于测试的代码。

  • 缺点:

    • 配置太过于繁琐!(SpringBoot会很好的解决这个问题)
  • 总结:反正无敌!!就是学:smile:!

Spring的7大模块

image-20210217201434565

1.Spring Code

这是Spring框架最基础的部分,它提供了依赖注入(DependencyInjection)特征来实现容器对Bean的管理。这里最基本的概念是BeanFactory,它是任何Spring应用的核心。

2.Sping AOP

Spring在它的AOP模块中提供了对面向切面编程的丰富支持。这个模块是在Spring应用中实现切面编程的基础。它的目标是通过定义一组共同的接口和组件来促进AOP的使用以及不同的AOP实现之间的互用性。

3.Spring Context

核心模块的BeanFactory使Spring成为一个容器,而上下文模块使它成为一个框架。另外,这个模块提供了许多企业服务,例如电子邮件、JNDI访问、EJB集成、远程以及时序调度(scheduling)服务。也包括了对模版框架例如Velocity和FreeMarker集成的支持。

4.Spring DAO

使用JDBC经常导致大量的重复代码,取得连接、创建语句、处理结果集,然后关闭连接。Spring的JDBC和DAO模块抽取了这些重复代码,因此你可以保持你的数据库访问代码干净简洁,并且可以防止因关闭数据库资源失败而引起的问题。另外,这个模块还使用了Spring的AOP模块为Spring应用中的对象提供了事务管理服务。

5.Spring ORM

对那些更喜欢使用对象/关系映射工具而不是直接使用JDBC的人,Spring提供了ORM模块。Spring并不试图实现它自己的ORM解决方案,而是为几种流行的ORM框架提供了集成方案

6.Spring Web

Web上下文模块建立于应用上下文模块之上,提供了一个适合于Web应用的上下文。

7.Spring MVC

Spring为构建Web应用提供了一个功能全面的MVC框架,Spring的MVC框架使用IoC对控制逻辑和业务对象提供了完全的分离。

Spring IOC

1.什么是IOC

控制反转模式(也称作依赖性注入)的基本概念是:不创建对象,但是描述创建它们的方式。在代码中不直接与对象和服务连接,但在配置文件中描述哪一个组件需要哪一项服务。容器 (在 Spring 框架中是 IOC 容器) 负责将这些联系在一起。在典型的 IOC 场景中,容器创建了所有对象,并设置必要的属性将它们连接在一起,决定什么时间调用方法。

1.1个人理解:

就类似于工厂模式里面的工厂,对程序进行了一定的解耦

image-20210218215731762 !image-20210218215750870

这样降低了app对资源的依赖,直接对工厂要。这样app对资源的控制权进行放弃转而为让第三方(框架)控制资源所以IOC被称为控制反转

1.2作用:

削减计算机程序的耦合(解除我们代码中的依赖关系)

简而言之:对象由spring 来创建,管理,装配!就只需要改成xml来实现不同操作!

Spring IoC容器的使用

​ org.springframework.beans和org.springframework.context是Spring框架中IoC容器的基础,BeanFactory接口提供一种高级的配置机制能够管理任何类型的对象。

1.Spring 的主要maven依赖

1
2
3
4
5
6
7
8
9
10
11
12
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.3</version>
</dependency>

2.初步的使用

2.1创建bean.xml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
<?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:使用时的唯一标识-->
<!-- class:通过反射创建对象时需要用到的全限定类名-->
<bean id="accountService" class="com.wht.service.impl.AccountServiceImpl"></bean>
<bean id="accountDao" class="com.wht.dao.impl.AccountDaoImpl"></bean>

</beans>

2.2获取对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Client {
/**
* 获取spring的Ioc核心容器,并根据id获取对象
* ApplicationContext的三个常用实现类
* ClassPathXmlApplicationContext: 它只能加载类路径下的配置文件
* FileSystemXmlApplicationContext: 它可以加载任意路径下的配置文件(必须有访问权限,并不常有)
* AnnotationConfigApplicationContext: 它用于读取注解创建容器
* 核心容器的两个接口引发出的问题
* ApplicationContext: 多数才用此接口,因为spring会自动根据配置来选择合适的接口
* 它在构建核心容器时,创建对象的策略是立即加载的方式,就是当容器读了配置文件后对象已经被创建。(单例对象适用)
* BeanFactory:
* 它在构建核心容器时,创建对象的策略是延迟加载的方式,就是当什么时候根据id获取时才创建。(反之多例适用)
* @param args
*/
public static void main(String[] args) {
//获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//根据id获取对象
//方式1 需要自己强转
AccountService as = (AccountService) ac.getBean("accountService");
//方式2
AccountDao adao = ac.getBean("accountDao", AccountDao.class);
}
}

3.Spring对bean的管理细节

3.1 创建bean的三种方式

1
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
<!--    spring创建bean的三种方式
方式一:使用默认构造函数创建。
在spring配置文件中使用bean标签,配置id和class之后,且没有其他属性标签时。采用的此方法,若没有无参构造则无法创建。
方式二:使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)
方式三:使用工厂中的静态方法创建对象

-->
<!-- 方式一:
id:使用时的唯一标识
class:通过反射创建对象时需要用到的全限定类名
-->
<bean id="accountService" class="com.wht.service.impl.AccountServiceImpl"></bean>
<bean id="accountDao" class="com.wht.dao.impl.AccountDaoImpl"></bean>
<!-- 方式二:
id:使用时的唯一标识
factory-bean:工厂类使用的id
factory-method:工厂创建该对象所使用的方法
-->
<bean id="instanceFactory" class="com.wht.Factory.InstanceFactory"></bean>
<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>

<!-- 方式三:
id:使用时的唯一标识
class:该工厂的全限定类名
factory-method:工厂创建该对象所使用的方法
-->
<bean id="accountService" class="com.wht.Factory.StaticFactory" factory-method="getAccountService"></bean>

3.2 bean对象的作用范围

1
2
3
4
5
6
7
8
9
10
<!--       
bean的作用范围调整
bean标签的scope属性:
取值:
singleton:单例(默认值)
prototype:多例
request:作用于web应用的请求范围
session:作用于web应用的会话范围
global-session:作用于集群环境的会划范围,当不是集群范围时他就是session
-->

3.3 bean对象的生命周期

1
2
3
4
5
6
7
8
9
10
11
<!--    
bean对象的生命周期
单例对象
出生: 当容器创建时对象出生
活着:只要容器还在,对象就存活
死亡:容器销毁,对象死亡
多例对象
出生:当我们使用对象时才创建
活着:使用过程中一直活着
死亡:当对象长时间不用,且没有别的对象引用时,由java的垃圾回收器回收
-->

4.依赖注入

依赖关系的管理:
以后都交给spring来维护在当前类需要用到其他类的对象,由spring为我们提供,只需要配置文件中说明.
依赖关系的维护就叫做依赖注入。

4.1能注入的类型

  1. 基本类型和String
  2. 其他bean类型(在配置文件中或者注解配置过得bean)
  3. 复杂类型/集合类型

4.2 注入的方式

4.2.1使用构造函数注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!--
构造函数注入:
使用标签:constructor-arg
标签出现的位置:bean标签的内部
标签中的属性
type:用于指定注入的数据的数据类型,
index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值,从0开始
name:用于指定给构造函数中指定名称的参数赋值 (常用)
=========以上用于指定构造函数中哪个赋值============
value:用于提供基本类型和String类型的数据
ref:用于指定其他的bean类型数据
优点:
在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功。
缺点:
改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供。
-->
<bean id="accountService" class="com.wht.service.impl.AccountServiceImpl">
<constructor-arg name="name" value="test"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>

</bean>
<bean id="now" class="java.util.Date"></bean>

4.2.2 使用set方法注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!--
set方法注入: 更常用
使用标签:property
标签出现的位置:bean标签的内部
标签中的属性
name:用于指定注入时set方法名称
value:用于提供基本类型和String类型的数据
ref:用于指定其他的bean类型数据
优点:
创建对象时没有明确的限制,可以直接使用默认构造函数
缺点:
如果某个成员必须有值,则set方法没法保证一定注入
-->
<bean id="accountService2" class="com.wht.service.impl.AccountServiceImpl2">
<property name="name" value="test"></property>
<property name="age" value="20"></property>
<property name="birthday" ref="now"></property>
</bean>

4.2.3 复杂类型的注入

1
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
<!--
复杂数据的注入:
用于给list结构集合注入的标签:
list array set
用于Map结构集合注入的标签:
map props
结构相同,标签可以互换
所以常用 list map
-->
<bean id="accountService3" class="com.wht.service.impl.AccountServiceImpl3">
<property name="myStrs">
<array>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</array>
</property>
<property name="myList">
<list>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</list>
</property>
<property name="mySet">
<set>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</set>
</property>
<property name="myMap">
<map>
<entry key="TestA" value="aaa"></entry>
<entry key="TestB">
<value>BBB</value>
</entry>
</map>
</property>
<property name="myProps">
<props>
<prop key="testC">ccc</prop>
<prop key="testD">ddd</prop>
</props>
</property>
</bean>

4.2.4 使用注解注入

1
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/**
*曾经的xml配置
* <bean id="accountService2" class="com.wht.service.impl.AccountServiceImpl"
* scope="" init-method="" destroy-method="" >
* <property name="" value="" | ref = ""></property>
*
*</bean>
*
* 1.用于创建对象的注解
* @component
* 作用:用于把当前类存入spring容器中
* 属性:
* value:用于指定bean的id。默认类名首字母小写
* @Controller
* 一般用于表现层
* @Service
* 一般用于业务层
* @Repository
* 一般用于持久层
* 以上三个注解他们的作用和属性与Component是一模一样。
* 他们三个事spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰
* 2.用于注入数据的注解
* @Autowired:
* 作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配就可以注入成功。
* 如果ioc容器没有任何bean对象相匹配就注入失败
* 如果ioc容器中有多个类型匹配时
* 会以对象名称作为id来寻找bean对象,若没有该id就注入失败,反之就注入成功
* 出现位置:
* 可以是变量上,也可以方法上。
* 细节:
* 在使用注解注入时,set方法就不是必须的了。
* @Qualifier:
* 作用:在按照类型注入的基础上再按照名称注入,它在给类成员注入时不能单独使用。但是在给方法参数注入时可以
* 属性:
* value:用于注定注入bean的id
* @Resource:
* 作用:直接按照bean的id注入。它可以独立使用
* 属性:
* name:用于指定bean的id
* 以上三个注入只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。
* 另外集合类型的注入只能通过XML来实现
* @Value
* 作用:用于注入基本类型和String类型的数据
* 属性:
* value:用于指定数据的值,可以使用SpEL表达式
* SpEL的写法:${表达式}
* 3.用于改变作用范围的
* @Scope:
* 作用:用于指定bean的作用范围
* 属性:
* value:指定范围取值。常用:singleton prototype
* 4.和生命周期相关的
* @PreDestroy
* 作用:用于指定销毁方法
* @PostConstruct
* 作用:用于指定初始化方法
*/

@Component("accountService")
public class AccountServiceImpl implements AccountService {
// @Autowired
// @Qualifier("accountDao")
@Resource(name="accountDao")
private AccountDao accountDao =null;

public void saveAccount() {

}
}

使用java类作为配置类

1
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
/**
* 这是一个配置类,它的作用和bean.xml是一样的
* Spring中的新注解
* @Configuration
* 作用:指定当前类是一个配置类
* 细节:当配置作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写
* @ComponentScan
* 作用:用于通过注解指定spring在创建容器时要扫描的包,和base-package一样
* @Bean
* 作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器中
* 属性:
* name:用于指定bean的id。当不写时,默认值是当前方法名称
* @Import
* 作用:用于导入其他的配置类
* 属性:
* value:用于指定其他配置类的字节码
* 当我们使用import的注解之后,有import注解的类就是主配置类
* @PropertySource
* 作用:用于指定properties文件的位置
* 属性:
* value:指定文件的名称和路径
* 关键字:classpath:表示类路劲下
* @EnableTransactionManagement
* 作用:用于指定开始事务支持
*
*/

@Configuration
@ComponentScan("com.wht")
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbcConfig.properties")
public class SpringConfig {

}

5.spring整合junit

5.1导入spring整合junit的依赖

1
2
3
4
5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.3</version>
</dependency>

5.2 相关注解的使用

1
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
/**
*
* spring集合junit的配置
* 1.导入spring整合junit的依赖
* 2.使用junit提供的一个注解把原有的main方法替换了,替换成spring提供的
* @Runwith
* 3.告知spring的运行器,spring和ioc创建是基于xml还是注解的,并且说明位置
* @ContextConfiguration
* locations:指定xml文件的位置,加上classpath关键字,表示在类路径下
* classes:指定注解类所在的位置
* 当我们使用spring 5.x版本,需要junit的版本必须是4.12及以上
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {
@Autowired
private AccountService as = null;

// @Before
// public void init(){
// ac = new AnnotationConfigApplicationContext(SpringConfig.class);
// as = ac.getBean("accountService", AccountService.class);
// }

@Test
public void testFindAll(){
List<Account> allAccount = as.findAllAccount();
System.out.println(allAccount);
}
@Test
public void testFindOne(){
Account accountById = as.findAccountById(1);
System.out.println(accountById);
}
@Test
public void testSave(){
as.saveAccount(new Account(null,"李倩",10000));
}@Test
public void testUpdate(){
as.updateAccount(new Account(1,"666",10000));
}@Test
public void testDelete() {
as.deleteAccount(2);
}
}