Spring

1、Spring

1.1、简介

spring理念,使现有技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架。
SSH :Struct2 +Spring  +Hibernate
SSM :  SpringMvc +Spring  +Mabatis 

官网:https://spring.io/projects/spring-framework#overview

官方下载地址:http://repo.spring.io/release/org/springframework/spring

Spring中文文档:https://www.docs4dev.com/docs/zh/spring-framework/5.1.3.RELEASE/reference

GitHub官网:https://github.com/spring-projects/spring-framework

maven仓库官网:https://mvnrepository.com/search?q=spring

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.0.RELEASE</version>
</dependency>

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

1.2、优点

Spring是一个开源免费的框架(容器)
Spring是一个轻量级的,非入侵式的框架
控制反转 (IOC),面向切面编程 (AOP)
支持事物的处理,对框架整合的支持

总结一句话,Spring 就是一个轻量级的控制反转(IOC)和面向切面编程的框架!

1.3、组成

在这里插入图片描述

1.4、拓展

在Spring的官网有这个介绍:现代化的java开发!说白了就是基于Spring的开发!

在这里插入图片描述
Spring Boot
。一个快速开发的脚手架
。基于SpringBoot可以快速的开发单个微服务
。约定大于配置
Spring Cloud
。Spring Cloud是基于Spring Boot实现的

因为现在大多数公司都在使用SpringBoot进行快速开发,学习Spring Boot的前提,需要完全掌握Spring及SpringMVC!承上启下的作用!

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

2、IOC理论推导

之前:
1、UserDao接口

2、UserDaolmpl实现类

3、UserService业务接口

4、UserServiceImpl业务实现类

在我们之前的业务中,用户的需求可能会影响我们原来的代码,因为我们会根据用户的需求去修改源代码!
如果程序代码量十分大,修改一次的成本代价之分昂贵!
在这里插入图片描述

我们使用一个Set接口v实现。已经发生了革命性的变化!

private UserDao userDao;
     //利用set进行动态实现值的注入!
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

之前程序是主动创建对象,控制权在程序猿手上!
使用了Set注入后,程序不在具有主动性,而是变成了被动的接收对象。
说白了就是从程序主动创建对象变成了按照需求动态创建对象

这种思想,从本质上解决了问题,我们程序员不用再去管理对象的创建了。系统的耦合性大大降低,可以更加专注的在业务的实现上!这是IOC的原型!
在这里插入图片描述
IOC本质:
控制反转 IoC (Inversion of Control),是一种设计思想,DI(依赖注入)是实现 IoC 的一种方法,我们使用面向对象编程,对象的创建与对象的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓的控制反转就是:获得依赖对象的方式反转了。

在这里插入图片描述

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

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

3、HelloSpring

Spring-Study–02

Hello对象是谁创建的?
hello对象是由Spring创建的
Hello对象的属性是怎么设置的?
hello对象的属性是由Spring容器(容器就是beans.xml文件)设置的

这个过程就叫做控制反转:

控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制的,使用Spring后,对象是由Spring来创建的。

反转:程序本身不创建对象,而变成被动的接受对象。

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

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

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

ref : 引用Spring 容器中创建好的对象

value : 具体的值,基本数据类型!

4、IOC创建对象的方式

1、使用无参构造创建对象(默认)

2、假设我们要使用有参构造创建对象
1.下标赋值

    <!--第一种,下标赋值!-->
<bean id="user" class="com.kuang.pojo.User">
    <constructor-arg index="0" value="狂神说Java"/>
</bean>
  

2.类型

  <!-- 第二种方式,通过类型创建,不建议使用-->
<bean id="user" class="com.kuang.pojo.User">
    <constructor-arg type="java.lang.String" value="songtian"/>
</bean>

3.参数名

     <!--第三种,直接通过参数名来设置-->
<bean id="user" class="com.kuang.pojo.User">
    <constructor-arg name="name" value="松田"/>
</bean>

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

5、Spring配置

5.1、别名

<!--别名,如果添加了别名,我们也可以使用别名获取到这个对象-->
    <alias name="user" alias="usernew"/>

5.2、Bean的配置

<!--
    id: bean 的唯一标识,也就是相当于我们学的对象名
    class :bean 对象对应的全限定名:包名 + 类名
    name :也是别名,而且name更高级,可以同时起多个别名
    -->
    <bean id="userT" class="com.kuang.pojo.UserT" name="user2 u2,u3;u4">
        <property name="name" value="哈哈哈哈"/>
    </bean>

5.3、import

