Spring

概述及IoC理论推导

Spring简介

Spring:春天——->给软件行业带来了春天

2002年,Rod Jahnson首次推出了Spring框架雏形interface21框架。

2004年3月24日,Spring框架以interface21框架为基础,经过重新设计,发布了1.0正式版本。

很难想象Rod Jahnson的学历,他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学。

Spring理念:使现有技术更加实用。本身就是一个大杂烩,整合现有的框架技术

官网:htttp://spring.io/

官方下载地址:https://repo.spring.io/libs-release-local/org/springframework/spring/

Guthub:https://github.com/spring-projects

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

Spring的优点

  1. Spring是一个开源免费的框架,容器。
  2. Spring是一个轻量级的框架,非入侵的。
  3. 控制反转IoC,面向切面编程AOP
  4. 对事物的支持,对框架的支持

。。。。。。

一句话概括:

Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架)。

Spring组成

Spring的体系结构

Spring框架是一个分层的架构,由7个定义良好的模块组成。Spring模块构建在核心容器之上,核心容器定义了创建、配置和管理Bean的方式。

img

组成Spring框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:

  • 核心容器:核心容器提供Spring框架的基本功能。核心容器的主要组件是BeanFactory,他是工厂模式的实现。BeanFactory使用控制反转(IOC)模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
  • Spring上下文:Spring上下文是一个配置文件,向Spring框架提供上下文信息。Spring上下文包括企业服务,例如JNDI、EJB、电子邮件、国际化、校验和调度功能。
  • Spring AOP:JDBC DAO抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误信息 。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向JDBC的异常遵从通用的DAO异常层次结构。
  • Spring ORM:Spring框架插入了若干个ORM框架,从而提供了ORM的对象关系工具,其中包括JDO、Hibernate和iBatis SQL Map。所有这些都遵从Spring的同用事务和DAO异常层次结构。
  • Spring Web 模块:Web上下文模块建立在应用程序上下文模块之上,为基于Web的应用程序提供了上下文。所以,Spring框架支持与Jakarta Struts的集成。Web模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
  • Spring MVC框架:MVC框架是一个全功能构建Web应用程序的MVC实现。通过策略接口,MVC框架变成为高度可配置的,MVC容纳了大量视图技术,其中包括JSP、Velocity、Tiles、iText和POI。

Spring拓展

Spring Boot和Spring Cloud

  • Spring Boot是Spring的一套快速配置脚手架,可以基于Spring Boot快速开发单个微服务;
  • Spring Cloud是基于Spring Boot实现的;
  • Spring Boot专注于快速、方便集成的单个微服务个体,Spring Cloud关注全局的服务治理框架;
  • Spring Boot使用了约束由于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置,Spring Cloud很大的一部分是基于Spring Boot来实现,Spring Boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring Boot,属于依赖关系。
  • Spring Boot在Spring Cloud中起到了承上启下的作用,如果要学习Spring Cloud必须要学习Sping Boot。

img

弊端:发展了太久之后,违背了原来的理念!配置十分繁琐,人称:“配置地狱!”

IOC基础

新建一个空白的maven项目

分析实现

用原来的方式写一段代码

  1. 先写一个UserDao接口
1
2
3
public interface UserDao{
public void getUser();
}
  1. 再去写Dao的实现类
1
2
3
4
5
6
public class UserDaoImpl implements UserDao{
@Override
public void getUser(){
System.out.println("获取用户数据");
}
}
  1. 然后去写UserService的接口
1
2
3
public interface UserService{
public void getUser();
}
  1. 最后写Service的实现类
1
2
3
4
5
6
7
8
public class UserServiceImpl implements UserService{
private UserDao userDao = new UserDaoImpl();

@Override
public void getUser(){
userDao.getUser();
}
}
  1. 测试一下
1
2
3
4
5
@Test
public void test(){
UserService service = new UserServiceImpl();
service.getUser();
}

这是我们原来的方式,开始大家也是这么写的,那我们现在来修改一下,把Userdao的实现类增加一个。

1
2
3
4
5
6
public class UserDaoMySqlImpl implements UserDao{
@Override
public void getUser(){
System.out.println("MySql获取用户数据")
}
}

