1. Spring5
1.1 简介
- Spring(春天)—>给软件行业带来了春天!
- Spring框架以interface21为基础,经过重新设计而成!
- Spring理念: 使现有的技术更加容易使用,本身是个大杂烩,整合了现有的技术框架!
- Spring是一个轻量级的IoC和AOP容器框架。是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求。
- Spring是为了简化企业级应用开发的复杂性而创建的,简化开发!
Spring是如何简化Java开发的?
- 基于POJO的轻量级和最小侵入性编程;
- 通过IOC,依赖注入(DI)和面向接口编程实现松耦合
- 基于切面(AOP)和惯例进行声明式编程;
- 通过切面和模板减少样式代码
Spring官网: Spring
官方下载: 官方下载地址
GitHub: GitHub
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc
推荐导入以下这个包,其会自动导入和Spring相关的许多包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
1.2 优点
- Spring是一个开源的免费的框架(容器)!
- Spring是一个轻量级的,非入侵式的框架!
- 控制反转(IOC), 面向切面编程(AOP)!
- 支持事务的处理, 对框架整合的支持!
即: Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架!
1.3 组成
Spring框架是一个分层架构,由以下七大模块组成。
七大模块
组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合。
每个模块的功能如下:
- Spring Core:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。
- Spring Context:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
- Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中。
- Spring DAO:Spring DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
- Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
- Spring Web:Web 模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。Web 模块简化了处理多部分请求以及将请求参数绑定到域对象的工作。
- Spring MVC:Spring MVC 框架是一个全功能的构建 Web 应用程序的实现。其容纳大量视图技术,其中包括 JSP、Velocity等。
备注:
- Spring Boot
- 一个快速开发的脚手架,
- 基于springboot可以快速的开发单个微服务。
- 约定大于配置.
- Spring Cloud
- Spring Cloud是基于SpringBoot实现的
故Spring的学习起着承上启下的作用。
Spring的弊端: 配置十分繁琐,“配置地狱”!
2. IOC理论推导
在之前 JavaWeb阶段根据MVC开发一般需要:
-
UserDao接口
-
UserDaoImpl实现类
-
UserService业务层接口
-
UserServiceImpl业务接口实现类(且其中需要调用Dao层逻辑,需要new一个Dao层对象)
-
Servlet控制层(其中调用业务层逻辑,需要new一个业务层对象)
…
在JavaWeb的Mvc阶段的业务实现中, 用户需求的变化可能会引起我们去改动原来的代码;
如果代码量十分大,修改代码的成本会很高!
而如果通过提供一个Set方法,实现动态值的注入,就不用去直接改动源代码。
//利用Set动态实现Dao层对象的注入
public void setUserDao(UserDao userDao){
this.userDao = userDao;
}
之前,程序是主动创建对象,意味着控制权在程序猿手中!
使用了set方法动态注入值后,程序不再具有主动性,而是变成了被动的接受对象!
这种思想,从本质上解决了原来需要去改动源代码的问题。
程序员不用再去管理对象的创建了。
系统的耦合性大大降低,可以更加专注于业务逻辑的实现。
而这就是IOC控制反转的原型!!
IOC本质
控制反转IOC(Inversion of Control)其实是一种设计思想,
DL(依赖注入)是实现IOC的一种方法。
在没有IOC的程序中,我们使用面向对象编程, 对象的创建与对象间的依赖关系完全硬编码在程序中,即对象的创建由程序自己控制。
而在有IOC控制反转的程序中,对象的创建和管理转移给了第三方。
IOC通俗理解为: 获得依赖对象的方式反转了!
控制反转是一种通过(XML或注解)等进行描述并通过第三方去生产或获取特定对象的方式。
在Spring中实现控制反转的是IOC容器,其实现方式是依赖注入(Dependency injection, DI)。
IOC是Spring框架的核心内容,其使用多种方式完美的实现了IOC,即可以使用XML配置,也可以使用注解,甚至可以零配置实现IOC。
Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,待程序需使用时就直接从IOC容器中取出对象使用即可。
3. HelloSpring
- 导入Spring相关jar包
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc
Spring 需要导入commons-logging进行日志记录,我们通过maven导入以下这个包,其会自动下载对应的依赖项 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
- 编写相关代码
2.1 编写一个Hello实体类。
Hello.java
package com.carson.pojo;
public class Hello {
private String str;
public String getStr() {
return str;
}
//对象的属性通过Spring容器进行设置,即配置文件bean中的property标签
//这里一定要有Set,不然spring的配置文件property标签传值会报红
public void setStr(String str) {
this.str = str;
}
@Override
public String toString() {
return "Hello{" +
"str='" + str + '\'' +
'}';
}
}
2.2 通过创建xml文件的方式编写一个spring配置文件,
示例命名为: beans.xml
注: 在beans.xml中书写bean标签,就相当于创建对应的类的对象;
所以beans.xml中的各种bean标签就相当于各个类的已创建好的对象;
所以感觉beans.xml就是一个对象的容器的感觉!
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" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--bean就是java对象,由Spring创建和管理-->
<!--
id: java对象的自定义变量名
class: new的对象所在的全路径
property: 相当于给java对象中的属性传入值,使用前提是java类中要有属性的set方法
-->
<bean id="hello" class="com.carson.pojo.Hello">
<!--
property标签传值有两个属性value和ref
ref: 传入的是已注册的bean对象id,即引用Spring容器中已创建好的对象
value: 传入的是基本数据类型等具体的值-->
<property name="str" value="Spring"/>
</bean>
</beans>
2.3 编写测试文件进行测试
public class MyTest {
public static void main(String[] args) {
//获取Spring的上下文对象,传入的参数可以为一个或多个xml配置文件,下面的API函数为读取配置文件来创建上下文对象的API, Spring还有其它通过读取注解或文件的方式的相关API
//Spring容器就类似于婚介所,管理众多的对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//现在我们的对象都在Spring中进行管理了,我们要使用,直接去里面取出来就可以了
//getBean():传入的参数为spring配置文件中对象的bean标签的id
Hello hello = (Hello)context.getBean("hello");
System.out.println(hello.toString());//测试输出
}
}
注:
- Hello对象是由Spring创建的。【即Spring配置文件】
- Hello对象的属性是通过Spring容器进行设置的,即bean标签中的property标签。使用前提是原先对象中存在相应属性的Set方法。
以上的过程就叫 控制反转。
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建,即手动new对象。
使用Spring后,对象的创建和管理是由Spring来负责的。
反转:程序本身不创建对象,而变成了被动的接受对象。
DI依赖注入: 就是利用set方法来对特定属性的属性值进行注入。
IOC控制反转是一种编程思想,由主动的编程变成被动的接收。
所谓的IOC,一句话理解: 对象由Spring来创建,管理和装配!!
4. IOC创建对象的配置方式
创建测试用的实体类User.java
package com.carson.pojo;
public class User {
private String name;
public User() {
System.out.println("User的无参构造被初始化了!");
}
public User(String name) {
this.name = name;
System.out.println("User的有参构造被初始化了");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name="+name);
}
}
- 使用无参构造器+属性的Set方法来创建对象
即通过这种方式创建对象,原本bean实体类必须具有无参构造器和相应属性的Set方法!
<!--通过无参构造器的对象配置-->
<bean id="user" class="com.carson.pojo.User" >
<!--name属性绑定对象的属性参数名-->
<property name="name" value="Carson无参构造对象"></property>
</bean>
-
使用有参构造器创建对象
即通过这种方式创建对象,原本bean实体类必须具有 有参构造器!
以下是三种配置对象属性值的方式,即属性值的注入方式:
- 传递 参数位置下标注入属性值【推荐】
<!-- 通过有参构造器的对象属性配置 方式一:通过参数位置下标(从0开始),来注入属性值 --> <bean id="user" class="com.carson.pojo.User"> <constructor-arg index="0" value="Carson通过参数下标创造有参构造对象"></constructor-arg> </bean>
- 传递 参数类型注入属性值【不推荐使用】
(简单数据类型直接写,复杂数据类型需要写成全类名)
<!-- 通过有参构造器的对象属性配置 方式二:通过参数类型(简单数据类型直接写,复杂数据类型需要写成全类名) 不推荐使用,因为万一多个参数的类型一致就麻烦了 --> <bean id="user" class="com.carson.pojo.User"> <constructor-arg type="java.lang.String" value="Carson通过参数类型创造有参构造对象"></constructor-arg> </bean>
- 参数名
<!-- 通过有参构造器的对象属性配置 方式三:通过name属性绑定参数名字 --><bean id="user" class="com.carson.pojo.User"> <constructor-arg name="name" value="Carson通过name属性绑定参数名字创造有参构造对象"></constructor-arg></bean>
注:
1. 加载spring的配置文件的时候,spring容器中的对象(配置文件中的对象)就都被创建了,需要使用特定的对象的时候,就使用getBean()方法获取对象即可。
- spring配置文件中对象的创建默认为单例模式,故使用getBean()获取同个类对象的话,都是相同的同一个实例对象。
下面是测试使用getBean()获取同个类对象:
public class MyTest {
public static void main(String[] args) {
//获取Spring的上下文对象(传入spring的xml配置文件),加载配置文件
//在加载配置文件的时候,spring容器中的对象(配置文件中的对象)就都被创建了,且每个对象有且只有一个(单例模式)
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//getBean获取对象
User user = (User) context.getBean("user");
User user1 = (User) context.getBean("user");
user.show();
//即只存在一个实例对象(故获取的两个对象是一样的,结果为true)
System.out.println("获取的两个对象是否相同:"+(user==user1));
}
}
5. Spring配置说明
5.1 别名【alias】
<!--通过alias标签对一个bean取别名
name:对应原来bean的id名
alias: 对应别名
-->
<alias name="user" alias="user2"></alias>
//getBean获取对象(取了别名之后对象就多了个名字,就都可以获取的到)
User user = (User) context.getBean("user");
User user2 = (User) context.getBean("user2");
5.2 Bean的配置【bean】
<!-- bean标签的属性说明
id: bean的唯一标识符,相当于创建的对象的标识符
class: bean对象对应的类的全限定名,即包名+类名
name: 也是对bean取别名,可以同时写多个别名,多个别名间的分隔符可以是逗号,空格,分号
scope: 更改对象的创建模式,默认是singleton单例模式创建
-->
<bean id="user" class="com.carson.pojo.User" name="user2 user3,user4;user5" scope="singleton">
<property name="name" value="Carson"></property>
</bean>
5.3 Bean的作用域
- 单例模式(singleton, Spring的默认机制)
正如单例设计模式的性质一般, 容器中对象的存在有且只有一个,
故通过getBean()多次获取同个对象都是相同的。
<!--显式声明bean对象创建为单例模型-->
<bean id="user" class="com.carson.pojo.User" scope="singleton">
</bean>
- 原型模型(prototype)
正如原型设计模式的性质一般,容器中的对象不只存在一个,其它对象的创建以一个对象为原型进行复制而得。
故通过getBean()多次获取同个对象都是不同的!
- 其余的request, session, application等只能在web开发中使用!
5.4 import
import标签, 一般用于团队开发使用, 它可以将多个beans.xml
等spring配置文件, 导入合并为一个总的spring配置文件, 即一般命名为applicationContext.xml
的总配置文件。
假设现在项目中有多个人开发,每个人负责不同的类的开发,而每个人都会有一个beans.xml
的配置文件,从而导致不同的类被注册在不同的配置文件中,从而我们可以利用import将所有人的beans.xml
合并为一个总的applicationContext.xml
!
这样在加载spring配置文件的时候,导入总的配置文件即可。
<!--bean会根据导入配置文件的先后顺序进行导入-->
<import resource="beans.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>
注:
多个beans.xml配置文件合并时, 可能会发生多个beans.xml文件中的bean出现id相同状况,这时后面进入的配置文件中的bean会覆盖掉先进入的配置文件中的bean!
6. 依赖注入(DI)
什么是依赖?
bean对象的创建依赖于容器!
什么是注入?
bean对象中所有属性值,由容器来负责注入!
6.1 有参构造器属性注入
通过这种有参构造器的方式创建对象,原本bean实体类必须具有 有参构造器!
以下是通过有参构造器的三种属性值的注入方式:
- 传递 参数位置下标注入属性值【推荐】
<!-- 通过有参构造器的对象属性配置
方式一:通过参数位置下标(从0开始),来注入属性值
-->
<bean id="user" class="com.carson.pojo.User">
<constructor-arg index="0" value="Carson通过参数下标创造有参构造对象"></constructor-arg>
</bean>
- 传递 参数类型注入属性值【不推荐使用】
(简单数据类型直接写,复杂数据类型需要写成全类名)
<!-- 通过有参构造器的对象属性配置
方式二:通过参数类型(简单数据类型直接写,复杂数据类型需要写成全类名)
不推荐使用,因为万一多个参数的类型一致就麻烦了
-->
<bean id="user" class="com.carson.pojo.User">
<constructor-arg type="java.lang.String" value="Carson通过参数类型创造有参构造对象"></constructor-arg>
</bean>
- 参数名
<!-- 通过有参构造器的对象属性配置
方式三:通过name属性绑定参数名字
-->
<bean id="user" class="com.carson.pojo.User">
<constructor-arg name="name" value="Carson通过name属性绑定参数名字创造有参构造对象"></constructor-arg>
</bean>
6.2 无参构造器+Set方式注入【重点】
由于原本bean实体类默认自带无参构造器,所以只要不写有参构造器就不会覆盖掉无参构造器!
故使用Set方式注入时,原本bean类注意不要写 有参构造器!
测试环境搭建
- 被引用的数据对象
Address.java
package com.carson.pojo;
public class Address {
//待注入值的属性
private String address;
//1.要有无参构造器
public String getAddress() {
return address;
}
//2.要有属性值的set方法
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Address{" +
"address='" + address + '\'' +
'}';
}
}
- 真实创建的对象
Student.java
package com.carson.pojo;
import java.util.*;
public class Student {
//普通简单数据类型
private String name;
//引用数据类型
private Address address;
//数组
private String[] books;
//List
private List<String> hobbies;
//Map
private Map<String,String> card;
//Set
private Set<String> games;
//设置null
private String wife;
//Properties
private Properties info;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String[] getBooks() {
return books;
}
public void setBooks(String[] books) {
this.books = books;
}
public List<String> getHobbies() {
return hobbies;
}
public void setHobbies(List<String> hobbies) {
this.hobbies = hobbies;
}
public Map<String, String> getCard() {
return card;
}
public void setCard(Map<String, String> card) {
this.card = card;
}
public Set<String> getGames() {
return games;
}
public void setGames(Set<String> games) {
this.games = games;
}
public String getWife() {
return wife;
}
public void setWife(String wife) {
this.wife = wife;
}
public Properties getInfo() {
return info;
}
public void setInfo(Properties info) {
this.info = info;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", address=" + address +
", books=" + Arrays.toString(books) +
", hobbies=" + hobbies +
", card=" + card +
", games=" + games +
", wife='" + wife + '\'' +
", info=" + info +
'}';
}
}
- 配置文件
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" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="addressBean" class="com.carson.pojo.Address">
<property name="address" value="深圳"/>
</bean>
<bean id="student" class="com.carson.pojo.Student">
<!--普通简单数据类型值注入,通过value-->
<property name="name" value="Carson"/>
<!--其与下面的形式等价-->
<property name="name">
<value>Carson</value>
</property>
<!--bean注入,引用数据类型的属性值注入,需要通过ref-->
<property name="address" ref="addressBean"/>
<!--数组-->
<property name="books">
<array>
<value>JAVA</value>
<value>Spring</value>
<value>Python</value>
<value>NOdeJS</value>
</array>
</property>
<!--List-->
<property name="hobbies">
<list>
<value>码代码</value>
<value>听歌</value>
<value>健身</value>
</list>
</property>
<!--Map-->
<property name="card">
<map>
<entry key="姓名" value="Carson"/>
<entry key="学号" value="1111111"/>
</map>
</property>
<!--Set-->
<property name="games">
<set>
<value>张三</value>
<value>李四</value>
<value>王五</value>
</set>
</property>
<!--传递null值-->
<property name="wife">
<null/>
</property>
<!--Properties-->
<property name="info">
<props>
<prop key="driver">com.mysql.jdbc.Driver</prop>
<prop key="url">http://localhost:3306</prop>
<prop key="username">root</prop>
<prop key="password">root</prop>
</props>
</property>
</bean>
</beans>
- 测试类
public class MyTest {
public static void main(String[] args) {
//读取配置文件,创建上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//getBean()获取对象
Student student = (Student) context.getBean("student");
//打印输出
System.out.println(student.toString());
}
}
6.3 拓展方式注入
我们还可以使用p命名空间和c命名空间进行依赖注入!
c命名空间: 是对应有参构造器注入的简化版!
p命名空间: 是对应无参构造器+Set方法注入的简化版!
注意点:
c命名和p命名空间不能直接使用,需要在xml文件上方进行导入!
使用的bean实体类:
User.java
public class User {
private String name;
private int age;
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试:
@Test
public void test(){
//读取配置文件获取上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
//getBean()获取对象返回Object,如果参数传递bean的class对象的话就不用再进行对象的强制转换了
User user = context.getBean("user", User.class);
System.out.println(user);
}
6.3.1 c命名空间注入
beans.xml
中导入c命名空间并进行bean对象配置
<?xml version="1.0" encoding="UTF-8"?>
<beans ....
xmlns:c="http://www.springframework.org/schema/c"
....
<!--c命名空间注入:
本质: c对应construct-args,本质通过有参构造器方式注入
使用格式: c:属性名="属性值" -->
<bean id="user" class="com.carson.pojo.User" c:name="C: carson" c:age="21" />
</beans>
注:
- c命名空间注入的本质是: 通过有参构造器方式注入!
- 所以bean实体类中必须要有 有参构造方法!
6.3.2 p命名空间注入
beans.xml
中导入p命名空间并进行bean对象配置
<?xml version="1.0" encoding="UTF-8"?>
<beans ....
xmlns:p="http://www.springframework.org/schema/p"
....
<!--p命名空间注入:
本质: p对应property属性,本质通过set方法注入
使用格式: p:属性名="属性值"-->
<bean id="user" class="com.carson.pojo.User" p:name="P: carson" p:age="21"/>
</beans>
注:
- p命名空间注入的本质是: 通过无参构造器+Set方法的方式注入!
- 所以bean实体类中要有无参构造器和对应属性的set方法!
7. Bean的自动装配
什么是Bean的自动装配?
即: 自动给bean的属性装配值。
Spring会在上下文自动寻找,并自动给bean装配对应的引用数据类型变量的属性值!
在Spring中有三种给bean装配属性值的方式:
- 在spring的xml配置文件中手动显式的配置bean及其属性值,也就是上面的做法!
- 通过Java程序给bean装配属性值。
- 这里要讲的是隐式的自动装配bean属性值【重要】【针对引用数据类型的属性进行装配赋值】
7.1 未使用自动装配
示例测试
- 创建测试用的相关的实体类
Car.java
public class Cat {
public void shout(){
System.out.println("小猫叫了!");
}
}
Dog.java
public class Dog {
public void shout(){
System.out.println("小狗叫了!");
}
}
People.java
public class People {
//引用数据类型
private Cat cat;
//引用数据类型
private Dog dog;
//基本数据类型
private String name;
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- 编写
beans.xml
配置文件,需要手动配置引用数据类型对应的 bean
<bean id="cat" class="com.carson.pojo.Cat"/>
<bean id="dog" class="com.carson.pojo.Dog"/>
<bean id="people" class="com.carson.pojo.People" >
<property name="name" value="Carson"/>
<!--需要手动配置引用数据类型对应的bean,如下:-->
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
</bean>
7.2 byName自动装配
具体如何使用byName进行自动装配?
答: 在bean标签的autowire属性进行配置,选择属性值为byName.
<bean id="cat" class="com.carson.pojo.Cat"/>
<bean id="dog" class="com.carson.pojo.Dog"/>
<!--使用byName的话,被注入的bean的id要和当前对象的特定的引用数据类型属性的set方法set后面的单词一致且首字母要小写-->
<bean id="people" class="com.carson.pojo.People" autowire="byName">
<property name="name" value="Carson"/>
</bean>
注:
- 使用byName自动装配可以省略手动传递引用数据类型属性值。
- 使用byName的话,bean的id要和对象的特定属性的set方法set后面的单词一致且首字母要小写。
- 以本例而言, 即第一个bean的id属性cat对应了People类中cat属性的setCat这个方法中Cat的首字母小写的形式。
- 以本例而言, 即第二个bean的id属性dog对应了People类中dog属性的setDog这个方法中Dog的首字母小写的形式。
7.3 byType自动装配
具体如何使用byType进行自动装配?
答: 在bean标签的autowire属性进行配置,选择属性值为byType.
<bean class="com.carson.pojo.Cat"/>
<bean class="com.carson.pojo.Dog"/>
<!--使用byType的话,bean的class全路径要和对象的特定属性的类型相一致
1:使用byType的话要确保bean全局唯一,不能出现class相同的重复的bean
2: 使用byType的话,bean可以省略id属性匹配
-->
<bean id="people" class="com.carson.pojo.People" autowire="byType">
<property name="name" value="Carson"/>
</bean>
注:
- 使用byType自动装配可以省略手动传递引用数据类型的属性值。
- 使用byType的话要确保bean全局唯一,不能出现class相同的重复的bean。
- 使用byType的话,bean可以省略id属性,如上所示。
- 使用byType的话,bean的class全路径要和对象的特定属性的类型相一致。
7.4 使用注解实现自动装配
使用注解进行自动装配的常用注解有哪些?
- @Autowired【Spring的注解】
- @Resource 【Java的注解】
注:
自动装配的对象是有要求的。
【即:只针对引用数据类型的属性进行装配赋值】
【在属性/set方法上放置这两个注解,就相当于去绑定了Spring容器中已经创建好的对象;即相当于创建了实体类对象】
7.4.1 @Autowired和@Resource的比较
相同点:
- 都是针对引用数据类型数据属性值的自动装配。
- 注解要么都可以放在属性字段上面,要么都可以放在属性对应的Set方法上面。
不同点:
- @Autowired默认先通过byType进行匹配, 如果匹配不到容器中对应的bean标签,则通过byName进行匹配;如果还匹配不到,就需要结合另外一个注解@Qualifier(value=“要匹配的bean的id名”)来与指定的特定bean进行匹配。
- @Resource默认先通过byName进行匹配, 如果匹配不到容器中对应的bean标签,则通过byType进行匹配;如果还匹配不到,就需要给@Resource注解中的name属性赋值来与指定的特定bean进行匹配, 即@Resource(name=“匹配的bean的id”)
7.4.2 @Autowired【Spring的注解】
由于@Autowired属于Spring的注解,而使用Spring的注解之前需要在beans.xml中进行相关的配置
使用Spring注解配置beans.xml配置文件步骤
- beans标签中导入与注解的相关的约束,如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
</beans>
- beans标签需要配置使用Spring注解的标签,如下:
<beans>
.....
<!--Spring使用注解需要配置的标签-->
<context:annotation-config/>
....
</beans>
- 最终使用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" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--Spring使用注解需要配置的标签-->
<context:annotation-config/>
<!--配置相关的bean对象-->
<bean id="cat" class="com.carson.pojo.Cat"></bean>
<bean id="dog" class="com.carson.pojo.Dog"></bean>
<bean id="people" class="com.carson.pojo.People"></bean>
</beans>
@Autowired注解的具体使用:
- @Autowired默认按照byType与容器中bean标签进行匹配并进行自动装配,示例如下:
beans.xml
<!--Spring使用注解需要配置的标签-->
<context:annotation-config/>
<!--bean对象配置-->
<bean id="cat" class="com.carson.pojo.Cat"></bean>
<bean id="dog" class="com.carson.pojo.Dog"></bean>
People.java
public class People {
@Autowired
private Cat cat;
@Autowired
private Dog dog;
}
- 当出现多个相同类型的bean就按照byName进行匹配并进行自动装配, 示例如下:
beans.xml
<!--Spring使用注解需要配置的标签-->
<context:annotation-config/>
<!--bean对象配置-->
<bean id="cat" class="com.carson.pojo.Cat"></bean>
<bean id="cat1" class="com.carson.pojo.Cat"></bean>
<bean id="dog" class="com.carson.pojo.Dog"></bean>
<bean id="dog1" class="com.carson.pojo.Dog"></bean>
People.java
public class People {
@Autowired
private Cat cat;
@Autowired
private Dog dog;
}
-
如果此时byName也匹配不到,可以再加上一个@Qualifier(value=“要匹配的bean的id名”)来指定特定的bean进行匹配。
示例如下:
beans.xml
<!--Spring使用注解需要配置的标签-->
<context:annotation-config/>
<!--bean对象配置-->
<bean id="cat1" class="com.carson.pojo.Cat"></bean>
<bean id="cat2" class="com.carson.pojo.Cat"></bean>
<bean id="dog1" class="com.carson.pojo.Dog"></bean>
<bean id="dog2" class="com.carson.pojo.Dog"></bean>
People.java
public class People {
@Autowired
@Qualifier("cat1")
private Cat cat;
@Autowired
@Qualifier("dog1")
private Dog dog;
}
- @Autowired注解的特殊用法
@Autowired(required=false)
当beans.xml
中没有配置相应的对象的bean标签时,这里以Cat属性为例。
<!--配置bean,没有配置Cat对应的bean标签-->
<bean id="dog" class="com.carson.pojo.Dog"></bean>
<bean id="people" class="com.carson.pojo.People"></bean>
原先的People.java
Cat字段中用@Autowired注解,但不显式指定required属性为false:
发现Cat属性会报红,再运行测试程序:
测试程序
public class MyTest {
public static void main(String[] args) {
//读取配置文件创建上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//getBean()获取对象
People people = context.getBean("people", People.class);
//打印引用数据类型对象
System.out.println(people.getCat());
System.out.println(people.getDog());
}
发现运行测试测试程序也会出错。
但如果显式定义了Autowired的required属性为false,这个Cat对象就可以为null,
发现Cat属性不会报红了。
同时再运行下上面的测试程序:
测试程序
public class MyTest {
public static void main(String[] args) {
//读取配置文件创建上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//getBean()获取对象
People people = context.getBean("people", People.class);
//打印引用数据类型对象
System.out.println(people.getCat());
System.out.println(people.getDog());
}
这样运行测试程序打印对象属性值也不会报错。
发现程序正常运行,且Cat属性可以为null。
7.4.3 @Resource【Java的注解】
由于@Resource属于Java的注解,故不需要在beans.xml中做任何配置
@Resource注解的具体使用:
-
@Resource默认先按byName进行匹配
示例如下:
beans.xml
<!--bean对象配置-->
<bean id="cat" class="com.carson.pojo.Cat"></bean>
<bean id="dog" class="com.carson.pojo.Dog"></bean>
People.java
public class People {
@Resource
private Cat cat;
@Resource
private Dog dog;
}
-
byName匹配不到再按byType进行匹配.
示例如下:
beans.xml
<!--bean对象配置-->
<bean id="cat1212" class="com.carson.pojo.Cat"></bean>
<bean id="dog1212" class="com.carson.pojo.Dog"></bean>
<bean id="people" class="com.carson.pojo.People"></bean>
People.java
public class People {
@Resource
private Cat cat;
@Resource
private Dog dog;
}
虽然上面的bean的id不一样,但Type是唯一的,所以属性值是可以正常注入的!
-
@Resource如果通过byType进行匹配也匹配不到,就需要给@Resource注解中的name属性赋值来指定匹配的bean。
格式即: @Resource(name=“匹配的bean的id”)
示例如下:
beans.xml
<bean id="cat1" class="com.carson.pojo.Cat"></bean>
<bean id="cat2" class="com.carson.pojo.Cat"></bean>
<bean id="dog1" class="com.carson.pojo.Dog"></bean>
<bean id="dog2" class="com.carson.pojo.Dog"></bean>
People.java
@Resource(name="cat1")
private Cat cat;
@Resource(name="dog1")
private Dog dog;
8.Spring使用注解开发
Spring注解使用前提?
- beans.xml配置文件导入context约束和注解的支持
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:Context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--context:component-scan 指定要扫描的包,这个包下的注解就会生效-->
<context:component-scan base-package="com.carson.pojo"/>
<!--context:annotation-config 使用其让Spring注解生效-->
<context:annotation-config/>
</beans>
- 在Spring4之后,要使用注解开发,必须要保证aop的包导入了.
- 建立测试的实体类
User.java
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
8.1 使用注解进行bean注册
使用的注解: @Component
,放在类的上方,说明这个类被Spring管理了!
注:
- @Component注解, 等价于 <bean id=“user” class="“com.carson.pojo.User”/>
- 注册的bean的id名字默认为这个类名的小写形式
示例如下:
applicationContext.xml
<!--context:component-scan 指定要扫描的包,这个包下的注解就会生效-->
<context:component-scan base-package="com.carson.pojo"/>
<!--context:annotation-config 使用Spring注解的配置-->
<context:annotation-config/>
User.java
//@Component注解
//等价于 <bean id="user" class=""com.carson.pojo.User"/>
//注册的bean的id名字默认为这个类名的小写形式,即: user
@Component
public class User {
private String name;
.....
}
8.2 注解限制bean作用域
使用的注解: @Scope(“具体值”)
,放在类的上方。
示例如下:
User.java
@Component
@Scope("singleton")//单例模式
@Scope("prototype")//原型模式
public class User {
private String name;
....
}
8.3 注解进行属性值注入
使用的注解: @Value("属性值")
注:
- 针对的属性是简单数据类型的属性值注入!
- @Value(“属性值”)等价<propertyname=“name” value=“属性值” />
- @Value(“属性值”) 可以直接加在属性上方
- @Value(“属性值”) 也可以直接加在属性对应的Set方法上方
示例如下:
User.java
public class User {
//@Value("属性值") 可以直接加在属性上方
//等价于<property name="name" value="属性值" />
@Value("Carson")
private String name;
public String getName() {
return name;
}
//@Value("属性值") 也可以直接加在属性对应的Set方法上方
//@Value("Carson的Set方法!")
public void setName(String name) {
this.name = name;
}
}
8.4 自动装配
注: 针对的属性是引用数据类型的属性值的自动装配!
自动装配的相关注解使用 上面已记录,参考上面即可。
8.5 衍生的注解
@Component
有几个衍生的注解。
常用于Web开发中的MVC三层架构进行分层的相关Java类/接口 中。
<!--更改扫描的包,范围扩大为: com.carson下的所有包的内容-->
<context:component-scan base-package="com.carson"/>
<!--context:annotation-config 使用Spring注解的配置-->
<context:annotation-config/>
- dao层。【
@Repository
】
@Repository
public interface UserDao {
...
}
- Service层。【
@Service
】
@Service
public interface UserService {
...
}
- Controller层。【
@Controller
】
@Controller
public class UserController {
}
注:
@Component, @Repository, @Service, @Controller
这四个注解的功能都是一样的, 都是代表将某个类注册到Spring中进行管理
8.6 对比小结
对比XML方式配置Spring和注解方式Spring
- XML配置更加万能,适用于任何场合!维护简单方便!
- 注解配置的话,不是自己类使用不了,维护相对复杂!
- 推荐:
- XML用来管理bean.
- 注解只负责完成属性的注入!
9. 使用Java的方式配置Spring
使用Java的方式配置Spring,即现在我们完全不使用Spring的XML配置文件了,而是将Spring的配置全权交给 Java来做 !
实体类
package com.carson.pojo;
import org.springframework.beans.factory.annotation.Value;
public class User {
@Value("Carson") //使用注解来进行属性值注入
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
Spring配置类
package com.carson.config;
import com.carson.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
//通过使用@Configuration注解代表这是一个Spring配置类,这个配置类等价于之前的beans.xml配置文件
//这个Spring配置类也会被Spring容器接管,从而注册到容器中,因为@Configuration这个注解包含了@Component
@Configuration
//使用@ComponentScan("com.carson.pojo")注解就是扫描包,其等价于<context:component-scan base-package="com.carson.pojo"/>
@ComponentScan("com.carson.pojo")
//使用@Import(xx.class)注解,可以导入其它的Spring配置类,其等价于import标签<import resource="beans.xml"/>
@Import(CarsonConfig2.class)
public class CarsonConfig {
//@Bean注解代表注册一个Bean,就相当于在容器中注册的bean标签
//@Bean注解只能放在方法的上面使用
//这个方法的名字,就相当于bean标签中的id属性
//这个方法的返回值, 就相当于bean标签中的class属性
//@Bean 基础声明
//Spring的@Bean注解用于告诉方法,产生一个Bean对象,然后这个Bean对象交给Spring管理。产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中。
//SpringIOC 容器管理一个或者多个bean,这些bean都需要在@Configuration注解下进行创建,在一个方法上使用@Bean注解就表明这个方法需要交给Spring进行管理。
@Bean
public User user(){
return new User(); //就是返回要注入到bean的对象
}
}
@Bean是一个方法级别上的注解,主要用在@Configuration注解的类里,也可以用在@Component注解的类里。添加的bean的id为方法名
`
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
这个配置就等同于之前在xml里的配置
<beans>
<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
测试类
public class MyTest {
public static void main(String[] args) {
//如果完全使用了配置类方式来配置Spring,我们就只能通过如下的 AnnotationConfig 来获取容器的上下文对象(通过传入配置类的Class对象)
ApplicationContext context = new AnnotationConfigApplicationContext(CarsonConfig.class);
User user = context.getBean("user", User.class);
System.out.println(user.toString());
}
}
这种纯Java的配置方式, 在SpringBoot中随处可见 !!
10. 代理模式
10.1 介绍
为什么要学习代理模式?
答: 其就是Spring AOP的底层原理!
代理模式的分类?
- 静态代理模式
- 动态代理模式
代理模式的图示, 以顾客租房为例:
代理模式的角色
- 抽象角色: 一般使用接口或者抽象类来实现。
- 真实角色: 被代理的角色
- 代理角色: 代理真实角色,一般会有一些附属的操作!
- 客户: 访问代理角色的人
10.2 静态代理模式
顾客租房的静态代理实现
- 接口
//抽象角色: 即代理角色和真实角色需共同实现的方法
//租房
public interface Rent {
//租房的方法
public void rent();
}
- 真实角色
//真实角色: 房东。需要实现代理角色即接口的方法
//房东要出租房
public class Host implements Rent {
public void rent() {
System.out.println("房东出租房子!");
}
}
- 代理角色
//代理角色: 需要实现抽象角色即接口的方法
//中介要代理出租房子
public class Proxy implements Rent {
//根据合成复用原则,不去继承类,而是内部实例化类
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
//中介帮房东出租房子
public void rent() {
/*中介还可以有其它附属操作*/
seeHouse();
signContract();
//代理房东出租房子
host.rent();
fee();
}
/*中介还可以有其它附属操作*/
//看房
public void seeHouse(){
System.out.println("中介带你看房!");
}
//签属租凭合同
public void signContract(){
System.out.println("签属租凭合同!");
}
//收取中介费
public void fee(){
System.out.println("中介收取中介费!");
}
}
- 客户端访问代理角色
//顾客要租房子
public class Client {
public static void main(String[] args) {
//真实角色(房东)
Host host = new Host();
//代理角色(代理房东出租房子,一般会有一些附属的操作)
Proxy proxy = new Proxy(host);
//面对中介租房
proxy.rent();
}
}
在MVC中控制层调用业务逻辑层的静态代理实现
需求: 现在需要在CRUD方法的每个方法上增加一个打印日志的log需求,但如果直接改动原有代码的话,成本和风险过高,故需通过代理模式来扩展代码而不是改动代码。
这种思想即: AOP(Aspect Oriented Program)思想。
具体代码如下:
- 接口
//抽象角色: 业务逻辑层的接口
//即 UserService
public interface UserService {
//CRUD方法
public void add();
public void del();
public void update();
public void query();
}
- 真实角色
//真实角色:UserServiceImpl
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("增加的方法!");
}
public void del() {
System.out.println("删除的方法!");
}
public void update() {
System.out.println("更改的方法!");
}
public void query() {
System.out.println("查询的方法!");
}
}
- 代理角色
//代理角色: UserServiceProxy
public class UserServiceProxy implements UserService {
//合成复用原则,要代理一个角色,进行内部实例化
private UserService userServiceImpl;
//Spring建议注入对象用Set方法而不是构造方法
public void setUserServiceImpl(UserService userServiceImpl) {
this.userServiceImpl = userServiceImpl;
}
public void add() {
log("add");
userServiceImpl.add();
}
public void del() {
log("del");
userServiceImpl.del();
}
public void update() {
log("update");
userServiceImpl.update();
}
public void query() {
log("query");
userServiceImpl.query();
}
//举例需要增加: 打印日志方法
public void log(String msg){
System.out.println("[Debug]: 使用了"+msg+"方法!");
}
}
- 客户端访问代理角色
//客户端访问代理角色
public class Client {
public static void main(String[] args) {
//真实角色
UserService userServiceImpl = new UserServiceImpl();
//代理角色
UserServiceProxy userServiceProxy = new UserServiceProxy();
//代理角色设置被代理的对象
userServiceProxy.setUserServiceImpl(userServiceImpl);
//客户端与代理角色交互
userServiceProxy.add();
}
}
静态代理模式的好处?
- 可以使真实角色的操作更加纯粹简单!不用去关注公共的业务。
- 公共的业务即附属的操作交给了代理角色!实现了业务的分工。
- 公共业务发生扩展的时候,方便集中管理!
静态代理模式的缺点?
一个真实角色就会产生一个代理角色,代码量增加,导致开发效率低下!
10.3 动态代理模式
动态代理模式?
- 动态代理和静态代理角色成分是一样的。
- 动态代理的代理类是动态生成的,而不是像静态代理是直接写好的!
- 动态代理的实现
- 基于接口的动态代理。 举例:–> JDK动态代理。
- 基于类的动态代理。 举例:–> cglib
- Java字节码实现。 举例:–> javassist
动态代理的好处?
- 解决了静态代理的缺点。
- 一个动态代理类代理的是一个接口,一般就是对应一个业务。
- 一个动态代理类可以代理多个类,只要是实现了同一个接口即可!
基于接口的动态代理实现
这里的动态代理需要涉及到反射的基础知识,可参考个人博文,
附上链接:反射机制
动态代理需要了解和运用两个类:
-
Proxy: 用来生成动态代理类的。
-
InvocationHandler: 即自动调用invoke()处理程序并返回结果的。
顾客租房的动态代理实现
- 动态代理类
//通过接口实现动态代理
//动态生成代理类的类,需要实现:InvocationHandler这个接口
//通过这个处理程序类来自动生成代理类实例
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Rent rent;
//Spring建议注入对象用Set方法而不是构造方法
public void setRent(Rent rent) {
this.rent = rent;
}
//生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
}
//处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质,就是利用反射机制实现的
//这里的method就是java.lang.reflection反射机制包下的对象
Object result = method.invoke(rent, args);
return result;
}
}
- 客户端访问动态生成的代理类
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//动态生成代理角色
// 1.实例化 实现动态生成代理类的类 的实例对象
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
// 2. 调用方法设置其需要代理的角色
proxyInvocationHandler.setRent(host);
// 3. 调用方法获得代理类(这里的proxy就是动态生成的代理类,需要进行对象类型的强制转换)
Rent proxy = (Rent) proxyInvocationHandler.getProxy();
//客户端访问代理类
proxy.rent();
}
}
在MVC中控制层调用业务逻辑层的动态代理实现
- 动态代理类
//通过接口实现动态代理
//动态生成代理类的类,需要实现:InvocationHandler这个接口
//通过这个处理程序类来自动生成代理类实例
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private UserService userService;
//Spring推荐使用Set方法
public void setUserService(UserService userService) {
this.userService = userService;
}
//生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),userService.getClass().getInterfaces(),this);
}
//处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//这里的method就是java.lang.reflection反射机制包下的对象
log(method.getName());
Object result = method.invoke(userService, args);
return result;
}
//增加打印日志
public void log(String msg){
System.out.println("[Debug]: 使用了"+msg+"方法!");
}
}
- 客户端访问动态生成的代理类
public class Client {
public static void main(String[] args) {
//真实角色
UserServiceImpl userService = new UserServiceImpl();
//动态生成代理角色
// 1.实例化 实现动态生成代理类的类 的实例对象
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
// 2. 调用方法设置其需要代理的角色
proxyInvocationHandler.setUserService(userService);
// 3. 调用方法获得代理类(这里的proxy就是动态生成的代理类,需要进行对象类型的强制转换)
UserService proxy = (UserService) proxyInvocationHandler.getProxy();
//客户端访问代理类
proxy.del();
}
}
封装动态代理类成工具类
动态代理类,接收Object
//通过接口实现动态代理
//动态生成代理类的类,需要实现:InvocationHandler这个接口
//通过这个处理程序类来自动生成代理类实例
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口【即: 代理中介实现的接口】
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成得到代理类【即:动态生成代理类】
//方法的参数(ClassLoader loader,@NotNull Class<?>[] interfaces,@NotNull reflect.InvocationHandler h)
// loader:用哪个类加载器去加载代理对象
// interfaces: 动态代理类需要实现的接口(通过反射机制获取)
// h: 动态代理方法在执行时,会调用h里面的invoke()方法去执行
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
//处理代理实例,并返回结果【即:这个方法会被自动调用,故需要执行的代码需要放在这个方法里面】
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质,就是利用反射机制实现的
Object result = method.invoke(target,args);
return result;
}
}
Client
public class Client {
public static void main(String[] args) {
//真实角色
UserServiceImpl userServiceImpl = new UserServiceImpl();
//动态生成代理角色
// 1.实例化 实现动态生成代理类的类 的实例对象
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
// 2. 调用方法设置其需要代理的角色
proxyInvocationHandler.setTarget(userServiceImpl);
// 3. 调用方法获得代理类(这里的proxy就是动态生成的代理类,需要进行对象类型的强制转换)
UserService proxy = (UserService) proxyInvocationHandler.getProxy();
//客户端访问代理类
proxy.del();
}
}
11. AOP
11.1 什么是AOP
-
AOP是OOP(Object Oriented Programming) 的延续, 是软件开发中的一个热点,也是Spring框架中的一个重要内容。
-
AOP(Aspect Oriented Programming) 意为: 面向切面编程。
-
AOP:是一种横向编程思想。 其可以在不影响原来的业务逻辑的情况下,实现业务逻辑的增强。AOP是一个实现代码解耦的利器。
-
AOP 使得在 不改变原代码功能流程的基础上去加入新的功能,其实就是一种 扩展性 的表现,其 底层基本原理就是上面讲的"动态代理"技术,跟设计模式里"代理模式"的思想是一样的。
-
利用AOP可以对业务逻辑的各个部分进行隔离, 从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,提高开发效率。
11.2 AOP在Spring中的作用
允许用户自定义切面。
- 横切关注点: 跨越了应用程序多个模块的方法或功能。即是,与我们已实现的业务逻辑无关的,但是需要我们关注的部分。如: 日志,安全,缓存,事务等等。
- 切面(Aspect): 横切关注点 被模块化了的特殊对象。即,将横切关注点作为一个类。
- 通知(Advice): 切面必须要完成的工作。即,它是切面类中的一个方法。
- 目标(Target): 被通知的对象。
- 代理(Proxy): 向目标对象应用通知之后创建的对象。
- 切入点(PointCut): 切面通知 执行的 “地点” 的定义。
- 连接点(Joint): 与切入点匹配的执行点。
SpringAOP中, 通过通知(Advice)即方法定义横切逻辑, Spring中支持5种类型的通知(Advice):
通知类型 | 连接点 | 对应需要实现的Spring AOP中的接口 |
---|---|---|
前置通知 | 方法前 | org.springframework.aop.MethodBeforeAdvice |
后置通知 | 方法后 | org.springframework.aop.AfterReturningAdvice |
环绕通知 | 方法前后 | org.aopalliance.intercept.MethodInterceptor |
异常抛出通知 | 方法抛出异常 | org.springframework.aop.ThrowAdvice |
引介通知 | 类中增加新的方法属性 | org.springframework.aop.IntroductionInterceptor |
AOP在不改变原有代码的情况下,去增加新的功能。
11.3 使用Spring实现AOP
【重点】使用AOP织入,需要先导入一个依赖包!
<!--使用Spring AOP 必须引入的依赖 aop织入-->
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
实现AOP的方式
方式一: 使用Spring的API接口 来实现AOP
- 被代理的接口(抽象角色)
UserService.java
public interface UserService {
//增删改查方法
public void add();
public void del();
public void update();
public void select();
}
- 真实角色(即原来的代码,原来的已存在的业务逻辑,是不能改动的)
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("增加的方法!");
}
public void del() {
System.out.println("删除的方法!");
}
public void update() {
System.out.println("更改的方法!");
}
public void select() {
System.out.println("查询的方法!");
}
}
- 待增加的方法功能即Advice,需要各作为一个单独的类
Log.java,在已存在的原来的方法之前打印日志,即前置日志
//在我们的方法执行之前执行
//需要继承的Spring的API接口:MethodBeforeAdvice
public class Log implements MethodBeforeAdvice {
//重写方法(这个方法会在我们的执行方法之前自动调用)
//method: 要执行的目标对象的方法
//args: 参数
//target: 目标对象
public void before(Method method, Object[] args, Object target) throws Throwable {
//这个方法专注于执行我们方法之前需要干什么
System.out.println(target.getClass().getName()+"的"+method.getName()+"方法被执行了!");
}
}
AfterLog.java,在原来已存在的方法之后打印日志,即后置日志
//在我们的方法执行之后执行
//需要继承的Spring的API接口:AfterReturningAdvice或者 AfterAdvice
public class AfterLog implements AfterReturningAdvice {
//重写方法(这个方法会在我们执行的方法之后去执行,且这个方法可以接收返回值)
//returnValue: 返回值
//method: 要执行的目标对象的方法
//args: 参数
//target: 目标对象
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了"+method.getName()+"方法,返回结果为:"+returnValue);
}
}
- 配置文件(代理角色的配置,即代理角色的实现交给Spring AOP)
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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
">
<!--注册bean对象-->
<bean id="userServiceImpl" class="com.carson.service.UserServiceImpl"></bean>
<bean id="log" class="com.carson.log.Log"></bean>
<bean id="afterLog" class="com.carson.log.AfterLog"></bean>
<!--配置aop: 需要在文件的最上方先导入aop的相关约束-->
<!--方式一: 使用原生Spring API接口 实现AOP-->
<aop:config>
<!--1. 定义切入点 (即配置要给哪个类插入一些方法)
其中expression格式: execution(修饰词 返回值 类名 方法名 参数) *符号代表任意, 而.. 代表可以有任意个参数
execution()中填写代表的是: 要执行的位置!
这里按照方法定义顺序考虑即可: 即 execution(返回值 类全路径.方法名(参数))
-->
<aop:pointcut id="pointcut" expression="execution(* com.carson.service.UserServiceImpl.*(..))"/>
<!--2.通知对应到切入点 (将advice-ref中对应的方法类对应到pointcut-ref代表的切入点的对应的方法中)-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
- 测试类
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//注意点: 动态代理代理的是接口(故下面必须要返回接口,即返回代理角色)
UserService userService = context.getBean("userServiceImpl", UserService.class);
//客户端访问代理角色
userService.select();
}
}
方式二: 使用自定义类 来实现AOP【推荐】
- 被代理的接口(抽象角色)
UserService.java
public interface UserService {
//增删改查方法
public void add();
public void del();
public void update();
public void select();
}
- 真实角色(即原来的代码,原来的已存在的业务逻辑,是不能改动的)
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("增加的方法!");
}
public void del() {
System.out.println("删除的方法!");
}
public void update() {
System.out.println("更改的方法!");
}
public void select() {
System.out.println("查询的方法!");
}
}
- 自定义的切面类(里面书写待增加的新的功能方法)
//自定义类,作为切面
public class DiyPointCut {
public void before(){
System.out.println("方法执行前!!");
}
public void after(){
System.out.println("方法执行后");
}
}
- 配置文件(代理角色的配置,即代理角色的实现交给Spring AOP)
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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
">
<!--注册bean对象-->
<bean id="userServiceImpl" class="com.carson.service.UserServiceImpl"></bean>
<bean id="diy" class="com.carson.diy.DiyPointCut"></bean>
<!--方式二:使用自定义类实现AOP-->
<aop:config>
<!--自定义切面,其中 ref:代表要引用的类-->
<aop:aspect ref="diy">
<!--1.设置切入点 -->
<aop:pointcut id="pointcut" expression="execution(* com.carson.service.UserServiceImpl.*(..))"/>
<!--2.配置通知到切入点,新增的方法和切入点的对应-->
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>
- 测试类
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//注意点: 动态代理代理的是接口(故下面必须要返回接口,即返回代理角色)
UserService userService = context.getBean("userServiceImpl", UserService.class);
//客户端访问代理角色
userService.select();
}
}
方式三: 使用注解实现AOP
- 被代理的接口(抽象角色)
UserService.java
public interface UserService {
//增删改查方法
public void add();
public void del();
public void update();
public void select();
}
- 真实角色(即原来的代码,原来的已存在的业务逻辑,是不能改动的)
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("增加的方法!");
}
public void del() {
System.out.println("删除的方法!");
}
public void update() {
System.out.println("更改的方法!");
}
public void select() {
System.out.println("查询的方法!");
}
}
- 使用AOP注解的切面类
//@Aspect注解标注这个类是一个切面
@Aspect
public class AnnotationPointCut {
//@Before注解的内容就是新增的方法的切入点
@Before("execution(* com.carson.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("=====方法执行前=====");
}
//@After注解的内容就是新增的方法的切入点
@After("execution(* com.carson.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("=====方法执行后======");
}
//@Around即环绕通知
//在环绕中,我们可以给方法加一个参数,代表处理的切入的点
@Around("execution(* com.carson.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
//执行方法,其相当于过滤器的doFilter()
Object proceed = jp.proceed();
System.out.println("环绕后");
}
}
- 配置文件(代理角色的配置,即代理角色的实现交给Spring AOP)
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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
">
<!--注册bean对象-->
<bean id="userServiceImpl" class="com.carson.service.UserServiceImpl"></bean>
<bean id="annotationPointCut" class="com.carson.diy.AnnotationPointCut"></bean>
<!--方式三: 使用注解实现AOP-->
<!--开启AOP注解支持 proxy-target-class参数可以配置动态代理的实现: JDK(默认,对应false) cglib(对应true)-->
<aop:aspectj-autoproxy proxy-target-class="false"/>
</beans>
- 测试类
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//注意点: 动态代理代理的是接口(故下面必须要返回接口,即返回代理角色)
UserService userService = context.getBean("userServiceImpl", UserService.class);
//客户端访问代理角色
userService.select();
}
}
12. 整合Mybatis
12.1 回顾Mybatis基本使用
- pojo目录下编写实体类。
User.java
public class User {
private int id;
private String name;
private String pwd;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
- resources目录下编写mybatis核心配置文件。
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration核心配置文件-->
<configuration>
<!--别名管理: 通过扫描包方式设置别名-->
<typeAliases>
<!--name属性对应包的位置-->
<package name="com.carson.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/> <!--事务管理是JDBC-->
<dataSource type="POOLED">
<!--更换连接数据库的各个属性值-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<!--注:这里的&连接符要用&进行表示,& 是 HTML 中 & 的表示方法-->
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--每一个Mapper.xml都需要在mybatis的核心配置文件中注册-->
<mappers>
<!--路径要用/进行分割-->
<mapper resource="com/carson/mapper/UserMapper.xml"/>
</mappers>
</configuration>
- mapper目录下编写接口文件
UserMapper.java
public interface UserMapper {
public List<User> getUsers();
}
- mapper目录下编写接口对应的Mapper.xml(mybatis中每一个接口都要对应一个xml文件)
【注意:每一个Mapper.xml都需要在mybatis的核心配置文件中注册 ,这里已在上面注册】
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace要对应接口文件的全路径-->
<mapper namespace="com.carson.mapper.UserMapper">
<select id="getUsers" resultType="user">
select * from user;
</select>
</mapper>
- 测试
public class MyTest {
public static void main(String[] args) throws IOException {
//读取mybatis-config配置文件
String resources = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resources);
//创建sqlSessionFactory实例对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//创建sqlSession对象,其相当于Connection数据库连接对象,参数为true代表自动提交事务
SqlSession sqlSession = sqlSessionFactory.openSession(true);
try{
//填入接口类的class对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//利用对象调用方法
List<User> users = mapper.getUsers();
//打印测试
for(User user:users){
System.out.println(user);
}
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭sqlSession
sqlSession.close();
}
}
}
12.2 Spring整合Mybatis
附上Spring整合mybatis的官网学习文档链接:mybatis-spring
注意: 各个依赖版本之间有一定的依赖关系,如下:
spring和mybatis整合使用前需要在pom.xml文件中导入相关依赖和进行配置,如下:
pom.xml
<?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>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Spring-10-mybatis</artifactId>
<!--导入相关依赖-->
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!--导入spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<!--Spring操作数据库的话,还需要一个spring-jdbc的依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<!--Spring AOP的织入包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
<!--mybatis和spring整合的包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
</dependencies>
<!--解决资源不放在resources目录下的导致的资源导出失败问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>
整合mybatis方式一
- pojo目录下编写测试的实体类。
User.java
public class User {
private int id;
private String name;
private String pwd;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
- mapper目录下编写接口文件
usermapper.java
public interface UserMapper {
public List<User> getUsers();
}
- mapper目录下编写接口对应的Mapper.xml(mybatis中每一个接口都要对应一个xml文件)
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace要对应接口文件的全路径-->
<mapper namespace="com.carson.mapper.UserMapper">
<select id="getUsers" resultType="user">
select * from user;
</select>
</mapper>
- resources目录下编写mybatis核心配置文件。
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--由于spring配置文件中可以进行相关的mybatis设置,故原先的mybatis核心配置文件只保留部分设置-->
<!--一般在mybatis的配置文件只留下两项配置: 即别名管理和设置项-->
<!--configuration标签是核心配置文件-->
<configuration>
<!--1.别名管理: 通过扫描包方式设置别名-->
<typeAliases>
<!--name属性对应包的位置-->
<package name="com.carson.pojo"/>
</typeAliases>
<!--2.settings标签是对mybatis的相关设置-->
<!--<settings>
<setting name="" value=""/>
</settings>-->
</configuration>
- resources目录下建立spring配置文件
spring-dao.xml
在spring-dao.xml
中做如下几个配置:
注: 由于此类的相关内容是固定写死的, 故可直接作为连接数据库的工具类使用!
-
数据源配置
-
sqlSessionFactory
-
sqlSessionTemplate
spring-dao.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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--1 DataSource:使用spring-jdbc依赖提供的数据源类替换Mybatis原先数据源类-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!--类中注入连接数据库的相关参数属性值-->
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--2.sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--注入dataSource属性值-->
<property name="dataSource" ref="dataSource"/>
<!--绑定Mybatis配置文件 也可以不绑定 使用property标签去替代mybatis配置文件中的配置
如果绑定了mybatis的配置文件,那么mybatis的配置文件和spring的配置文件就连接起来了-->
<!--注: 下面标签的value值的固定形式: classpath:文件路径。(如果文件位于资源目录下,则直接写文件名即可)-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--由于每一个Mapper.xml都需要在mybatis的核心配置文件中注册,下面等价于注册各个Mapper.xml-->
<!-- 注意:classpath:后的文件路径要用/进行分割-->
<property name="mapperLocations" value="classpath:com/carson/mapper/*.xml"/>
</bean>
<!-- 3. SqlSessionTemplate-->
<!--这里的SqlSessionTemplate:就是我们之前mybatis中使用的sqlSession-->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<!--只能使用有参构造器方式注入sqlSessionFactory,因为SqlSessionTemplate中没有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
</beans>
- 需要给对应接口添加对应的实现类
UserMapperImpl.java
UserMapperImpl.java
//现在要多写一个对应的接口实现类
public class UserMapperImpl implements UserMapper {
//我们所有的操作,都用sqlSessionTemplate来执行,其就相当于之前mybatis中的sqlSession
private SqlSessionTemplate sqlSessionTemplate;
//来个set方法,方便在spring配置文件中注入
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}
public List<User> getUsers() {
//这里的getMapper和方法调用和之前mybatis中的使用相同
UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class);
return mapper.getUsers();
}
}
- resources目录下新建一个spring的总的配置文件
applicationContext.xml
- 在其中导入之前先创建的spring配置文件
spring-dao.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" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--导入之前创建的spring-dao.xml-->
<import resource="spring-dao.xml"/>
<!--注册接口的实现类 -->
<bean id="userMapperImpl" class="com.carson.mapper.UserMapperImpl">
<property name="sqlSessionTemplate" ref="sqlSessionTemplate"/>
</bean>
</beans>
- 测试
public class MyTest2 {
public static void main(String[] args) {
//读取总的spring配置文件,创建上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//getBean()来获取接口的实现类的对象
UserMapperImpl userMapperImpl = context.getBean("userMapperImpl", UserMapperImpl.class);
//调用实现类对象的方法
List<User> users = userMapperImpl.getUsers();
//打印输出
for(User user:users){
System.out.println(user);
}
}
}
整合mybatis方式二
注:
- 方式二通过继承SqlSessionDaoSupport类,再调用其getSqlSession()方法获取sqlSession对象。
- 故spring-dao.xml配置文件中可省去对 sqlSessionTemplate的配置!
- pojo目录下编写测试的实体类。
User.java
public class User {
private int id;
private String name;
private String pwd;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
- mapper目录下编写接口文件
usermapper.java
public interface UserMapper {
public List<User> getUsers();
}
- mapper目录下编写接口对应的Mapper.xml(mybatis中每一个接口都要对应一个xml文件)
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace要对应接口文件的全路径-->
<mapper namespace="com.carson.mapper.UserMapper">
<select id="getUsers" resultType="user">
select * from user;
</select>
</mapper>
- resources目录下编写mybatis核心配置文件。
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--由于spring配置文件中可以进行相关的mybatis设置,故原先的mybatis核心配置文件只保留部分设置-->
<!--一般在mybatis的配置文件只留下两项配置: 即别名管理和设置项-->
<!--configuration标签是核心配置文件-->
<configuration>
<!--1.别名管理: 通过扫描包方式设置别名-->
<typeAliases>
<!--name属性对应包的位置-->
<package name="com.carson.pojo"/>
</typeAliases>
<!--2.settings标签是对mybatis的相关设置-->
<!--<settings>
<setting name="" value=""/>
</settings>-->
</configuration>
- resources目录下建立spring配置文件
spring-dao.xml
在spring-dao.xml
中做如下几个配置:
注: 方式二不用在配置文件中配置sqlSessionTemplate对象了!
-
数据源配置
-
sqlSessionFactory
spring-dao.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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--1 DataSource:使用spring-jdbc依赖提供的数据源类替换Mybatis原先数据源类-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!--类中注入连接数据库的相关参数属性值-->
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--2.sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--注入dataSource属性值-->
<property name="dataSource" ref="dataSource"/>
<!--绑定Mybatis配置文件 也可以不绑定 使用property标签去替代mybatis配置文件中的配置
如果绑定了mybatis的配置文件,那么mybatis的配置文件和spring的配置文件就连接起来了-->
<!--注: 下面标签的value值的固定形式: classpath:文件路径。(如果文件位于资源目录下,则直接写文件名即可)-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--由于每一个Mapper.xml都需要在mybatis的核心配置文件中注册,下面等价于注册各个Mapper.xml-->
<!-- 注意:classpath:后的文件路径要用/进行分割-->
<property name="mapperLocations" value="classpath:com/carson/mapper/*.xml"/>
</bean>
</beans>
- 需要给对应接口添加对应的实现类
UserMapperImpl2.java
UserMapperImpl2.java
//现在要多写一个对应的接口实现类
//方式二需要继承SqlSessionDaoSupport类
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{
public List<User> getUsers() {
//通过SqlSessionDaoSupport类中的getSqlSession()获取sqlSession对象
return getSqlSession().getMapper(UserMapper.class).getUsers();
}
}
- resources目录下新建一个spring的总的配置文件
applicationContext.xml
- 在其中导入之前先创建的spring配置文件
spring-dao.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" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--导入之前创建的spring-dao.xml-->
<import resource="spring-dao.xml"/>
<!--注册接口的实现类 -->
<bean id="userMapperImpl2" class="com.carson.mapper.UserMapperImpl2">
<!--注入sqlSessionFactory-->
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
</beans>
- 测试
public class MyTest3 {
public static void main(String[] args) {
//读取总的spring配置文件,创建上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//getBean()来获取接口的实现类的对象
UserMapper userMapper = context.getBean("userMapperImpl2", UserMapper.class);
//调用实现类对象的方法
List<User> users = userMapper.getUsers();
//打印输出
for(User user:users){
System.out.println(user);
}
}
}
13. 声明式事务
为什么需要配置事务管理?
- 如果不配置事务,可能存在数据提交不一致的情况!
- 如果不在Spring中去配置声明式事务,我们就需要在原代码中手动配置事务管理。
- 事务在项目的开发中十分重要,涉及到数据的一致性和完整性等问题!
spring中的事务管理方式:
- 声明式事务(重点): 事务结合AOP的应用,交由Spring容器管理事务。
- 一般情况下比编程式事务好用。
- 将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
- 将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式的事务管理。
- 编程式事务: 事务需要在原Java代码中手动进行管理。
- 将事务管理代码嵌到业务方法中来控制事务的提交和回滚
- 缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码
由于需要操作数据库,pom.xml中需要导入的相关依赖和相关配置,如下:
pom.xml
<?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>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Spring-11-transaction</artifactId>
<!--导入相关依赖-->
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!--导入spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<!--Spring操作数据库的话,还需要一个spring-jdbc的依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<!--Spring AOP的织入包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
<!--mybatis和spring整合的包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
</dependencies>
<!--解决资源不放在resources目录下的导致的资源导出失败问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>
未进行事务管理应用示例
示例步骤:
- pojo目录下编写测试的实体类。
User.java
public class User {
private int id;
private String name;
private String pwd;
public User() {
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
- mapper目录下编写接口文件
新增两个方法,增加和删除用户!
usermapper.java
public interface UserMapper {
//查询
public List<User> getUsers();
//增加
public int insertUser(User user);
//删除(简单数据类型要加 @param())
public int delUser(@Param("id") int id);
}
- mapper目录下编写接口对应的Mapper.xml(mybatis中每一个接口都要对应一个xml文件)
mapper文件,我们故意把 delete删除的sql语句 写错,方便测试!
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.carson.mapper.UserMapper">
<select id="getUsers" resultType="user">
select * from user;
</select>
<!--id对应方法名-->
<insert id="addUser" parameterType="user">
insert into user values(#{id},#{name},#{pwd});
</insert>
<!--这里故意将删除的sql语句写错,给delete加个s-->
<delete id="delUser" parameterType="int">
deletes from user where id=#{id};
</delete>
</mapper>
- resources目录下编写mybatis核心配置文件。
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--由于spring配置文件中可以进行相关的mybatis设置,故原先的mybatis核心配置文件只保留部分设置-->
<!--一般在mybatis的配置文件只留下两项配置: 即别名管理和设置项-->
<!--configuration标签是核心配置文件-->
<configuration>
<!--1.别名管理: 通过扫描包方式设置别名-->
<typeAliases>
<!--name属性对应包的位置-->
<package name="com.carson.pojo"/>
</typeAliases>
<!--2.settings标签是对mybatis的相关设置-->
<!--<settings>
<setting name="" value=""/>
</settings>-->
</configuration>
- resources目录下的spring配置文件
spring-dao.xml
中增加事务管理的配置
使用Spring管理事务,注意头文件的约束导入 : tx
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
spring-dao.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:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd
">
<!--DataSource:使用spring-jdbc依赖提供的数据源类替换Mybatis原先的数据源类-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!--类中注入连接数据库的相关参数属性值-->
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--注入dataSource属性值-->
<property name="dataSource" ref="dataSource"/>
<!--绑定Mybatis配置文件 也可以不绑定 使用property标签去替代mybatis配置文件中的配置
如果绑定了mybatis的配置文件,那么mybatis的配置文件和spring的配置文件就连接起来了-->
<!--注: 下面标签的value值的固定形式: classpath:文件路径。(如果文件位于资源目录下,则直接写文件名即可)(且classpath:和文件路径之间不能有空格,否则会出错)-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--由于每一个Mapper.xml都需要在mybatis的核心配置文件中注册,下面等价于注册各个Mapper.xml-->
<!-- 注意:classpath:后的文件路径要用/进行分割-->
<property name="mapperLocations" value="classpath:com/carson/mapper/*.xml"/>
</bean>
<!--这里的SqlSessionTemplate:就是我们之前mybatis中使用的sqlSession-->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<!--只能使用有参构造器方式注入sqlSessionFactory,因为SqlSessionTemplate中没有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
</beans>
- 需要给对应接口添加对应的实现类
UserMapperImpl.java
在查询User方法中间加上增加和删除User的相关方法,这里示例增加9号用户然后删除5号用户!
UserMapperImpl.java
//现在要多写一个对应的接口实现类
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{
public List<User> getUsers() {
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
//添加测试代码(在查询User中加上增加和删除User的相关方法)
User user = new User(9, "增加的小王", "1212112");
addUser(user);//增加一个用户
delUser(5);//删除5号用户
return mapper.getUsers();//返回信息
}
public int addUser(User user) {
return getSqlSession().getMapper(UserMapper.class).addUser(user);
}
public int delUser(int id) {
return getSqlSession().getMapper(UserMapper.class).delUser(id);
}
}
- resources目录下新建一个spring的总的配置文件
applicationContext.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" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--导入spring-dao.xml-->
<import resource="spring-dao.xml"/>
<!--注册接口的实现类 -->
<bean id="userMapperImpl" class="com.carson.mapper.UserMapperImpl">
<!--注入sqlSessionFactory-->
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
</beans>
- 测试
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapperImpl", UserMapper.class);
List<User> users = userMapper.getUsers();
for(User user:users){
System.out.println(user);
}
}
结果:
意料之中因为delete语句多写个s的报错:
由于插入用户方法在前,故虽然删除用户报错,但9号用户成功插入!
虽然报错了,但是9号用户的也成功插入了,这就影响了数据的一致性和完整性!
为此,我们需要引入事务管理,满足事务的原子性要求!即要么都执行成功,要么失败回滚!
Spring给我们提供了事务管理,我们只需要配置即可!
声明式事务应用示例
删除前面增加的9号用户,重新准备进行测试。
示例步骤:
- pojo目录下编写测试的实体类。
User.java
public class User {
private int id;
private String name;
private String pwd;
public User() {
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
- mapper目录下编写接口文件
同样还是新增两个方法,增加和删除用户!
usermapper.java
public interface UserMapper {
//查询
public List<User> getUsers();
//增加
public int insertUser(User user);
//删除(简单数据类型要加 @param())
public int delUser(@Param("id") int id);
}
- mapper目录下编写接口对应的Mapper.xml(mybatis中每一个接口都要对应一个xml文件)
mapper文件,我们这里还是故意把 delete删除的sql语句 写错,方便测试!
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.carson.mapper.UserMapper">
<select id="getUsers" resultType="user">
select * from user;
</select>
<!--id对应方法名-->
<insert id="addUser" parameterType="user">
insert into user values(#{id},#{name},#{pwd});
</insert>
<!--这里故意将delete语句写错,多加了个s-->
<delete id="delUser" parameterType="int">
deletes from user where id=#{id};
</delete>
</mapper>
- resources目录下编写mybatis核心配置文件。
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--由于spring配置文件中可以进行相关的mybatis设置,故原先的mybatis核心配置文件只保留部分设置-->
<!--一般在mybatis的配置文件只留下两项配置: 即别名管理和设置项-->
<!--configuration标签是核心配置文件-->
<configuration>
<!--1.别名管理: 通过扫描包方式设置别名-->
<typeAliases>
<!--name属性对应包的位置-->
<package name="com.carson.pojo"/>
</typeAliases>
<!--2.settings标签是对mybatis的相关设置-->
<!--<settings>
<setting name="" value=""/>
</settings>-->
</configuration>
- resources目录下的spring配置文件
spring-dao.xml
中增加事务管理的配置
使用Spring管理事务,注意头文件的约束导入 : tx
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
spring-dao.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:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd
">
<!--DataSource:使用spring-jdbc依赖提供的数据源类替换Mybatis原先的数据源类-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!--类中注入连接数据库的相关参数属性值-->
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--注入dataSource属性值-->
<property name="dataSource" ref="dataSource"/>
<!--绑定Mybatis配置文件 也可以不绑定 使用property标签去替代mybatis配置文件中的配置
如果绑定了mybatis的配置文件,那么mybatis的配置文件和spring的配置文件就连接起来了-->
<!--注: 下面标签的value值的固定形式: classpath:文件路径。(如果文件位于资源目录下,则直接写文件名即可)(且classpath:和文件路径之间不能有空格,否则会出错)-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--由于每一个Mapper.xml都需要在mybatis的核心配置文件中注册,下面等价于注册各个Mapper.xml-->
<!-- 注意:classpath:后的文件路径要用/进行分割-->
<property name="mapperLocations" value="classpath:com/carson/mapper/*.xml"/>
</bean>
<!--这里的SqlSessionTemplate:就是我们之前mybatis中使用的sqlSession-->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<!--只能使用有参构造器方式注入sqlSessionFactory,因为SqlSessionTemplate中没有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<!--配置声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入dataSource数据源对象,可以通过有参构造器注入,也可set方法注入-->
<!--<constructor-arg ref="dataSource"/>-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--结合AOP实现事务的织入-->
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--给哪些方法配置事务 并配置事务的传播特性(表示这些方法要怎么使用事务),一般为默认的REQUIRED,其适用于绝大多数的情况-->
<tx:attributes>
<!--其中*为通配符,匹配所有的方法-->
<tx:method name="query" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--AOP配置:将事务切入-->
<aop:config>
<!--1.设置切入点-->
<aop:pointcut id="txPointCut" expression="execution(* com.carson.mapper.*.*(..))"/>
<!--2.配置Advice(通知)到切入点-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
</beans>
- 需要给对应接口添加对应的实现类
UserMapperImpl.java
在查询User方法中间加上增加和删除User的相关方法,示例增加9号用户然后删除5号用户!
UserMapperImpl.java
//现在要多写一个对应的接口实现类
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{
public List<User> getUsers() {
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
//添加测试代码(在查询User中加上增加和删除User的相关方法)
User user = new User(9, "增加的小王", "1212112");
addUser(user);//增加一个用户
delUser(5);//删除5号用户
return mapper.getUsers();//返回信息
}
public int addUser(User user) {
return getSqlSession().getMapper(UserMapper.class).addUser(user);
}
public int delUser(int id) {
return getSqlSession().getMapper(UserMapper.class).delUser(id);
}
}
- resources目录下新建一个spring的总的配置文件
applicationContext.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" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--导入spring-dao.xml-->
<import resource="spring-dao.xml"/>
<!--注册接口的实现类 -->
<bean id="userMapperImpl" class="com.carson.mapper.UserMapperImpl">
<!--注入sqlSessionFactory-->
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
</beans>
- 测试
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapperImpl", UserMapper.class);
List<User> users = userMapper.getUsers();
for(User user:users){
System.out.println(user);
}
}
结果:
意料之中的报错:
但发现,由于增加了事务管理,数据表中并没有像之前一样增加了9号用户,还是保持原样!
而这就是我们通过spring引入事务管理后想看到的!
欢迎关注个人公众号,回复“Spring”,获取本文所有的完整测试代码!