这个 import ,一般用于团队开发使用,它可以将多个配置文件,导入合并为一个。
假设现在项目中有多个人开发,这三个人负责不同的类开发,不同的类需要注册在不同的bean中,我们可以利用 import 将所有人的 beans.xml 合并为一个总的!
张三
李四
王五
applicationContext.xml

  <import resource="beans.xml"/>
  <import resource="beans2.xml"/>
  <import resource="beans3.xml"/>

使用的时候,直接使用总的配置就可以了

6、依赖注入(DI)

6.1、构造器注入

前面已经说过了

6.2、Set方式注入【重点】

依赖注入:Set 注入!

不会的话去官文档中查询使用方法。

。依赖:bean 对象的创建依赖于容器
。注入:bean对象中的所有属性,由容器来注入

【环境搭建】

1、复杂类型
在这里插入图片描述

public class Address {
    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
     @Override
    public String toString() {
        return "Address{" +
                "address='" + address + '\'' +
                '}';
    }
}

2、真实测试对象

在这里插入图片描述
此处并未拷入get set及其tostring 方法。

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;

3、beans,xml

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="student" class="com.kaung.pojo.Student">
    <!--第一种、普通的注入方式,value -->
    <property name="name" value="利利"/>
</bean>

</beans>

4、测试类

在这里插入图片描述

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.getName());
    }
}

完善注入信息:

<?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.kaung.pojo.Address">
    <property name="address" value="西安"/>
</bean>

<bean id="student" class="com.kaung.pojo.Student">
    <!--第一种、普通的注入方式,value -->
    <property name="name" value="利利"/>
    <!--第二种、bean注入,使用ref -->
    <property name="address" ref="address"/>

    <!--数组注入,ref-->
    <property name="books">
        <array>
            <value>红楼梦</value>
            <value>西游记</value>
            <value>三国演义</value>
            <value>水浒传</value>
        </array>
    </property>
    <!--List-->
    <property name="hobbys">
        <list>
            <value>听歌</value>
            <value>敲代码</value>
            <value>看电影</value>
        </list>
    </property>
    <!--map-->
    <property name="card">
        <map>
            <entry key="身份证" value="610125199802315569"/>
            <entry key="银行卡" value="34154545154554545"/>
        </map>
    </property>

    <!--set-->
    <property name="games">
        <set>
            <value>LOL</value>
            <value>CF</value>
            <value>COC</value>
        </set>
    </property>
    <!--null-->
    <property name="wife">
        <null/>
    </property>

    <!--Properties-->
    <property name="info">
        <props>
            <prop key="学号">1060917024023</prop>
            <prop key="性别">男性</prop>
            <prop key="姓名">男性</prop>
            <prop key="username">root</prop>
            <prop key="password">123456</prop>
        </props>
    </property>
</bean>

</beans>

6.3、拓展方式注入

我们可以使用P命令空间和c命令空间进行注入

官方解释:
在这里插入图片描述

使用:

<?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.kaung.pojo.User" p:name="钱江" p:age="18"/>

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

</beans>

测试:

  @Test
    public void Test2(){
        ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
        User user = context.getBean("user2", User.class);
        System.out.println(user);
    }

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

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

6.4、bean的作用域

在这里插入图片描述
1、单例模式(Spring默认机制)

<bean id="user2" class="com.kaung.pojo.User" c:age="18" c:name="狂神" scope="singleton"/>

2、原型模式:每次从容器中get的时候,都会产生一个新对象!

<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>

在 Spring 中,Bean 的作用域是指在应用程序中创建 Bean 实例的方式和生命周期。Spring 框架提供了五种标准作用域:

1、singleton:每个 Spring 应用程序中只存在一个 Bean 实例,所有请求都将返回同一个实例。

2、prototype:每次请求 Bean 时都会创建一个新的实例。

3、request:每个 HTTP 请求都将创建一个新的 Bean 实例,适用于 Web 应用程序。

4、session:每个 HTTP 会话都将创建一个新的 Bean 实例,适用于 Web 应用程序。

5、global session:基于 Servlet 3.0+ 规范,在集群环境下使用,与 session 作用域类似,但 Bean 实例会跨多个 HTTP 请求和会话存活。

不同作用域的 Bean 生命周期有所不同。通常情况下,Singleton Bean 在容器启动时被创建,而 Prototype Bean 则是在每次请求时创建。Request 和 Session Bean 与 Web 请求和会话的生命周期关联,并在请求或会话结束时销毁。Global Session Bean 则是随着整个应用程序上下文的加载而创建,并在应用程序上下文退出时销毁。

通过正确选择 Bean 的作用域,可以优化应用程序的性能和资源消耗,并确保正确地管理 Bean 的状态和生命周期。

例如:

将需要在多个地方共享的 Bean 声明为 Singleton,可以避免多次创建实例和消耗内存