紧接着我们要去使用Mysql的话,我们就需要去service实现类里面修改对应的方法

1
2
3
4
5
6
7
public class UserServiceImpl implements UserService{
private UserDao userDao = new UserDaoMySqlImpl():
@Override
public void getUser(){
userDao.getUser();
}
}

再假设,我们再增加一个userdao的实现类。

1
2
3
4
5
6
public class UserDaoOracleImpl implements UserDao{
@Override
public void getUser(){
System.out.println("Orcle获取用户数据");
}
}

那么我们要使用Oracle,又需要去service实现类里面修改对应的实现。假设我们的这种需求非常大,这种方法就根本 不适用了,甚至反人类了,每次变动,都需要修改大量的代码。这种设计的耦合性太高了,牵一发而动全身。

那我们如何去解决呢?

我们可以在需要用到他的地方,不去实现它,而是留出一个接口,利用set,我们去代码里面修改一下。

1
2
3
4
5
6
7
8
9
10
11
public class UserServiceImpl implements UserService {
private UserDao userDao;
//利用set实现
public void setUserDao(UserDao userDao){
this.userDao = userDao;
}
@Override
public void getUser(){
userDao.getUser();
}
}

现在我们去测试类里,进行测试;

1
2
3
4
5
6
@Test
public void test(){
UserServiceImpl service = new UserServiceImpl;
service.setUserDao(new UserDaoOracleimpl());
service.getUser();
}

大家发现区别了没有?可能很多人说没啥区别。但是,他们已经发生了根本性的变化,很多地方都不一样了。仔细去思考一下,以前所有东西都是由程序去进行控制创建,而现在由我们自行控制创建对象,把主动权交给了调用者。程序不用去管怎么创建,怎么实现了。他只负责提供一个接口。

这种思想,从本质上解决了问题,我们把程序们不用再去管理对象的创建了,更多的去关注业务的实现。耦合性大大降低。这也就是IOC的原型!

image-20210718190344315

IOC本质

控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓的控制反转就是:获得依赖对象的方式反转了。

img

Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。

image-20210718190938022

采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。

控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。

HelloSpring

1、 编写一个Hello实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Hello {
private String name;

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

public void show(){
System.out.println("Hello,"+ name );
}
}

2、编写我们的spring文件 , 这里我们命名为applicationContest.xml

1
2
3
4
5
6
7
8
9
10
11
12
<?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就是java对象 , 由Spring创建和管理-->
<bean id="hello" class="com.kuang.pojo.Hello">
<property name="name" value="Spring"/>
</bean>

</beans>

3、我们可以去进行测试了 .

1
2
3
4
5
6
7
8
@Test
public void test(){
//解析beans.xml文件 , 生成管理相应的Bean对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//getBean : 参数即为spring配置文件中bean的id .
Hello hello = (Hello) context.getBean("hello");
hello.show();
}

思考问题?

  • Hello 对象是谁创建的 ? 【hello 对象是由Spring创建的
  • Hello 对象的属性是怎么设置的 ? hello 对象的属性是由Spring容器设置的

这个过程就叫控制反转 :

  • 控制 : 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 , 使用Spring后 , 对象是由Spring来创建的
  • 反转 : 程序本身不创建对象 , 而变成被动的接收对象 .

依赖注入 : 就是利用set方法来进行注入的.

IOC是一种编程思想,由主动的编程变成被动的接收

可以通过newClassPathXmlApplicationContext去浏览一下底层源码 .

OK , 到了现在 , 我们彻底不用再程序中去改动了 , 要实现不同的操作 , 只需要在xml配置文件中进行修改 , 所谓的IoC,一句话搞定 : 对象由Spring 来创建 , 管理 , 装配 !

