0%

Spring学习笔记

Spring

1,简介

Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。

起源

  • 2002年首次推出了Spring的雏形 interface21
  • 2004年,Spring基于interface21框架为基础,不断地丰富其内涵,于3月24号推出了1.0正式版本
  • Spring缔造者Rod Johnson,毕业于悉尼大学计算机系但是他还有另一个身份,音乐学的博士

SSH: Struck2+Spring+Hibernate

SSM: SpringMVC+Spring+Mybatis

注释:

  • Struck2Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。
  • HibernateHibernate是一个开放源代码的对象关系映射框架,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。(但是自由性不高,无法对SQL定制)

下载

GitHub:https://github.com/spring-projects/spring-framework/releases/tag/v5.3.5

Maven:

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

<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc的一个驱动 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.5</version>
</dependency>

优点

  • Spring是一个免费的开源的框架(容器)
  • Spring是一个轻量级的,非入侵式(加入框架对之前的代码不会有影响)的框架
  • Spring是一个控制反转(IOC),面向切面编程(AOP)的框架
  • 支持事务的处理,对其他框架的整合

2,组成

img

1,核心容器(Spring Core)

核心容器提供Spring框架的基本功能。Spring以bean的方式组织和管理Java应用中的各个组件及其关系。Spring使用BeanFactory来产生和管理Bean,它是工厂模式的实现。BeanFactory使用控制反转(IoC)模式将应用的配置和依赖性规范与实际的应用程序代码分开。

2,应用上下文(Spring Context)

Spring上下文是一个配置文件,向Spring框架提供上下文信息。Spring上下文包括企业服务,如JNDI、EJB、电子邮件、国际化、校验和调度功能。

3,Spring面向切面编程(Spring AOP)

通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring框架中。所以,可以很容易地使 Spring框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。

4,JDBC和DAO模块(Spring DAO)

JDBC、DAO的抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理,和不同数据库供应商所抛出的错误信息。异常层次结构简化了错误处理,并且极大的降低了需要编写的代码数量,比如打开和关闭链接。

5,对象实体映射(Spring ORM)

Spring框架插入了若干个ORM框架,从而提供了ORM对象的关系工具,其中包括了Hibernate、JDO和 IBatis SQL Map等,所有这些都遵从Spring的通用事物和DAO异常层次结构。

6,Web模块(Spring Web)

Web上下文模块建立在应用程序上下文模块之上,为基于web的应用程序提供了上下文。所以Spring框架支持与Struts集成,web模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。

7,MVC模块(Spring Web MVC)

MVC框架是一个全功能的构建Web应用程序的MVC实现。通过策略接口,MVC框架变成为高度可配置的。MVC容纳了大量视图技术,其中包括JSP、POI等,模型来有JavaBean来构成,存放于m当中,而视图是一个接口,负责实现模型,控制器表示逻辑代码,由c的事情。Spring框架的功能可以用在任何J2EE服务器当中,大多数功能也适用于不受管理的环境。Spring的核心要点就是支持不绑定到特定J2EE服务的可重用业务和数据的访问的对象,毫无疑问这样的对象可以在不同的J2EE环境,独立应用程序和测试环境之间重用。

3,IOC理论推导

Web

在之前学习JavaWeb时,如果我们想要调用SQL需要Servlice层调用Dao层

代码如下:

Dao

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
package cn.xpp011.dao.Role;

import cn.xpp011.dao.DBCPTool;
import cn.xpp011.pojo.Role;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class RoleDaoImpl implements RoleDao{
//获取角色列表
@Override
public List<Role> getrolelist() throws SQLException {
Connection con= DBCPTool.getConnection();
PreparedStatement ps=null;
ResultSet res=null;

List<Role> list=new ArrayList<>();
String sql="select * from smbms_role";
Object[] objects={};
res = DBCPTool.execute(con, ps, res, objects, sql);
while (res.next()){
Role role=new Role();
role.setId(res.getInt("id"));
role.setRoleCode(res.getString("roleCode"));
role.setRoleName(res.getString("roleName"));
list.add(role);
}

DBCPTool.close(con,ps,res);

return list;
}
}

Servlice

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
package cn.xpp011.servlice.Role;

import cn.xpp011.dao.DBCPTool;
import cn.xpp011.dao.Role.RoleDaoImpl;
import cn.xpp011.pojo.Role;
import org.junit.Test;

import java.sql.SQLException;
import java.util.List;

public class RoleServliceImpl implements RoleServlice{
private RoleDaoImpl roleDao=null;

public RoleServliceImpl() {
this.roleDao=new RoleDaoImpl();
}

//获取角色列表
@Override
public List<Role> getrolelist() {
List<Role> roleList=null;
try {
roleList = roleDao.getrolelist();
} catch (SQLException throwables) {
throwables.printStackTrace();
}

return roleList;
}

@Test
public void testgetrolelist(){
RoleServliceImpl roleServlice=new RoleServliceImpl();
List<Role> roleList = roleServlice.getrolelist();
for (Role role : roleList) {
System.out.println(role.getRoleName());
}
}
}