而将需要在多次请求中使用的 Bean 声明为 Prototype,则可以避免共享状态和线程安全等问题

需要注意的是,只有在使用 Web 应用程序时才能使用 Request、Session 和 Global Session 作用域。在非 Web 应用程序中使用这些作用域可能会导致错误或意外行为。

7、Bean的自动装配

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

在Spring中,有三种装配的方式
1、在xml中显示的配置
2、在java中显示配置
3、隐式的自动装配bean【重要

7.1、测试

测试环境搭建

1、新建一个项目

2、新建两个实体类,Cat Dog 都有一个叫的方法

public class Cat {
   public void shout() {
       System.out.println("miao~");
  }
}
public class Dog {
   public void shout() {
       System.out.println("wang~");
  }
}

3、新建一个用户类 User

public class User {
   private Cat cat;
   private Dog dog;
   private String str;
}

4、编写Spring配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

   <bean id="dog" class="com.kuang.pojo.Dog"/>
   <bean id="cat" class="com.kuang.pojo.Cat"/>

   <bean id="user" class="com.kuang.pojo.User">
       <property name="cat" ref="cat"/>
       <property name="dog" ref="dog"/>
       <property name="str" value="qinjiang"/>
   </bean>
</beans>

5、测试

public class MyTest {
   @Test
   public void testMethodAutowire() {
       ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
       User user = (User) context.getBean("user");
       user.getCat().shout();
       user.getDog().shout();
  }
}

结果正常输出,环境OK

7.2、ByName自动装配

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

7.3、ByType自动装配

<!--
byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean!
-->
    <bean id="people" class="com.kuang.pojo.People" autowire="byType">
        <property name="name" value="狂神呀"/>
    </bean>

小结:

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

byType 的时候,需要保证所有的bean的class唯一,并且这个bean需要和自动注入的属性的属性的类型一致!

7.4、使用注解实现自动装配【重点】

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

要使用注解须知:
1、导入约束,context约束

2、配置注解的支持:context:annotation-config/ 【重要!

<?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注解
1、直接在属性上使用即可!也可以在也可以在set方法上使用!
2、使用 @Autowired 我们可以不用编写set方法了,前提是你这个自动装配的属性在 IOC (Spring) 容器中存在,且符合名字 byname!
在这里插入图片描述
如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候,我们可以使用@Qualifier(value=“XXX”)去配置@Autowired的使用,指定一个唯一的bean对象注入!
在这里插入图片描述

public class People {

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

@Resource注解

public class People {

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

小结:
@Resource 和 @Autowired 的区别:

。都是用来装配的,都可以放在属性字段上

。@Autowired 通过 byType 的方式实现,而且必须要求这个对象存在!
。@Resource默认通过byname的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的情况在,就报错!
。执行顺序不同,@Autowired默认通过 byType 的方式实现,@Resource默认通过byname的方式实现

8、使用注解开发

在Spring4 后 ,要使用注解开发,必须保证aop的包导入了
在这里插入图片描述
使用注解注入就需要导入context约束,增加注解支持!

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

1、bean

@Component :组件。 放在类上,说明这个类被Spring管理了,就是bean!
等价于 <bean id="user" class="com.kuang.dao.User"/>

//等价于 <bean id="user" class="com.kuang.dao.User"/>
//@Component :组件,放在类上,说明这个类被Spring管理了,就是bean!

@Component
public class User {

2、属性如何注入

@Component
public class User {

    public String name;
    //相当于 <property name="name" value="kaungshen"/>
    @Value("kaungshen")
    public void setName(String name) {
        this.name = name;
    }
}

3、衍生的注解

@Component有几个衍生注解,我们在web开发中,会按照mvc三层架构分层!
。dao【@Repository】
。service【@Service】
。controller【@Controller】

这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean
4、自动装配置
@Autowired :自动装配通过类型。
如果@Autowired不能唯一自动装配上属性则需要用@Qualifier(value=“XXX”)去配合@Autowired的使用,指定一个唯一的bean对象注入!
@Resource:自动装配通过名字。类型

5、作用域

@Component
@Scope("prototype")
public class User {

    public String name;
    //相当于 <property name="name" value="kaungshen"/>
    @Value("kaungshen")
    public void setName(String name) {
        this.name = name;
    }
}

6、小结
xml 与 注解:

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

xml 与 注解最佳实践:
。xml用来管理bean
。注解只负责完成属性的注入
。我们在使用的过程中,只需要注意一个问题,必须让注解生效,就需要开启注解支持

<!--指定要扫描的包,这个包下的注解就会生效-->
<context:component-scan base-package="com.kuang"/>
<context:annotation-config/>

9、使用Java的方式配置Spring

我们现在要完全不适用Spring的xml配置了,全权交给java来做!
javaConfig是Spring的一个字项目,在Spring4之后,它变成了一个核心功能!

实体类

//这里这个注解的意思,就是说明这个类被Spring接管了,注册到了容器中
@Component
public class User {