IOC创建对象的方式

  1. 使用无参构造创建对象,默认!

  2. 假设我们要使用有参构造创建对象。

    1. 下标赋值
    1
    2
    3
    4
    <!--第一种:下标赋值-->
    <bean id="user" class="com.pomeloisland.pojo.User">
    <constructor-arg index="0" value="柚屿"/>
    </bean>
    1. 类型
    1
    2
    3
    4
    <!--第二种:通过类型创建,不建议使用-->
    <bean id="user" class="com.pomeloisland.pojo.User">
    <constructor-arg type="java.lang.String" value="柚屿"/>
    </bean>
    1. 参数名
    1
    2
    3
    4
    <!--第三种:直接通过参数名来设置-->
    <bean id="user" class="com.pomeloisland.pojo.User">
    <constructor-arg name="name" value="柚屿"/>
    </bean>

总结:在配置文件加载的时候,容器中管理的对象就已经初始化了!

Spring配置说明

1、别名

1
2
<!--设置别名:在获取Bean的时候可以使用别名获取-->
<alias name="userT" alias="userNew"/>

2、Bean的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--bean就是java对象,由Spring创建和管理-->

<!--
id : bean的标识符,要唯一,如果没有配置id,name就是默认标识符
如果配置id,又配置了name,那么name是别名
name可以设置多个别名,可以用逗号,分号,空格隔开
如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象;

class : bean的全限定名=包名+类名
-->
<bean id="hello" name="hello2 h2,h3;h4" class="com.kuang.pojo.Hello">
<property name="name" value="Spring"/>
</bean>

3、import

这个import,一般用于团队开发使用,他可以将多个配置文件,导入合并为一个;

假设,现在项目中有多个人开发,这三个人复制不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的!

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

依赖注入

1、构造器注入

1
2
3
<bean id="hello" class="com.kuang.pojo.Hello">
<property name="name" value="Spring"/>
</bean>

2、set方法注入【重点】

  • 依赖注入:Set注入
    • 依赖:bean对象的创建依赖于容器
    • 注入:bean对象中的所有属性,由容器来注入
  1. 模拟环境搭建

  2. 两个实体类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Data
    public class Student {

    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbys;
    private Map<String,String> card;
    private Set<String> games;
    private String wife;
    private Properties info;
    }
    1
    2
    3
    4
    @Data
    public class Address {
    private String address;
    }
    1. 配置 applicationContext.xml

      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
      <?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
      https://www.springframework.org/schema/beans/spring-beans.xsd">

      <bean id="address" class="com.kuang.pojo.Address">
      <property name="address" value="NJUPT9"/>
      </bean>
      <bean id="student" class="com.kuang.pojo.Student">
      <!--第一种,普通值注入,value-->
      <property name="name" value="狂神"/>

      <!--第二种,Bean注入-->
      <property name="address" ref="address"/>

      <!--数组-->
      <property name="books">
      <array>
      <value>红楼</value>
      <value>三国</value>
      </array>
      </property>

      <!--List-->
      <property name="hobbys">
      <list>
      <value>music</value>
      <value>swimming</value>
      <value>coding</value>
      </list>
      </property>

      <!--Map-->
      <property name="card">
      <map>
      <entry key="身份证" value="12312121212"/>
      <entry key="银行卡" value="678112121111000"/>
      </map>
      </property>

      <!--Set-->
      <property name="games">
      <set>
      <value>CF</value>
      <value>LOL</value>
      <value>GTA</value>
      </set>
      </property>

      <!--null-->
      <property name="wife">
      <null/>
      </property>

      <!--Properties-->
      <property name="info">
      <props>
      <prop key="学号">20190526</prop>
      <prop key="username">root</prop>
      <prop key="password">root</prop>
      </props>
      </property>
      </bean>
      </beans>
    2. 测试

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

3、拓展方式名注入

我们可以使用p命名空间和c命名空间进行注入

官方解释:

在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<!--p命名空间注入,可以直接注入属性的值:property-->
<bean id="user" class="com.kuang.pojo.User" p:name="狂神" p:age="18"/>

<!--c命名空间注入,通过构造器注入:construt-args-->
<bean id="user2" class="com.kuang.pojo.User" c:name="狂神2" c:age="11"/>

</beans>

注意点:p命名和c命名空间不能直接使用,需要导入xml约束!

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

bean的作用域

在这里插入图片描述

  1. 单例模式(Spring默认机制)