此时如果我们想要添加其他类型的业务sql,需要在Dao层加入新的类,调用时需要修改Servlice层来创建对应的Dao层类。

这样我们的代码掌握了控制权,如果添加新的Dao层,就需要修改Servlice层的代码,这样显然是不好的,如果在业务代码量十分大的时候,修改源代码的成本就十分昂贵了。

那么我们的目标是,当我们添加Dao层时,无需修改其他代码,让代码被动的接受需求,就能调用对应的dao层代码,这样我们就只需要专注于横向的添加dao层代码,其他地方我们无需管理修改,那么这样的代码是健康的,这也就是控制反转(IOC),将代码的控制权,从程序员(手动创建dao对应的Servlice),转交给客户(只需要被动的接受参数,就可以完成对应的dao)。

IOC

代码如下:

Dao

image-20210404145940670

Servlice (Set注入)

image-20210404150130468

最后测试时:我们的客户也就是MyTest,只需去传入参数,就可以执行对应的Dao

image-20210404150158529

总结:

两种情况之间的区别是巨大的,很多地方都不一样了,仔细思考,之前的所有东西但是由程序去创建的,需要程序员自行创建控制对象,而IOC这是把主动权交给了调用者,我们不再去管对象是怎么创建,怎么控制的了,它只负责一个接口。

这种思想,从本质上解决了问题,我们程序员不在去管具体对象的创建,而是更多的专注业务层的实现,耦合性大大降低,这也就是IOC的原型!

注:什么是耦合性:https://www.zhihu.com/question/21019721

image-20210404152621192

4,IOC本质

oc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:

●谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。

●为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。

用图例说明一下,传统程序设计如图,都是主动去创建相关对象然后再组合起来:

img

​ 当有了IoC/DI的容器后,在客户端类中不再主动去创建这些对象了,如图所示:

img

IoC能做什么

IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。

其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。

IoC很好的体现了面向对象设计法则之一 —— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。

5,HelloSpring

hello类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Hello {
private String str;

public String getStr() {
return str;
}

public void setStr(String str) {
this.str = str;
}

@Override
public String toString() {
return "Hello{" +
"str='" + str + '\'' +
'}';
}
}

容器配置文件(Beans.xml)

1
2
3
4
5
6
7
8
9
<?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="hello" class="Hello">
<property name="str" value="Spring"/>
</bean>
</beans>

测试:实例化容器

提供给ApplicationContext构造函数的一个或多个位置路径是资源字符串,这些资源字符串使容器可以从各种外部资源(例如本地文件系统,Java等)中加载配置元数据CLASSPATH

1
2
3
4
5
6
7
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.toString());
}
}

注意:Hello类中必须有set方法,容器其实终究还是以new创建对象的形式,需要通过set方法修改值

6,IOC创建对象的原理

  1. IOC容器创建对象的方式,是通过类的无参构造器创建出来的,默认!

  2. 那当我们书写了有参构造时,Java默认就不再提供无参构造时,那么IOC容器怎么实现有参构造器创建对象呢标签可以帮助我们实现,name为类属性的名字

    1
    2
    3
    4
    <bean id="hello" class="Hello">
    <constructor-arg name="str" value="张三"/>
    <property name="str" value="Spring"/>
    </bean>

总结:

  • 当我们实例化IOC容器时ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");不管我们是否调用getBean方法,容器都已经将它管理的类全部示例化
  • value和ref的区别 :value适用于基本类型,而当属性为类时,届时要使用ref

7,Spring配置

别名

name: bean的id

alias :别名名称

1
<alias name="mytest" alias="my"/>

Bean配置

id:bean的唯一标识符

class:指定对象的全限命名

name:别名 可以去多个

1
<bean id="userdao" class="cn.xpp.dao.UserDaoImpl" name="dao,ud"/>

import

import常见于团队开发,主要应用是将其他人开发的IOC配置文件整合到applicationContext.xml文件

1
2
<import resource="beans.xml"/>
<import resource="beans2.xml"/>

8,依赖注入

基于构造函数注入

标签

基本类型使用value属性

引用类型使用ref属性

1
2
3
4
<bean id="hello" class="Hello">
<constructor-arg name="str" value="张三"/>
<property name="str" value="Spring"/>
</bean>

基于Set注入

image-20210404223054540

属性列表

bean | ref | idref | list | set | map | props | value | null

基本类型注入

1
<property name="name" value="张三"/>

引用类型注入

1
<property name="address" ref="addent"/>

数组注入

1
2
3
4
5
6
7
<property name="nums" >
<array>
<value>1</value>
<value>2</value>
<value>3</value>
</array>
</property>