    private String name;

    public String getName() {
        return name;
    }
@Value("sntian")
    public void setName(String name) {
        this.name = name;
    }

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

配置文件

//这个也会被Spring容器托管,注册到容器中,因为他本来就是一个@Component,
// @Configuration代表这是一个配置类,就和我们之前看到的beans.xml是一样的
@Configuration
@ComponentScan("com.kuang.pojo")
@Import(KuangConfig2.class)
public class KuangConfig {

    //注册一个bean,就相当于我们之前写的一个bean标签,
    //这个方法的名字,就相当于bean标签中的id属性
    //这个方法的返回值,就相当于bean标签中的class属性
    @Bean
    public User user(){
        return new User();//就是返回要注入到bean的对象!
    }
}

测试类

public class MyTest {
    public static void main(String[] args) {
        //如果完全使用了配置类的方式去做,我们就只能通过 AnnotationConfig 上下文来获取容器,通过配置类的class对象加载!
        ApplicationContext context = new AnnotationConfigApplicationContext(KuangConfig.class);
        User getUser = (User) context.getBean("user");
        System.out.println(getUser.getName());
    }

这种纯java的配置方式,在SpringBoot中随处可见!

10、代理模式

为什么要学代理模式? 因为这就是SpringAOP的底层!【SpringAOP 和SpingMVC】面试必问

代理模式的分类:
。静态代理

。动态代理

在这里插入图片描述

10.1、静态代理

角色分析:

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

代码步骤:
1、接口

public interface Rent {
    public void rent();
}

2、真实角色

//房东
public class Host implements Rent{
    @Override
    public void rent() {
        System.out.println("房东要出租房子!");
    }
}

3、代理角色

public class Proxy implements Rent{
    private Host host;

    public Proxy() {
    }

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


    public void rent(){
        host.rent();
        seeHouse();
        hetong();
        fare();
    }
    //看房
    public void seeHouse(){
        System.out.println("中介带你看房!");
    }
    //签租赁合同
    public void hetong(){
        System.out.println("签租赁合同");
    }
    //看房
    public void fare(){
        System.out.println("收中介费");
    }
}

4、客户端访问代理角色

public class Client {
    public static void main(String[] args) {
        //房东要租房子
        Host host = new Host();
        //代理,中介帮房东租房子,但是呢?代理角色会有一些附属操作!
        Proxy proxy = new Proxy(host);
        //你不用面对房东,直接找中介租房即可!
        proxy.rent();
    }
}

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

缺点:
。一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率会降低

10.2、静态代理在理解

代码对应demo02

聊聊AOP
在这里插入图片描述

10.3、动态代理

动态代理和静态代理角色一样

动态代理类是动态生成的,不是我们直接写好的!

动态代理分为两大类:1、基于接口的动态代理类 2、基于类的动态代理

。基于接口-----JDK动态代理 【我们在这里使用
。基于类: cglib
。java字节码实现:javasist

需要了解两个类:
Proxy :代理 ,提供创建动态代理和实例的静态方法

InvocationHandler:调用处理程序,并返回一个结果

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

11、AOP

11.1、什么是AOP

AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,它可以在不修改原有业务逻辑代码的情况下,通过将横切关注点(如日志记录、事务管理、安全控制等)从业务逻辑中抽离出来进行统一管理和维护。

AOP 通过定义切面(Aspect)和切点(Pointcut),在运行时动态织入切面逻辑,实现对目标对象(Target)方法的增强(Advice),从而对系统的某些方面进行集中管理。

在 Java 开发中,AOP 最常用的实现是使用 Spring 框架的 AOP 功能。Spring AOP 主要采用动态代理的方式,为目标对象生成代理对象,从而实现对目标方法的增强。

Spring AOP 提供了四种类型的 Advice,分别是:

1、Before:在目标方法执行前调用。

2、After:在目标方法执行后调用,无论目标方法是否出现异常。

3、AfterReturning:在目标方法正常返回后调用。

4、AfterThrowing:在目标方法抛出异常后调用。

此外,Spring AOP 还提供了 Joinpoint、ProceedingJoinPoint 等类,用于获取目标对象的信息和控制程序流程。

总之,AOP 是一种重要的编程技术,它能够解决系统的横切关注点问题,提高代码复用性和可维护性。在 Java 开发中,可以使用 Spring AOP 来实现 AOP 编程。

图片
AspectJ 是一个基于 Java 语言的 AOP 框架,它提供了一套切入点(Pointcut)表达式语言,用于定义切面(Aspect)要织入代码的位置。

AspectJ 切入点语法比较灵活,可以使用通配符、逻辑操作符等方式进行组合。下面列举几个常用的 AspectJ 切入点语法:

execution:表示匹配方法执行的连接点。
例如:execution(public * com.example.demo.*Controller.*(..)) 表示匹配 com.example.demo 包下所有 controller 类中的所有公共方法。

within:表示匹配某个类型内的所有连接点。例如:within(com.example.demo.service.*) 表示匹配 com.example.demo.service 包下的所有连接点。

this:表示限定目标对象实例的类型。例如:this(com.example.demo.service.SomeService) 表示匹配目标对象是 SomeService 类型的所有连接点。

target:表示目标对象的类型。例如:target(com.example.demo.service.SomeService) 表示匹配目标对象类型是 SomeService 类型的所有连接点。

args:表示匹配参数列表中指定类型的连接点。例如:args(java.lang.String) 表示匹配所有包含 java.lang.String 类型参数的连接点。

以上仅是 AspectJ 切入点语法的一部分,其它还有很多灵活的应用方式,可以根据具体的业务需求进行选择和组合。在使用 AspectJ 进行切面编程时,需要编写一个切面类,并使用 AspectJ 切入点表达式定义切点和增强方式。

11.2、Aop在Spring中的作用

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

以下名词需要了解下:

1、横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …

2、切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。

3、通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。

4、目标(Target):被通知对象。

5、代理(Proxy):向目标对象应用通知之后创建的对象。

6、切入点(PointCut):切面通知 执行的 “地点”的定义。

7、连接点(JointPoint):与切入点匹配的执行点。

图片

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

图片

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

11.3、使用Spring实现Aop

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

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.9.4</version>
</dependency>

方式一

方式一:通过 Spring API 实现 【主要SpringAPI接口实现】

首先编写我们的业务接口和实现类

public interface UserService {