1
<bean id="user2" class="com.kuang.pojo.User" c:name="狂神2" c:age="11" scope="singleton"/>
  1. 原型模式:每次从容器中get的时候,都会产生一个新对象

    1
    <bean id="user2" class="com.kuang.pojo.User" c:name="狂神2" c:age="11" scope="prototype"/>
  2. 其余的request、session、application这些只能在web开发中使用到

Bean的自动装配

  • 自动装配是Spring满足bean依赖的一种方式
  • Spring会在上下文中自动寻找,并自动给bean装配属性

在Spring中有三种装配的方式

  1. 在xml中显示的配置
  2. 在java中显示的配置
  3. 隐式的自动装配bean 【重要】

1、测试

环境搭建:一个人有两个宠物

2、 byName自定装配

1
2
3
4
5
6
7
8
9
<bean id="cat" class="com.pomeloisland.pojo.Cat"/>
<bean id="dog" class="com.pomeloisland.pojo.Dog"/>

<!--
byName : 会自动在容器上下文中查找,和自己对象set方法后面的值对应的bean_id
-->
<bean id="people" class="com.pomeloisland.pojo.People" autowire="byName">
<property name="name" value="柚屿"/>
</bean>

3、byTpye自动装配

1
2
3
4
5
6
7
	<!--
byName : 会自动在容器上下文中查找,和自己对象set方法后面的值对应的bean_id
byType : 会自动在容器上下文中查找,和自己对象属性类型相同的bean
-->
<bean id="people" class="com.pomeloisland.pojo.People" autowire="byType">
<property name="name" value="柚屿"/>
</bean>

小结:

  • byName的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致
  • byType的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致

4、使用注解实现自动装配

jdk1.5支持的注解,Spring2.5就支持注解了!

要使用注解须知:

  1. 导入约束

  2. 配置注解的支持 context:annotation-config/

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <?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">

    <context:annotation-config/>

    </beans>

@Autowired

直接在属性上使用即可,也可以在set方法上使用

使用Autowired我们可以不用编写set方法了,前提是你这个自动装配的属性在 IOC(Spring)容器中存在,且符合名字byName

科普:

1
@Nullable	字段标记了这个注解,说明这个字段可以为null;
1
2
3
public @interface Autowired {
boolean required() default true;
}

测试代码:

1
2
3
4
5
6
7
8
public class People {
//如果显示定义了Autowired的required属性为false,说明这个对象可以为Null,否则不允许为空
@Autowired(required = false)
private Cat cat;
@Autowired
private Dog dog;
private String name;
}

如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【**@Autowired】完成的时候,我们可以使用@Qualifier(value = “xxx”)**去配合@Autowire的使用,指定一个唯一的bean对象注入!

1
2
3
4
5
6
7
8
9
public class People {

@Autowired
@Qualifier(value = "cat2")
private Cat cat;
@Autowired
private Dog dog;
private String name;
}

@Resource

1
2
3
4
5
6
7
8
public class People {

@Resource( name = "cat3")
private Cat cat;
@Resource
private Dog dog;
private String name;
}

小结:

@Resource和@Autowired的区别:

  • 都是用来自动转配的,都可以放在属性字段上
  • @Autowired 是通过byType的方式实现,而且必须要求这个对象存在!【常用】
  • @Resource 默认通过byName的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的情况下,就报错!【常用】
  • 执行顺序不同: @Autowired 通过byType的方式实现。@Resource默认通过byName的方式实现。

使用注解开发

在spring4之后,想要使用注解形式,必须得要引入aop的包

image-20210731175744277

在配置文件当中,还得要引入一个context约束

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

</beans>

1、Bean的实现

我们之前都是使用 bean 的标签进行bean注入,但是实际开发中,我们一般都会使用注解!

  1. 配置扫描哪些包下的注解

    1
    2
    <!--指定注解扫描包-->
    <context:component-scan base-package="com.pomeloisland.pojo"/>
  2. 在指定包下编写类,增加注解

    1
    2
    3
    4
    5
    @Component("user")
    // 相当于配置文件中 <bean id="user" class="当前注解的类"/>
    public class User {
    public String name = "秦疆";
    }
  3. 测试

    1
    2
    3
    4
    5
    6
    7
    @Test
    public void test(){
    ApplicationContext applicationContext =
    new ClassPathXmlApplicationContext("beans.xml");
    User user = (User) applicationContext.getBean("user");
    System.out.println(user.name);
    }