集合注入

1
2
3
4
5
6
7
8
<property name="result" >
<list>
<value>4</value>
<value>5</value>
<value>6</value>
<value>7</value>
</list>
</property>

Map注入

1
2
3
4
5
<property name="map" >
<map>
<entry key="姓名" value-ref="addent"/>
</map>
</property>

Set注入

1
2
3
4
5
6
7
<property name="set" >
<set>
<value>LOL</value>
<value>盗贼之海</value>
<value>盗贼之海</value>
</set>
</property>

注入空值

1
2
3
<property name="wife" >
<null/>
</property>

Properties配置类注入

注意:xml文件中<&是被严格禁止的需要通过<![CDATA[内容]]>来转义

1
2
3
4
5
6
7
8
<property name="properties" >
<props>
<prop key="driver">com.mysql.cj.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/smbms?serverTimezone=UTC<![CDATA[&]]>characterEncoding=utf8<![CDATA[&]]>useSSl=true</prop>
<prop key="username">root</prop>
<prop key="password">root</prop>
</props>
</property>

扩展方式注入

c命名空间注入

简化版的标签

首先在IOC容器配置文件中加入

xmlns:c="http://www.springframework.org/schema/c"

1
2
3
4
5
6
7
8
<?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:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="user2" class="cn.xpp.pojo.User" c:name="沈仁雨" c:age="20"/>
</beans>

p命名空间注入

简化版的标签

首先在IOC容器配置文件中加入

xmlns:p="http://www.springframework.org/schema/p"

1
2
3
4
5
6
7
8
9
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="user" class="cn.xpp.pojo.User" p:name="小癖癖" p:age="20"/>

</beans>

注意:

  • 使用c/p命名空间时必须在配置文件中导入xml约束
  • p命名空间无法对复杂类型注入如数组,集合等

Bean作用域

image-20210405122640743

单例模式(Spring默认)

scope标签值设置为singleton(单例模式)

单例模式即每一次从IOC容器拿去的对象都是同一个,单例模式避免了一个对象频繁的被创建和销毁

1
<bean id="user" class="cn.xpp.pojo.User" p:name="小癖癖" p:age="20" scope="singleton"/>

image-20210405151945252

原型模式

scope标签值设置为prototype(原型模式)

原型模式和单例模式相反,每一次从IOC容器拿去对象时,都是新new出来的,单例模式适用于单线程,多线程的单例,会有脏读,幻读,不可重复读等情况,所以原型模式适用于多线程

1
<bean id="user2" class="cn.xpp.pojo.User" c:name="沈仁雨" c:age="20" scope="prototype"/>

image-20210405152003248

9,Bean自动装配

byName自动装配

根据其他Bean的id名来自动装配

注意:byName是通过set注入方法后面的名字来进行自动查找上下文的。

也就是public void setDag(Dag dag) {} setDag的Dag,然后将它的首字母转化小写为dag,然后根据这个dag这个名字在上下文中查找

1
2
3
4
5
6
<bean id="dag" class="cn.xpp.pojo.Dag"/>
<bean id="cat" class="cn.xpp.pojo.Cat"/>

<bean id="people" class="cn.xpp.pojo.People" autowire="byName">
<property name="name" value="沈仁雨"/>
</bean>

缺点:

  • byName时必须保证其他Bean的id,一定是set注入后面名字的首字母小写

byType自动装配

根据类的类型来自动装配

由于是根据类型自动装配,所以其他Bean的id可以省略不写

1
2
3
4
5
6
<bean class="cn.xpp.pojo.Dag"/>
<bean class="cn.xpp.pojo.Cat"/>

<bean id="people" class="cn.xpp.pojo.People" autowire="byType">
<property name="name" value="沈仁雨"/>
</bean>

缺点:

  • byType时,必须保证其他bean的class类型唯一。

注解自动装配【重点】

注释在配置Spring方面比XML更好吗?

基于注释的配置的引入提出了一个问题,即这种方法是否比XML“更好”。简短的答案是“取决于情况”。长的答案是每种方法都有其优缺点,通常,由开发人员决定哪种策略更适合他们。由于定义方式的不同,注释在声明中提供了很多上下文,从而使配置更短,更简洁。但是,XML擅长连接组件而不接触其源代码或重新编译它们。一些开发人员更喜欢将布线放置在靠近源的位置,而另一些开发人员则认为带注释的类不再是POJO,而且,配置变得分散并且难以控制。

如果我们需要使用Spring的注解装配,那么我们要先在IOC配置文件中添加注解支持

注意<context:annotation-config/>标签一定要加上

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
">
<context:annotation-config/>
</beans>