   public void add();

   public void delete();

   public void update();

   public void search();

}
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 search() {
       System.out.println("查询用户");
  }
}

然后去写我们的增强类 , 我们编写两个 , 一个前置增强 一个后置增强

public class Log implements MethodBeforeAdvice {

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



public class AfterLog implements AfterReturningAdvice {
   //returnValue 返回值
   //method被调用的方法
   //args 被调用的方法的对象的参数
   //target 被调用的目标对象
   @Override
   public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
       System.out.println("执行了" + target.getClass().getName()
       +"的"+method.getName()+"方法,"
       +"返回值:"+returnValue);
  }
}

最后去spring的文件中注册 , 并实现aop切入实现 , 注意导入约束 .

<?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.kuang.service.UserServiceImpl"/>
   <bean id="log" class="com.kuang.log.Log"/>
   <bean id="afterLog" class="com.kuang.log.AfterLog"/>

   <!--aop的配置-->
   <aop:config>
       <!--切入点 expression:表达式匹配要执行的方法-->
       <aop:pointcut id="pointcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
       <!--执行环绕; 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 {
   @Test
   public void test(){
       ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
       UserService userService = (UserService) context.getBean("userService");
       userService.search();
  }
}

Aop的重要性 : 很重要 . 一定要理解其中的思路 , 主要是思想的理解这一块 .

Spring的Aop就是将公共的业务 (日志 , 安全等) 和领域业务结合起来 , 当执行领域业务时 , 将会把公共业务加进来 . 实现公共业务的重复利用 . 领域业务更纯粹 , 程序猿专注领域业务 , 其本质还是动态代理 .

方式二

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

目标业务类不变依旧是userServiceImpl

第一步 : 写我们自己的一个切入类

public class DiyPointcut {