2、属性注入

使用注解注入属性

  1. 可以不用提供set方法,直接在直接名上添加@value(“值”)

    1
    2
    3
    4
    5
    6
    7
    @Component("user")
    // 相当于配置文件中 <bean id="user" class="当前注解的类"/>
    public class User {
    @Value("秦疆")
    // 相当于配置文件中 <property name="name" value="秦疆"/>
    public String name;
    }
  2. 如果提供了set方法,在set方法上添加@value(“值”);

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Component("user")
    public class User {

    public String name;

    @Value("秦疆")
    public void setName(String name) {
    this.name = name;
    }
    }

3、衍生注解

我们这些注解,就是替代了在配置文件当中配置步骤而已!更加的方便快捷!

@Component三个衍生注解

为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。

  • @Controller:web层
  • @Service:service层
  • @Repository:dao层

写上这些注解,就相当于将这个类交给Spring管理装配了!

  1. 自动装配注解

    回顾bean的自动装配!

  2. 作用域

    @scope

    • singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
    • prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收
    1
    2
    3
    4
    5
    6
    @Controller("user")
    @Scope("prototype")
    public class User {
    @Value("秦疆")
    public String name;
    }

    小结

    XML与注解比较

    • XML可以适用任何场景 ,结构清晰,维护方便
    • 注解不是自己提供的类使用不了,开发简单方便

    xml与注解整合开发 :推荐最佳实践

    • xml管理Bean
    • 注解完成属性注入
    • 使用过程中, 可以不用扫描,扫描是为了类上的注解
    1
    <context:annotation-config/>  

    作用:

    • 进行注解驱动注册,从而使注解生效
    • 用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显示的向Spring注册
    • 如果不扫描包,就需要手动配置bean
    • 如果不加注解驱动,则注入的值为null!

4、基于Java类进行配置

JavaConfig 原来是 Spring 的一个子项目,它通过 Java 类的方式提供 Bean 的定义信息,在 Spring4 的版本, JavaConfig 已正式成为 Spring4 的核心功能 。

测试:

  1. 编写一个实体类,Dog

    1
    2
    3
    4
    @Component  //将这个类标注为Spring的一个组件,放到容器中!
    public class Dog {
    public String name = "dog";
    }
  2. 新建一个config配置包,编写一个MyConfig配置类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Configuration  //代表这是一个配置类
    public class MyConfig {

    @Bean //通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id!
    public Dog dog(){
    return new Dog();
    }

    }
  3. 测试

    1
    2
    3
    4
    5
    6
    7
    @Test
    public void test2(){
    ApplicationContext applicationContext =
    new AnnotationConfigApplicationContext(MyConfig.class);
    Dog dog = (Dog) applicationContext.getBean("dog");
    System.out.println(dog.name);
    }

    导入其他配置如何做呢?

    1. 我们再编写一个配置类!

      1
      2
      3
      @Configuration  //代表这是一个配置类
      public class MyConfig2 {
      }
    2. 在之前的配置类中我们来选择导入这个配置类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      @Configuration
      @Import(MyConfig2.class) //导入合并其他配置类,类似于配置文件中的 inculde 标签
      public class MyConfig {

      @Bean
      public Dog dog(){
      return new Dog();
      }

      }

      关于这种Java类的配置方式,我们在之后的SpringBoot 和 SpringCloud中还会大量看到,我们需要知道这些注解的作用即可!

代理模式

为什么要学习代理模式?因为这就是SpringAOP的底层!【SpringAOP和SpringMVC】

代理模式的分类:

  • 静态代理
  • 动态代理

image-20210731181619264

1、静态代理

角色分析:

  • 抽象角色:一般会使用接口或者抽象类来解决
  • 真实角色:被代理角色
  • 代理角色:代理真实角色,代理真实角色后,一般会做一些附属操作
  • 客户:访问代理对象的人!