注解:

  • @Autowired注解
    • @Autowired可以加在set方法和属性上
    • @Autowired自动装配的步骤是先使用ByType寻找属性对应的类型,如果存在多个相同的类型那么会使用ByName去寻找属性字段名对应的Bean的id
    • @Autowired(required = false):@Autowired注解有一个属性required 默认值为true,表示该属性不能为空,那么required = false时表示该属性可以为空,与注解@Nullable相同
  • @Qualifier(“XXX”)注解
    • @Qualifier()注解可以和@Autowired注解搭配使用,该注解可以指定一个Bean的id
  • @Resource注解
    • @Resource注解是Java自带的注解和@Autowired注解类似,不过它实现去找ByName,再去找ByType

案例:

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
public class People {
@Autowired(required = false)
private Dag dag;
@Resource
private Cat cat;

private String name;

public Dag getDag() {
return dag;
}

public void setDag(Dag dag) {
this.dag = dag;
}

public Cat getCat() {
return cat;
}

public void setCat(Cat cat) {
this.cat = cat;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
public String toString() {
return "People{" +
"dag=" + dag +
", cat=" + cat +
", name='" + name + '\'' +
'}';
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
">
<context:annotation-config/>
<bean id="dag" class="cn.xpp.pojo.Dag">
<property name="v" value="汪汪汪"/>
</bean>
<bean id="cat" class="cn.xpp.pojo.Cat">
<property name="v" value="喵喵喵"/>
</bean>

<bean id="people" class="cn.xpp.pojo.People" >
<property name="name" value="沈仁雨"/>
</bean>
</beans>

10,Spring注解开发

注释在配置Spring方面比XML更好吗?

基于注释的配置的引入提出了一个问题,即这种方法是否比XML“更好”。简短的答案是“取决于情况”。长的答案是每种方法都有其优缺点,通常,由开发人员决定哪种策略更适合他们。由于定义方式的不同,注释在声明中提供了很多上下文,从而使配置更短,更简洁。但是,XML擅长连接组件而不接触其源代码或重新编译它们。一些开发人员更喜欢将布线放置在靠近源的位置,而另一些开发人员则认为带注释的类不再是POJO,而且,配置变得分散并且难以控制。

bean

如何使用注解来代替bean:@Component注解

使用@Component注解的一个前提是在IOC容器配置文件中加入扫描包的标签<context:component-scan base-package=""/>,该标签会将该包下的@Component标签全部注册到配置文件中

1
<context:component-scan base-package="cn.xpp"/>

该类的名字是类名的小写

1
2
3
4
5
@Component//相当于是在IOC容器配置文件中<bean id="people" class="cn.xpp.pojo.People"/>
public class Cat {
private String v;

}

作用域

如何使用注解来代替作用域(Scope):@Scope(value=“XXX”)注解

使用@Scope可以声明该类的作用域,当value值为singleton,则为单例模式,当value值为prototype,则为原型模式。

1
2
3
4
5
6
@Component//相当于是在IOC容器配置文件中<bean id="people" class="cn.xpp.pojo.People"/>
@Scope(value = "singleton")
public class Cat {
private String v;

}

属性注入

如何使用注解来代替属性注入:@Value(“”)注解

该注解适用于基本类型的注入,引用类型需要使用@Autowired自动装配,或者IOC配置文件中注入

1
2
3
4
5
6
7
@Component//相当于是在IOC容器配置文件中<bean id="people" class="cn.xpp.pojo.People"/>
@Scope(value = "singleton")
public class Cat {
@Value("喵喵喵喵")
private String v;

}

衍生注解

@Component有几个衍生注解,在我们的MVC三层架构中不会全部使用@Component注解,那样难以区分

  • dao层【@Repository】
  • service层【@Service】
  • controller层【@Controller】

这四个注解的作用都一样的,都是代表将某个类注册到IOC容器中,装配Bean

自动装配注解

  • @Autowired注解
    • @Autowired可以加在set方法和属性上
    • @Autowired自动装配的步骤是先使用ByType寻找属性对应的类型,如果存在多个相同的类型那么会使用ByName去寻找属性字段名对应的Bean的id
    • @Autowired(required = false):@Autowired注解有一个属性required 默认值为true,表示该属性不能为空,那么required = false时表示该属性可以为空,与注解@Nullable相同
  • @Qualifier(“XXX”)注解
    • @Qualifier()注解可以和@Autowired注解搭配使用,该注解可以指定一个Bean的id
  • @Resource注解
    • @Resource注解是Java自带的注解和@Autowired注解类似,不过它实现去找ByName,再去找ByType

小结

xml与注解

  • xml更加万能,适用于任何场合!,维护方便简单
  • 注解不是自己类使用狐狸哦啊。维护相对复杂!

xml与注解最佳实践

  • xml用来管理bean
  • 注解只负责属性的注入
  • 在我们使用过程中,只需要注意一个问题,必须让注解生效,就需要开启注解的支持<context:component-scan base-package="cn.xpp"/>
1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
">
<context:annotation-config/>
</beans>

11,Java配置Spring

现在我们完全不需要使用Spring的IOC容器xml配置文件了,全权交给Java来做!

JavaConfig是Spring的一个子项目,在Spring4之后,它成为了一个核心项目

类:

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
public class User {
@Value("沈仁雨")
private String naem;
@Value("20")
private int age;

public String getNaem() {
return naem;
}

public void setNaem(String naem) {
this.naem = naem;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "User{" +
"naem='" + naem + '\'' +
", age=" + age +
'}';
}
}

使用java类配置IOC容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package cn.xpp.config;

import cn.xpp.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration//该注解声明了这个类是一个Spring的配置类,等价于之前的beans.xml配置文件
@ComponentScan("cn.xpp.pojo")//该注解相当于IOC容器的<context:component-scan base-package/>
public class XppConfig {
/*
@Bean注解相当于是书写一个<bean>标签
id属性为该方法的名字getUser
class属性为方法内返回的类
返回类的属性可以通过方法设置或者@Value注解注入
*/
@Bean
public User getUser(){
return new User();
}
}

测试类

1
2
3
4
5
6
7
8
public class MyTest {
@Test
public void tes(){
ApplicationContext context = new AnnotationConfigApplicationContext(XppConfig.class);
User user = context.getBean("getUser", User.class);
System.out.println(user);
}
}

注解:

  • @Configuration注解
    • 该注解声明了这个类是一个Spring的配置类,等价于之前的beans.xml配置文件
    • 分析源码发现@Configuration注解本身也会被注册到IOC容器中,因为该注解的本身是一个@Componentimage-20210405173106312
  • @Bean注解
    • @Bean注解相当于是书写一个标签
    • id属性为该方法的名字
    • class属性为方法内返回的类
  • @ComponentScan注解
    • @ComponentScan注解相当于IOC容器的<context:component-scan base-package/>标签
    • 它可以帮助我们去寻找指定包下的@Component标签注册到IOC容器当中

注意:

  • 如果我们使用了Java类负责IOC容器,那当我们读取配置文件时(配置类)需要使用到AnnotationConfigApplicationContext类读取配置类的class对象

    1
    ApplicationContext context = new AnnotationConfigApplicationContext(XppConfig.class);

12,代理模式

12.1代理模式

**代理模式的定义:**代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。

image-20210406134132704

为什么要用代理模式?

  • **中介隔离作用:**在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口
  • **开闭原则,增加功能:**代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。

12.2静态代理模式

角色分析:

  • 抽象角色:一般会使用接口或者抽象类来解决,用来约束
  • 真实角色:被代理的角色
  • 代理角色:代理真实的角色,代理角色之后,我们一般会做一些附属操作
  • 客户:访问代理角色,来达成需求

抽象角色

1
2
3
4
5
//租房接口
public interface Rent {
//租房方法
public void rent();
}

真实角色

1
2
3
4
5
6
7
//房东
public class Host implements Rent{
@Override
public void rent() {
System.out.println("我要租房了");
}
}

代理角色

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
//中介类
public class Proxy implements Rent{
private Host host;

public Proxy() {
}

public Proxy(Host host) {
this.host = host;
}

@Override
public void rent() {
lanfang();
System.out.println("租房");
qianhetong();
shoqian();
}

public void lanfang(){
System.out.println("看房源");
}

public void qianhetong(){
System.out.println("签合同");
}

public void shoqian (){
System.out.println("收取中介费");
}
}

客户

1
2
3
4
5
6
7
//客户类
public class Client {
public static void main(String[] args) {
Proxy proxy = new Proxy(new Host());
proxy.rent();
}
}

执行结果

image-20210406135936906

总结:

​ 在这个案例中我们发现我们的真实角色(房东)只实现了租房的接口,向其他的业务它都没有,而当我们的客户去租房时,去找了代理类(中介),代理类帮助我们干了很多事情(看房,签合同,收取房东费)

  • 使我们的真实角色更加纯粹,不用去关注一些扩展业务,专注于自己
  • 扩展业务交给了我们的代理类,实现了业务分工
  • 当我们的扩展业务需要扩充时,只需管理代理类,集中管理(改别人的源码等同于刨人家祖坟)

缺点:

  • 每一个真实角色就产生一个代理类,代码量翻倍(动态代理可以解决)

组合与聚合的区别

简单来讲,组合是一种较为紧密的关系,从生命周期上看,部分和整体是共存亡的关系。
聚合则是一种较为松散的关系,部分和整体的生命周期未必一致。

**组合:**当我们的代理模式去代理真实对象时,代理类和真实类的生命周期会同时消亡

**聚合:**当我们的代理模式去代理真实对象时,真实类不会和代理类一起消亡,如图所示

Host类不是代理类内部产生的

image-20210406141535912

12.3动态代理模式

  • 动态代理模式和静态代理模式的角色时一样的
  • 动态代理的代理类是动态生成的,并不是固定写好的
  • 动态代理分为两大类:基于接口的动态代理/基于类的动态代理
    • 基于接口——JDK的动态代理
    • 基于类的——cglib
    • Java字节码实现:Javassist: (Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态"AOP"框架。)

常用类

Proxy(代理):生成代理实例

invocationHandler(调用处理):每一个代理实例都有一个调用处理程序,也就是代理实例需要实现invocationHandler这个接口

案例:

被代理对象,和代理类实现的接口

1
2
3
4
5
6
package cn.xpp.demo3;

public interface Married {
void married();
}

被代理对象

1
2
3
4
5
6
public class She implements Married{
@Override
public void married() {
System.out.println("我结婚了");
}
}

生成动态代理类

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
//生成代理类
public class ProxyinvocationHandler implements InvocationHandler {

//被代理的类
private Married married;

public void setMarried(Married married) {
this.married = married;
}

//得到生成的代理类
public Object getProxy(){
/*
newProxyInstance 生成一个代理类
this.getClass().getClassLoader() 代理的加载器
married.getClass() 被代理的类的接口
this InvocationHandler 每一个代理实例的调用处理程序
*/
return Proxy.newProxyInstance(this.getClass().getClassLoader(), married.getClass().getInterfaces(),this);
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
buzhi();
Object invoke = method.invoke(married, args);
shoqian();
return invoke;
}

public void buzhi(){
System.out.println("布置场地");
}

public void shoqian(){
System.out.println("收钱");
}
}

客户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class My {
public static void main(String[] args) {
//被代理对象
She she = new She();
//实例化动态代理
ProxyinvocationHandler pih = new ProxyinvocationHandler();
//在动态代理类中设置被代理对象
pih.setMarried(she);
//得到代理类
Married proxy = (Married) pih.getProxy();
//执行代理类方法
proxy.married();
}
}

理解:

这里可以和静态代理结合起来理解,我们书写的生成代理类对象必须是实现InvocationHandler接口,通过newProxyInstance生成一个代理实例,当我们去调用代理类的方法时,我们实现接口InvocationHendler的invoke方法来帮我们去执行(通过反射),所以InvocationHendler在JDK中被解释为调用处理程序

1
2
3
4
5
6
7
8
9
public Object getProxy(){
/*
newProxyInstance 生成一个代理类
this.getClass().getClassLoader() 代理的加载器
married.getClass().getInterfaces() 被代理的类的接口
this InvocationHandler 每一个代理实例的调用处理程序
*/
return Proxy.newProxyInstance(this.getClass().getClassLoader(), married.getClass().getInterfaces(),this);
}

关键点:

Object invoke = method.invoke(married, args);

通过反射的invoke方法执行被代理类married的方法

method是代理类married的方法

args数组存放了需要使用的参数

那上面那串代码的意思既是对married类将meatod方法执行,参数是args

一个可以代理任何类的动态代理实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;

public void setTarget(Object target) {
this.target = target;
}

public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(target, args);
return invoke;
}
}

13,AOP

13.1 简介:

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

13.2 什么时候要用到面向切面AOP呢?

举个例子,你想给你的网站加上鉴权,

对某些url,你认为不需要鉴权就可以访问,

对于某些url,你认为需要有特定权限的用户才能访问

如果你依然使用OOP,面向对象,

那你只能在那些url对应的Controller代码里面,一个一个写上鉴权的代码

而如果你使用了AOP呢?

那就像使用Spring Security进行安全管理一样简单(更新:Spring Security的拦截是基于Servlet的Filter的,不是aop,不过两者在使用方式上类似):

1
2
3
4
5
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/static","/register").permitAll()
.antMatchers("/user/**").hasRoles("USER", "ADMIN")

这样的做法,对原有代码毫无入侵性,这就是AOP的好处了,把和主业务无关的事情,放到代码外面去做。

所以当你下次发现某一行代码经常在你的Controller里出现,比如方法入口日志打印,那就要考虑使用AOP来精简你的代码了。

13.3 使用Spring实现AOP

在实现Spring-AOP之前我们需要先导入AOP织入包(因为SpringAOP是在程序运行时通过织入的方式执行)

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>

AOP 领域中的特性术语:

  • 通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
  • 连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
  • 切点(PointCut): 可以插入增强处理的连接点。
  • 切面(Aspect): 切面是通知和切点的结合。
  • 引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。
  • 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。

代理对象和被代理对象实现的接口

1
2
3
4
5
6
public interface User {
public void add();
public void delete();
public void update();
public void select();
}

被代理对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class UserImpl implements User{
@Override
public void add() {
System.out.println("添加了一个用户");
}

@Override
public void delete() {
System.out.println("删除了一个用户");
}

@Override
public void update() {
System.out.println("修改了一个用户");
}

@Override
public void select() {
System.out.println("查询了一个用户");
}
}

扩展业务

1
2
3
4
5
6
7
8
9
10
11
12
13
//MethodBeforeAdvice 在方法执行前打印
/**
* Method 方法
* Before 之前
* Advice 建议(日志)
*/
public class Log implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("["+new Date().toLocaleString()+"]"+target.getClass().getName()+"执行了"+method.getName()+"方法。"+"参数:"+args);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//AfterReturningAdvice 在方法执行之后打印
/**
* After之后
* Returning 返回值
* Advice 建议(日志)
*/
public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("["+new Date().toLocaleString() +"]"+target.getClass().getName()+"执行了"+method.getName()+"方法 "
+"参数是:"+args+" 返回值:"+returnValue);
}
}