   public void before(){
       System.out.println("---------方法执行前---------");
  }
   public void after(){
       System.out.println("---------方法执行后---------");
  }
   
}

去spring中配置

<!--第二种方式自定义实现-->
<!--注册bean-->
<bean id="diy" class="com.kuang.config.DiyPointcut"/>

<!--aop的配置-->
<aop:config>
   <!--第二种方式:使用AOP的标签实现-->
   <aop:aspect ref="diy">
       <aop:pointcut id="diyPonitcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
       <aop:before pointcut-ref="diyPonitcut" method="before"/>
       <aop:after pointcut-ref="diyPonitcut" method="after"/>
   </aop:aspect>
</aop:config>

测试:

public class MyTest {
   @Test
   public void test(){
       ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
       UserService userService = (UserService) context.getBean("userService");
       userService.add();
  }
}

方式三

方式三:使用注解实现【】

第一步:编写一个注解实现的增强类

package com.kuang.config;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class AnnotationPointcut {
   @Before("execution(* com.kuang.service.UserServiceImpl.*(..))")
   public void before(){
       System.out.println("---------方法执行前---------");
  }

   @After("execution(* com.kuang.service.UserServiceImpl.*(..))")
   public void after(){
       System.out.println("---------方法执行后---------");
  }

   @Around("execution(* com.kuang.service.UserServiceImpl.*(..))")
   public void around(ProceedingJoinPoint jp) throws Throwable {
       System.out.println("环绕前");
       System.out.println("签名:"+jp.getSignature());
       //执行目标方法proceed
       Object proceed = jp.proceed();
       System.out.println("环绕后");
       System.out.println(proceed);
  }
}

第二步:在Spring配置文件中,注册bean,并增加支持注解的配置

<!--第三种方式:注解实现-->
<bean id="annotationPointcut" class="com.kuang.config.AnnotationPointcut"/>
<aop:aspectj-autoproxy/>

aop:aspectj-autoproxy:说明

通过aop命名空间的<aop:aspectj-autoproxy />声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。当然,spring 在内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,但具体实现的细节已经被<aop:aspectj-autoproxy />隐藏起来了

<aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,当配为<aop:aspectj-autoproxy poxy-target-class=“true”/>时,表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。

12、整合Mybatis

12.1、回忆mybatis

1、导入相关jar包
。junit
。mybatis
。mysql数据库
。Spring相关的
。aop织入
。mybatis-spring【new】

junit

<dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>4.12</version>
</dependency>

mybatis

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

mysql-connector-java

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

spring相关

<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>5.1.10.RELEASE</version>
</dependency>
<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-jdbc</artifactId>
   <version>5.1.10.RELEASE</version>
</dependency>

aspectJ AOP 织入器

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.9.4</version>
</dependency>

mybatis-spring整合包 【重点】

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

配置Maven静态资源过滤问题!

<build>
   <resources>
       <resource>
           <directory>src/main/java</directory>
           <includes>
               <include>**/*.properties</include>
               <include>**/*.xml</include>
           </includes>
           <filtering>true</filtering>
       </resource>
   </resources>
</build>

2、编写配置文件

3、代码实现

回忆MyBatis
1、编写pojo实体类

package com.kuang.pojo;

public class User {
   private int id;  //id
   private String name;   //姓名
   private String pwd;   //密码
}

2、实现mybatis的配置文件

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

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

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

   <mappers>
       <package name="com.kuang.dao"/>
   </mappers>
</configuration>

UserDao接口编写

public interface UserMapper {
   public List<User> selectUser();
}

接口对应的Mapper映射文件

<?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.kuang.dao.UserMapper">