代码步骤:

  1. 接口

    1
    2
    3
    4
    //租房
    public interface Rent {
    void rent();
    }
  2. 真实角色

    1
    2
    3
    4
    5
    6
    7
    8
    //房东
    public class Host implements Rent{

    @Override
    public void rent() {
    System.out.println("房东要出租房子!");
    }
    }
  3. 代理角色

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    //代理角色
    public class Proxy implements Rent{
    private Host host;

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

    public Proxy() {
    }

    @Override
    public void rent() {
    host.rent();
    seeHouse();
    hetong();
    fare();
    }

    //看房
    public void seeHouse(){
    System.out.println("中介带你看房");
    }

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

    //签合同
    public void hetong(){
    System.out.println("签租赁合同");
    }
    }
  4. 客户端访问代理角色

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class Client {
    public static void main(String[] args) {
    //房东要出租房子
    Host host = new Host();
    //代理,中介帮房东出租房子,但是,代理角色一般会有一些附属操作!
    Proxy proxy = new Proxy(host);

    //不用面对房东,直接找中介租房即可!
    proxy.rent();
    }
    }

代理模式的好处:

  • 可以使真是角色的操作更加纯粹!不要去关注一些公共业务
  • 公共也就交给了代理角色!实现了业务的分工!
  • 公共业务发生扩展的时候,方便集中管理!

缺点:

  • 一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率会变低

2、加深理解

代码

1
2
3
4
5
6
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
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
//真实对象
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("增加了一个用户");

}

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

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

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

//1.改动原有的代码是公司中的大忌
}
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
public class UserServiceProxy implements UserService{
private UserServiceImpl userService;

public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}

@Override
public void add() {
log("add");
userService.add();
}

@Override
public void delete() {
log("delete");
userService.add();
}

@Override
public void update() {
log("update");
userService.add();
}

@Override
public void query() {
log("query");
userService.add();
}
//日志方法
public void log(String msg){
System.out.println("[DEBUG]使用了"+msg+"方法");
}
}
1
2
3
4
5
6
7
8
9
10
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();

UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);

proxy.delete();
}
}

聊聊AOP

image-20210801211220349

3、动态代理

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是动态生成的,不是我们直接写好的!
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
    • 基于接口 —- JDK动态代理【原生使用】
    • 基于类:cglib
    • java字节码实现:javasist

需要了解两个类:Proxy【代理】,InvocationHandler【调用处理程序】

动态代理的好处:

  • 可以使真是角色的操作更加纯粹!不要去关注一些公共业务
  • 公共也就交给了代理角色!实现了业务的分工!
  • 公共业务发生扩展的时候,方便集中管理!
  • 一个动态代理类代理的是一个接口,一般就是对应的一类业务
  • 一个动态代理类可以代理多个类,只要是实现了同一个接口即可

万能的工具类

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
//用这个类,自动生成代理类
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 {

log(method.getName());
//动态代理的本质,就是使用反射机制实现!
Object result = method.invoke(target, args);
return result;
}

public void log(String msg){
System.out.println("执行了"+msg+"方法");
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Client {
public static void main(String[] args) {
//真实角色
UserServiceImpl userService = new UserServiceImpl();
//代理角色,不存在
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//设置要代理的对象
pih.setTarget(userService);
//动态生成代理类
UserService proxy = (UserService) pih.getProxy();

proxy.add();
}
}

AOP

1、什么是AOP

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

图片

2、AOP在Spring中的作用

提供声明式事务;允许用户自定义切面

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 ….
  • 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
  • 目标(Target):被通知对象。
  • 代理(Proxy):向目标对象应用通知之后创建的对象。
  • 切入点(PointCut):切面通知 执行的 “地点”的定义。
  • 连接点(JointPoint):与切入点匹配的执行点。

图片

SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:

图片

即 Aop 在 不改变原有代码的情况下 , 去增加新的功能 .

3、使用Spring实现AOP

【重点】使用AOP织入,需要导入一个依赖包!

1
2
3
4
5
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>

方式一:使用Spring的API接口【主要是SpringAPI接口实现】

applicationContext.xml文件配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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="userService" class="com.pomeloisland.service.UserServiceImpl"/>
<bean id="log" class="com.pomeloisland.log.Log"/>
<bean id="afterLog" class="com.pomeloisland.log.AfterLog"/>
<!--方式一:使用原生的Spring API接口-->
<!--配置aop:需要导入aop的约束-->
<aop:config>
<!--切入点:expression:表达式,execution(要执行的位置!)-->
<aop:pointcut id="pointcut" expression="execution(* com.pomeloisland.service.UserServiceImpl.*(..))"/>
<!--执行环绕增加!-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>

</beans>

被代理的接口

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

接口实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class UserServiceImpl implements UserService{
@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
public class Log implements MethodBeforeAdvice {

//method:要执行的目标对象的方法
//args:参数
//target:目标对象
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
}
}

尾部日志

1
2
3
4
5
6
7
8
public class AfterLog implements AfterReturningAdvice {

//o:返回值
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("执行了"+method.getName()+"返回结果为"+o);
}
}