IOC配置文件

注意一定要添加aop支持xmlns:aop="http://www.springframework.org/schema/aop"

http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd

**方式一:**使用Spring的API实现(Log,AfterLog实现的接口)

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
<?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"
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.xsd
">
<!--注册bean-->
<bean id="userimpl" class="cn.xpp.service.UserImpl"/>
<bean id="log" class="cn.xpp.Log.Log"/>
<bean id="afterlog" class="cn.xpp.Log.AfterLog"/>


<!--配置aop-->
<aop:config>
<!--pointcut切入点 :需要在哪里切入
id 切入点的名字
execution表达式:* * * * * 任何返回类型 任何包下的任何类 的任何方法 ..随意参数
-->
<aop:pointcut id="logproxy" expression="execution(* cn.xpp.service.UserImpl.*(..))"/>
<!--advisor 将需要的扩展业务加入到切入点-->
<aop:advisor advice-ref="log" pointcut-ref="logproxy"/>
<aop:advisor advice-ref="afterlog" pointcut-ref="logproxy"/>
</aop:config>

</beans>

**方式二:**配置自定义的扩展类

自定义的类

1
2
3
4
5
6
7
8
public class DiyLog {
public void before(){
System.out.println("=============执行方法前=================");
}
public void after(){
System.out.println("=============执行方法后=================");
}
}
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
<?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"
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.xsd
">
<!--注册bean-->
<bean id="userimpl" class="cn.xpp.service.UserImpl"/>
<bean id="log" class="cn.xpp.Log.Log"/>
<bean id="afterlog" class="cn.xpp.Log.AfterLog"/>