   <select id="selectUser" resultType="User">
    select * from user
   </select>

</mapper>

测试类

@Test
public void selectUser() throws IOException {

   String resource = "mybatis-config.xml";
   InputStream inputStream = Resources.getResourceAsStream(resource);
   SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
   SqlSession sqlSession = sqlSessionFactory.openSession();

   UserMapper mapper = sqlSession.getMapper(UserMapper.class);

   List<User> userList = mapper.selectUser();
   for (User user: userList){
       System.out.println(user);
  }

   sqlSession.close();
}

12.2、MyBatis-Spring

引入Spring之前需要了解mybatis-spring包中的一些重要类;

http://www.mybatis.org/spring/zh/index.html

图片

什么是 MyBatis-Spring?

MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。

知识基础

在开始使用 MyBatis-Spring 之前,你需要先熟悉 Spring 和 MyBatis 这两个框架和有关它们的术语。这很重要

MyBatis-Spring 需要以下版本:

MyBatis-SpringMyBatisSpring 框架Spring BatchJava
2.03.5+5.0+4.0+Java 8+
1.33.4+3.2.2+2.1+Java 6+

如果使用 Maven 作为构建工具,仅需要在 pom.xml 中加入以下代码即可:

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

要和 Spring 一起使用 MyBatis,需要在 Spring 应用上下文中定义至少两样东西:一个 SqlSessionFactory 和至少一个数据映射器类。

在 MyBatis-Spring 中,可使用SqlSessionFactoryBean来创建 SqlSessionFactory。要配置这个工厂 bean,只需要把下面代码放在 Spring 的 XML 配置文件中:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
 <property name="dataSource" ref="dataSource" />
</bean>

注意:SqlSessionFactory需要一个 DataSource(数据源)。这可以是任意的 DataSource,只需要和配置其它 Spring 数据库连接一样配置它就可以了。

在基础的 MyBatis 用法中,是通过 SqlSessionFactoryBuilder 来创建 SqlSessionFactory 的。而在 MyBatis-Spring 中,则使用 SqlSessionFactoryBean 来创建。

在 MyBatis 中,你可以使用 SqlSessionFactory 来创建 SqlSession。一旦你获得一个 session 之后,你可以使用它来执行映射了的语句,提交或回滚连接,最后,当不再需要它的时候,你可以关闭 session。

SqlSessionFactory有一个唯一的必要属性:用于 JDBC 的 DataSource。这可以是任意的 DataSource 对象,它的配置方法和其它 Spring 数据库连接是一样的。

一个常用的属性是 configLocation,它用来指定 MyBatis 的 XML 配置文件路径。它在需要修改 MyBatis 的基础配置非常有用。通常,基础配置指的是 < settings> 或 < typeAliases>元素。

需要注意的是,这个配置文件并不需要是一个完整的 MyBatis 配置。确切地说,任何环境配置(<environments>),数据源(<DataSource>)和 MyBatis 的事务管理器(<transactionManager>)都会被忽略。SqlSessionFactoryBean 会创建它自有的 MyBatis 环境配置(Environment),并按要求设置自定义环境的值。

SqlSessionTemplate 是 MyBatis-Spring 的核心。作为 SqlSession 的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的 SqlSession。

模板可以参与到 Spring 的事务管理中,并且由于其是线程安全的,可以供多个映射器类使用,你应该总是用 SqlSessionTemplate 来替换 MyBatis 默认的 DefaultSqlSession 实现。在同一应用程序中的不同类之间混杂使用可能会引起数据一致性的问题。

可以使用 SqlSessionFactory 作为构造方法的参数来创建 SqlSessionTemplate 对象。

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
 <constructor-arg index="0" ref="sqlSessionFactory" />
</bean>

现在,这个 bean 就可以直接注入到你的 DAO bean 中了。你需要在你的 bean 中添加一个 SqlSession 属性,就像下面这样:

public class UserDaoImpl implements UserDao {

 private SqlSession sqlSession;

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