测试类

1
2
3
4
5
6
7
8
9
10
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

//动态代理代理的是接口:注意点
UserService userService = context.getBean("userService", UserService.class);

userService.add();
}
}

方式二:自定义来实现AOP【主要是切面定义】

1
2
3
4
5
6
7
8
9
10
//自定义切入点类
public class DiyPointCut {
public void before(){
System.out.println("======方法执行前======");
}

public void after(){
System.out.println("======方法执行后======");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
<!--方式二:自定义类-->
<bean id="diy" class="com.pomeloisland.diy.DiyPointCut"/>
<aop:config>
<!--自定义切面,ref要引用的类-->
<aop:aspect ref="diy">
<!--切入点-->
<aop:pointcut id="point" expression="execution(* com.pomeloisland.service.UserServiceImpl.*(..))"/>
<!--通知-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>

测试类跟之前无任何区别!

方式三:使用注解实现!

注解类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//方式三:使用注解方式实现AOP
@Aspect //标注这个类是一个切面
public class AnnotationPointCut {
@Before("execution(* com.pomeloisland.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("======方法执行前======");
}

//在环绕增强中,我们可以给定一个参数,代表我们要获取处理的切入点
@Around("execution(* com.pomeloisland.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("======环绕前======");

//执行方法
Object proceed = joinPoint.proceed();

System.out.println("======环绕后======");
}
}

applicationContext.xml配置文件

1
2
3
4
<!--方式三-->
<bean id="annotationPointCut" class="com.pomeloisland.diy.AnnotationPointCut"/>
<!--开启注解支持 默认JDK动态代理(proxy-target-class="false") cglib(proxy-target-class="true")-->
<aop:aspectj-autoproxy/>

测试类跟之前毫无区别!

整合Mybatis

步骤:

  1. 导入相关jar包

    • junit
    • mybatis
    • mysql
    • mysql数据库
    • spring相关的
    • aop织入
    • mybatis-spring【new】
    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
    <dependencies>
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    </dependency>

    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.25</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.9</version>
    </dependency>

    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.7</version>
    </dependency>

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

    <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.6</version>
    </dependency>

    </dependencies>
  2. 编写配置文件

  3. 测试

1、回顾Mybatis

  1. 编写实体类

    1
    2
    3
    4
    5
    6
    7
    8
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
    private String name;
    private int id;
    private String pwd;
    }
  2. 编写核心配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <?xml version="1.0" encoding="GBK" ?>
    <!DOCTYPE configuration
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <!--configuration核心配置文件-->
    <configuration>

    <typeAliases>
    <package name="com.pomeloisland.pojo"/>
    </typeAliases>

    <environments default="development">
    <environment id="development">
    <transactionManager type="JDBC"/>
    <dataSource type="POOLED">
    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
    <property name="username" value="zhwroot"/>
    <property name="password" value="zhwroot"/>
    </dataSource>
    </environment>
    </environments>

    <mappers>
    <mapper class="com.pomeloisland.mapper.UserMapper"/>
    </mappers>
    </configuration>
  3. 编写接口

    1
    2
    3
    4
    public interface UserMapper {
    List<User> selectUser();
    }