<!--注册自定义的类-->
<bean id="diy" class="cn.xpp.diy.DiyLog"/>
<!--配置aop-->
<aop:config>
<!--配置切入点pointcut-->
<aop:pointcut id="point" expression="execution(* cn.xpp.service.UserImpl.*(..))"/>
<!--设置自定义类的方法-->
<aop:aspect ref="diy">
<!--在方法执行之前调用before方法-->
<aop:before method="before" pointcut-ref="point"/>
<!--在方法执行之后调用after方法-->
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
</beans>

**方式三:**注解实现AOP

注意先使用注解AOP时要现在IOC配置文件中加入<aop:aspectj-autoproxy/>开启切面自动装配

image-20210409145432188

被AOP注解描述的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//相当于在beans.xml文件中配置了一个Aspect切面
@Aspect
public class DiyLog {
//相当于在切面中写了<aop:before/>标签
@Before("execution(* cn.xpp.service.UserImpl.*(..))")
public void before(){
System.out.println("=============执行方法前=================");
}

//相当于在切面中写了<aop:after/>标签
@After("execution(* cn.xpp.service.UserImpl.*(..))")
public void after(){
System.out.println("=============执行方法后=================");
}

//相当于在切面中写了<aop:around/>标签
@Around("execution(* cn.xpp.service.UserImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
jp.proceed();
System.out.println("环绕后");
}
}

执行结果

image-20210409130102895

14,整合Mybatis

添加Spring-Mybaitis的一些jar包

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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>Spring-study</artifactId>
<groupId>cn.xpp</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>Spring-Mybatis</artifactId>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>

<build>
<!-- 以下是资源过滤器 可以在生成项目target时将.XML/.properties配置文件保留 -->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/test/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.18</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>

<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.5</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.5</version>
</dependency>

<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>