 public User getUser(String userId) {
   return sqlSession.getMapper...;
}
}

按下面这样,注入 SqlSessionTemplate:

<bean id="userDao" class="org.mybatis.spring.sample.dao.UserDaoImpl">
 <property name="sqlSession" ref="sqlSession" />
</bean>

整合实现一
1、引入Spring配置文件beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

2、配置数据源替换mybaits的数据源

<!--配置数据源:数据源有非常多,可以使用第三方的,也可使使用Spring的-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
   <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
   <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
   <property name="username" value="root"/>
   <property name="password" value="123456"/>
</bean>

3、配置SqlSessionFactory,关联MyBatis

<!--配置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/kuang/dao/*.xml"/>
</bean>

4、注册sqlSessionTemplate,关联sqlSessionFactory;

<!--注册sqlSessionTemplate , 关联sqlSessionFactory-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
   <!--利用构造器注入-->
   <constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>

5、增加Dao接口的实现类;私有化sqlSessionTemplate

public class UserDaoImpl implements UserMapper {

   //sqlSession不用我们自己创建了,Spring来管理
   private SqlSessionTemplate sqlSession;

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

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

6、注册bean实现

<bean id="userDao" class="com.kuang.dao.UserDaoImpl">
   <property name="sqlSession" ref="sqlSession"/>
</bean>

7、测试

  @Test
   public void test2(){
       ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
       UserMapper mapper = (UserMapper) context.getBean("userDao");
       List<User> user = mapper.selectUser();
       System.out.println(user);
  }

结果成功输出!现在我们的Mybatis配置文件的状态!发现都可以被Spring整合!

<?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>
   <typeAliases>
       <package name="com.kuang.pojo"/>
   </typeAliases>
</configuration>

整合实现二
mybatis-spring1.2.3版以上的才有这个 .

官方文档截图 :

dao继承Support类 , 直接利用 getSqlSession() 获得 , 然后直接注入SqlSessionFactory . 比起方式1 , 不需要管理SqlSessionTemplate , 而且对事务的支持更加友好 . 可跟踪源码查看

图片

测试:

1、将我们上面写的UserDaoImpl修改一下

public class UserDaoImpl extends SqlSessionDaoSupport implements UserMapper {
   public List<User> selectUser() {
       UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
       return mapper.selectUser();
  }
}

2、修改bean的配置

<bean id="userDao" class="com.kuang.dao.UserDaoImpl">
   <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

3、测试

@Test
public void test2(){
   ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
   UserMapper mapper = (UserMapper) context.getBean("userDao");
   List<User> user = mapper.selectUser();
   System.out.println(user);
}

总结 : 整合到spring以后可以完全不要mybatis的配置文件,除了这些方式可以实现整合之外,我们还可以使用注解来实现,这个等我们后面学习SpringBoot的时候还会测试整合!

13、声明式事务

13.1、回顾事务

事务在项目开发过程非常重要,涉及到数据的一致性的问题,不容马虎!

事务管理是企业级应用程序开发中必备技术,用来确保数据的完整性和一致性。

事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。

事务四个属性ACID

原子性(atomicity)

事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用

一致性(consistency)

一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中

隔离性(isolation)

可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏

持久性(durability)

事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中

13.2、测试

将上面的代码拷贝到一个新项目中

在之前的案例中,我们给userDao接口新增两个方法,删除和增加用户;

//添加一个用户

int addUser(User user);

//根据id删除用户

int deleteUser(int id);

mapper文件,我们故意把 deletes 写错,测试!

<insert id="addUser" parameterType="com.kuang.pojo.User">
insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
</insert>

<delete id="deleteUser" parameterType="int">
deletes from user where id = #{id}
</delete>

编写接口的实现类,在实现类中,我们去操作一波

public class UserDaoImpl extends SqlSessionDaoSupport implements UserMapper {

//增加一些操作

  public List<User> selectUser() {
       User user = new User(4,"小明","123456");
       UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
       mapper.addUser(user);
       mapper.deleteUser(4);
       return mapper.selectUser();
  }

//新增

 public int addUser(User user) {
       UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
       return mapper.addUser(user);
  }

//删除

   public int deleteUser(int id) {
       UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
       return mapper.deleteUser(id);
  }

测试

@Test
public void test2(){
   ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
   UserMapper mapper = (UserMapper) context.getBean("userDao");
   List<User> user = mapper.selectUser();
   System.out.println(user);
}

报错:sql异常,delete写错了

结果 :插入成功!

没有进行事务的管理;我们想让他们都成功才成功,有一个失败,就都失败,我们就应该需要事务!

以前我们都需要自己手动管理事务,十分麻烦!

但是Spring给我们提供了事务管理,我们只需要配置即可;

13.3、Spring中的事务管理

Spring在不同的事务管理API之上定义了一个抽象层,使得开发人员不必了解底层的事务管理API就可以使用Spring的事务管理机制。Spring支持编程式事务管理和声明式的事务管理。

编程式事务管理

将事务管理代码嵌到业务方法中来控制事务的提交和回滚

缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码

声明式事务管理: AOP

一般情况下比编程式事务好用。

将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。

将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理。

使用Spring管理事务,注意头文件的约束导入 :

txxmlns:tx="http://www.springframework.org/schema/tx"

http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">

事务管理器

无论使用Spring的哪种事务管理策略(编程式或者声明式)事务管理器都是必须的。

就是 Spring的核心事务管理抽象,管理封装了一组独立于技术的方法。

JDBC事务

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
       <property name="dataSource" ref="dataSource" />
</bean>

配置好事务管理器后我们需要去配置事务的通知

<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
   <tx:attributes>
       <!--配置哪些方法使用什么样的事务,配置事务的传播特性-->
       <tx:method name="add" propagation="REQUIRED"/>
       <tx:method name="delete" propagation="REQUIRED"/>
       <tx:method name="update" propagation="REQUIRED"/>
       <tx:method name="search*" propagation="REQUIRED"/>
       <tx:method name="get" read-only="true"/>
       <tx:method name="*" propagation="REQUIRED"/>
   </tx:attributes>
</tx:advice>

spring事务传播特性:

事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring支持7种事务传播行为:

propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。

propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。

propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。

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

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

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

propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作

Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。

假设 ServiveX#methodX() 都工作在事务环境下(即都被 Spring 事务增强了),假设程序中存在如下的调用链:Service1#method1()->Service2#method2()->Service3#method3(),那么这 3 个服务类的 3 个方法通过 Spring 的事务传播机制都工作在同一个事务中。

就好比,我们刚才的几个方法存在调用,所以会被放在一组事务当中!

配置AOP

导入aop的头文件!

<!--配置aop织入事务-->
<aop:config>
   <aop:pointcut id="txPointcut" expression="execution(* com.kuang.dao.*.*(..))"/>
   <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>

进行测试

删掉刚才插入的数据,再次测试!

@Test
public void test2(){
   ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
   UserMapper mapper = (UserMapper) context.getBean("userDao");
   List<User> user = mapper.selectUser();
   System.out.println(user);
}

思考问题?

为什么需要配置事务?

如果不配置,可能存在数据提交不一致的情况。

且需要我们手动提交控制事务;

事务在项目开发过程非常重要,涉及到数据的一致性的问题,不容马虎!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值