  4. 编写Mapper

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <?xml version="1.0" encoding="GBK" ?>
    <!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!--namespace=绑定一个对应的Dao/Mapper接口-->
    <mapper namespace="com.pomeloisland.mapper.UserMapper">

    <select id="selectUser" resultType="user">
    select *
    from mybatis.user;
    </select>

    </mapper>
  5. 测试结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public class MyTest {
    @Test
    public void test() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream in = Resources.getResourceAsStream(resource);

    SqlSessionFactory build = new SqlSessionFactoryBuilder().build(in);
    SqlSession sqlSession = build.openSession(true);

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> users = mapper.selectUser();
    for (User user : users) {
    System.out.println(user);
    }
    }
    }

2、Mybatis-Spring

1、整合Mybatis第一种方式

  1. 编写数据源配置
  2. sqlSessionFactory
  3. sqlSessionTemplate
  4. 需要给接口加实现类
  5. 将自己写的实现类注入到Spring中,测试使用

Mybatis的配置文件可以完全由Spring来做!!!

spring-dao.xml:

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
<?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">

<!--DataSource:使用Spring的数据源替换Mybatis的配置 c3p0 dbcp druid
使用Spring提供的JDBC-->
<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?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
<property name="username" value="zhwroot"/>
<property name="password" value="zhwroot"/>
</bean>

<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource"/>
<!--绑定Mybatis配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/pomeloisland/mapper/*.xml"/>
</bean>

<!--SqlSessionTemplate就是使用的sqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--只能使用构造器注入sqlSessionFactory,因为它没有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
</beans>

UserMapperImpl.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class UserMapperImpl implements UserMapper{
//所有操作在原来都使用sqlSession来执行,现在都使用SqlSessionTemplate;
private SqlSessionTemplate sqlSession;

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

@Override
public List<User> selectUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectUser();
}
}

测试类:Test:

1
2
3
4
5
6
7
8
9
10
11
public class MyTest {
@Test
public void test() throws IOException {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");

UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
}

进一步简化:applicationContext.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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">


<import resource="spring-dao.xml"/>

<bean id="userMapper" class="com.pomeloisland.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>

2、整合Mybatis的第二种方式

UserMapperImpl2.java:

1
2
3
4
5
6
7
8
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> selectUser() {
// SqlSession sqlSession = getSqlSession();
// UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return getSqlSession().getMapper(UserMapper.class).selectUser();
}
}

applicationContext.xml:

1
2
3
<bean id="userMapper2" class="com.pomeloisland.mapper.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>

测试类:

1
2
3
4
5
6
7
8
9
public void test() throws IOException {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

UserMapper userMapper = context.getBean("userMapper2", UserMapper.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
}

声明式事务

1、回顾事务

  • 把一组业务当成一个业务来做;要么都成功,要么都失败!
  • 事务在项目开发中,十分的重要,涉及到数据的一致性问题,不能马虎!
  • 确保完整性和一致性;

事务的ACID原则:

  • 原子性
  • 一致性
  • 隔离性
    • 多个业务可能操作同一个资源,防止数据损坏
  • 持久性
    • 事务一旦提交,无论系统发生什么问题,结果都不会被影响,被持久化的写到存储器中!

2、Spring声明事务

  • 声明式事务:AOP
  • 编程式事务:需要在代码中,进行事务的管理

为什么需要事务?

  • 如果不配置四五,可能存在数据提交不一致的情况;
  • 如果不在Spring中配置声明式事务,就需要在代码中手动配置事务!
  • 事务在项目的开发中十分重要,涉及到数据的一致性和完整性问题,不容马虎!

配置事务

spring-dao.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!--配置声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="datasource"/>
</bean>

<!--结合AOP实现事务的织入-->
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--给一些方法配置事务-->
<!--配置事务的传播特性:new propagation= -->
<tx:attributes>
<tx:method name="add"/>
<tx:method name="delete"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>

<!--配置事务切入-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.pomeloisland.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>

代码请访问

Gitee:https://gitee.com/lzjtuzhw/spring

Github:https://github.com/pomeloisland/spring