</dependencies>
</project>
  1. 创建IOC容器配置文件,书写数据源

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <!--数据源  配置数据库的那些东西 现在使用spring提供的类DriverManagerDataSource进行配置注册-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <!--配置jdbc-->
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
    <!--配置数据连接地址-->
    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&amp;characterEncoding=utf8&amp;useSSl=true"/>
    <!--配置用户-->
    <property name="username" value="root"/>
    <!--配置密码-->
    <property name="password" value="root"/>
    </bean>
  2. 书写SqlSessionFactoryBean(相当于原Mybatis的配置文件)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <!--注册SqlSessionFactoryBean相当于通过Spring内置的SqlSessionFactoryBean对象来代替了Mybatis的mybatis-config.xml文件-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!--将数据源注入到SqlSessionFactoryBean中-->
    <property name="dataSource" ref="dataSource"/>
    <!--将Spring的SqlSessionFactoryBean和Mybatis的配置文件关联起来,一起用方便,互通-->
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
    <!--将Mybatis的xml文件注册-->
    <property name="mapperLocations" value="classpath:cn/xpp/dao/*Mapper.xml"/>
    </bean>
  3. 书写SqlSessionTemplate(相当于原Mybatis的SqlSession)注意:SqlSessionTemplate是线程安全的和Mybatis的SqlSession不同,它可以进行复用

    1
    2
    3
    4
    5
    <!--SqlSessionTemplate相当于Mybatis的SqlSession,可以通过她执行数据库操作-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <!--通过构造器将配置文件注入,读取配置文件生成SqlSessionTemplate-->
    <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
  4. 实现*Mapper接口的实现类(服务层)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class UserMapperImpl implements UserMapper{
    private SqlSessionTemplate sqlSession;

    public void setSqlSession(SqlSessionTemplate sqlSession) {
    this.sqlSession = sqlSession;
    }

    //注意这里敢使用同一个sqlSession而不像Mybatis每一次使用new新的sqlSession是因为Spring的SqlSessionTemplate是线程安全的,
    // 可以多次复用
    @Override
    public List<User> getUserList() {
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    return mapper.getUserList();
    }
    }
  5. 将实现类注册到IOC配置文件中

    1
    2
    3
    4
    5
    <!--编写服务层,也就是接口的实现类-->
    <bean id="userMapper" class="cn.xpp.dao.UserMapperImpl">
    <!--set注入sqlSession-->
    <property name="sqlSession" ref="sqlSession"/>
    </bean>

方式二:

我们可以将实现类继承一个SqlSessionDaoSupport的类

通过SqlSessionDaoSupport类的getSqlSession方法会帮助我们生成SqlSessionTemplate(进一步简化代码)

1
2
3
4
5
6
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> getUserList() {
return getSqlSession().getMapper(UserMapper.class).getUserList();
}
}

**注意:**在IOC容器注册时只需注入sqlSessionFactory即可(不是sqlSession

1
2
3
4
<bean id="userMapper2" class="cn.xpp.dao.UserMapperImpl2">
<property name="sqlSessionFactory"
ref="sqlSessionFactory"/>
</bean>

15,Spring声明式事务

事务的ACID原则

  • 原子性
  • 一致性
  • 持久性
  • 隔离性

事务的重要性不言而喻,在项目中我们一定需要保证数据库的安全,维持ACID原则

那么如果我们在Spring中配置事务需要做些什么

Spring开启事务流程

  1. 要开启 Spring 的事务处理功能,在 Spring 的配置文件中创建一个 DataSourceTransactionManager 对象:

    1
    2
    3
    4
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--dataSource配置的数据源-->
    <constructor-arg ref="dataSource" />
    </bean>
  2. 在Spring配置文件中声明事务的命名空间

    1
    xmlns:tx="http://www.springframework.org/schema/tx"
  3. 通过AOP织入的方式声明事务

    1
    2
    3
    4
    5
    6
    7
    8
    <!--通过AOP织入的方式声明事务
    advice通知-->
    <tx:advice id="interceptor" transaction-manager="transactionManager">
    <tx:attributes>
    <!--method方法(支持通配符) propagation事务传播-->
    <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
    </tx:advice>
  4. 将声明式事务注册到AOP中

    1
    2
    3
    4
    5
    <aop:config >
    <!--pointcut切面 选择从哪里织入-->
    <aop:pointcut id="point" expression="execution(* cn.xpp.dao.UserMapperImpl3.*(..))"/>
    <aop:advisor advice-ref="interceptor" pointcut-ref="point"/>
    </aop:config>

事务传播

关键字:propagation ,除了第一个REQUIRED,需要记住事务传播有七种

  • REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 (也是Spring的默认事务)

  • SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。

  • MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。

  • REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。

  • NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

  • NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。

  • NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。

声明式事务

声明式事务是通过Spring的AOP实现的所以,它有着不入侵源代码,横向扩展业务的好处

注意

Spring一次事务只包含实现类的一个方法如:add()方法就是一次事务,而add()和delete()方法是两次事务

16,重点总结

  • IOC【重点】
  • Bean自动装配【重点】
  • Spring注解开发【重点】
  • 代理模式和AOP【重点】
  • 声明式事务【重点】

文章作者:xpp011

发布时间:2021年11月06日 - 15:11

原始链接:http://xpp011.cn/2021/11/06/52ba89f4.html

许可协议: 转载请保留原文链接及作者。