【Spring】Spring学习笔记完整篇

文章目录

Spring

官网 Spring | Home

spring-framework 弹簧框架 (spring.io)

官方文档:[Index of /spring-framework/docs

核心技术 (spring.io)


下载的spring web mvc:

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

1、简介

  1. 2002,首次推出spring框架雏形,interface21框架!

  2. 2004年3月24日,spring 1.0版本发布

  3. Rod Johnson , spring Framework 创始人

  4. spring理念:让现有技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架!

  • SSH Strut2+spring+Hibernate
  • SSM SpringMVC+Spring+Mybatis

官网:Spring Framework

官方下载地址:Index of /spring-framework/docs

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

优点

  • 开源、免费的框架(容器)
  • 轻量级
  • 非入侵式(导入spring框架后不会改变项目原来的结构内容)
  • 控制反转IOC,面向切面编程AOP
  • 支持事务处理
  • 支持框架整合

Spring 就是一个轻量级的控制反转IOC和面向切面编程AOP的框架!

组成

Spring框架由组织成约20个模块的功能组成。这些模块分为核心容器、数据访问/集成、Web、AOP(面向方面的编程)、检测、消息传递和测试,如下图所示。

image-20220802084037659

  • springboot
    • 一个快速开发的脚手架
    • 基于springboot可以快速的开发单个微服务
    • 约定大于配置
  • spring cloud
    • spring cloud 是基于springboot实现的

弊端

发展了太久,违背了原来的理念:配置十分繁琐!

2、IOC 控制反转

总结: IOC就是将对象由Spring去创建,管理,装配!

之前开发:

  1. UserDao接口
  2. UserDaoImpl实现类
  3. UserService业务接口
  4. UserServiceImpl业务实现类

弊端:

用户更改需求的时候,程序员需要改动具体实现接口的代码,很麻烦!

利用生成 Set 接口,发生了革命性变化!

public class UserServiceImpl implements UserService{

    //利用set进行动态实现Dao层接口的注入
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    //利用新new出来的Dao层implement实现类对象,调用Dao层接口方法
    //private UserDao userDao = new UserDaoImpl();
    private UserDao userDao = new UserDaoOracleImpl();

    public void getUser() {

        //userDao.getUser();
        //userDao.getMySql();
        userDao.getOracle();
    }


}
  • 之前,主动创建对象,控制权在程序员手上

  • 现在,使用 Set 注入后,程序员没有了主动性,变成了被动接受的对象,那么控制权交给了谁?

  • 程序员不用再管理对象的创建

  • 系统耦合性大大降低

  • 程序员可以更加专注在业务上

控制什么?

控制创建对象的方式;

谁来控制?

原本应用程序的对象是通过程序本身控制创建,

加入Spring后,对象由Spring来创建;

反转?

程序本身不再创建对象了,而是被动接收Spring创建的对象;

依赖注入DI?

就是利用set方法进行注入;

IOC和DI关系

DI(依赖注入)是实现IOC(控制反转)这个编程思想的一种方式;

IOC本质

  • IOC控制反转 inversion of control
  • IOC是一种编程思想,由主动编程变成被动的接收对象
  • 通过 newClassPathXMLApplicationContext去浏览底层代码
  • IOC是Spring框架的核心内容
  • IOC是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。

image-20220802142638472

3、hello spring

Hello.java

package com.kuang.pojo;

public class Hello {

    private  String str;


    public String getStr() {
        return str;
    }

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

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

beans.xml

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

    <!--使用Spring来创建对象,在spring这些都称之Bean  一个bean == 一个对象    new Hello()


        类型 变量名 = new 类型();
        Hello hello = new Hello();

        id == 变量名
        class == new 的对象
        property == 给对象中的属性设置一个值


    -->
    <bean id="hello" class="com.kuang.pojo.Hello">
        <property name="str" value="Spring"></property>
    </bean>

</beans>

MyTest.java

import com.kuang.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {

    public static void main(String[] args) {

        // 1、获取Spring的上下文对象,拿到Spring容器
        // 允许容器从各种外部资源(如本地文件系统、Java 等)加载配置元数据。ApplicationContext CLASSPATH
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        
        // 2、容器在手,天下我有,需要什么,直接从容器中获取
        // 我们的对象现在都在Spring中管理了,我们要使用对象,不用new,直接从ApplicationContext里面取出来就行
        Hello hello = (Hello) context.getBean("hello");
        System.out.println(hello.toString());
    }
}

测试结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qWHpZJth-1660011316857)(https://s2.loli.net/2022/08/02/93S5JK1R78M6Fyf.png)]

ApplicationContext和ServletContext相同处

【JSP】JSP从基础到入门笔记完整一篇_宋丹敏的博客-CSDN博客

ServletContext

image-20220802143439557

ApplicationContext

用对象的时候去容器中拿

image-20220802143943516

Hello对象由谁创建?

是由Spring容器创建的;

<!--使用Spring来创建对象,在spring这些都称之Bean  一个bean == 一个对象    new Hello()


        类型 变量名 = new 类型();
        Hello hello = new Hello();

        id == 变量名
        class == new 的对象
        property == 给对象中的属性设置一个值


    -->
<bean id="hello" class="com.kuang.pojo.Hello">
    <property name="str" value="Spring"></property>
    </bean>

Hello对象属性如何设置?

由Spring容器设置;

<bean id="hello" class="com.kuang.pojo.Hello">
    
    <!--property 属性 设置对象的属性 -->
    
    <property name="str" value="Spring"></property>
</bean>

ClassPathXmlApplicationContext生成路径

ClassPathXmlApplicationContext生成

4、IOC创建对象的方式

4.1 使用无参构造创建

User.java

package com.kuang.pojo;

public class User {

    private String name;

    // 无参构造
    public User(){
        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);
    }
}

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="user" class="com.kuang.pojo.User">
        <property name="name" value="秦疆"></property>
    </bean>
</beans>

MyTest.java

import com.kuang.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {

    public static void main(String[] args) {

        //获取应用上下文,加载beans.xml配置元数据
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //通过bean 从context中获取User对象
        User user = (User) context.getBean("user");

        user.show();

        //System.out.println(user.getName());

    }
}

运行结果:

image-20220802161632580

这里如果将User实体类中定义的无参构造删除,只写入有参构造方法,就会报错:

image-20220802162140521

所以得出结论:IOC创建对象通过使用无参构造创建

那么只有有参构造时候,如何创建对象?

4.2 使用有参构造创建

第一种:下标赋值

bean.xml

<bean id="user" class="com.kuang.pojo.User">
    <!-- constructor-arg:constructor argument 构造函数参数   
		index="0"表示第一个参数下标为0,value="狂神说Java"-->
    <constructor-arg index="0" value="狂神说Java"></constructor-arg>
</bean>

实例:

User.java

package com.kuang.pojo;

public class User {

    private String name;

    // 有参构造
    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

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

bean.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="user" class="com.kuang.pojo.User">
        <constructor-arg index="0" value="狂神说Java"></constructor-arg>
    </bean>
</beans>

MyTest.java

import com.kuang.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {

    public static void main(String[] args) {

        //获取应用上下文,加载beans.xml配置元数据
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //通过bean 从context中获取User对象
        User user = (User) context.getBean("user");

        user.show();

    }
}

测试结果:

image-20220802163100397

第二种:类型匹配【不建议】

bean.xml

<bean id="user" class="com.kuang.pojo.User">
    <constructor-arg type="java.lang.String" value="秦疆"></constructor-arg>
</bean>

第三种:参数名【推荐】

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

总结

Spring容器, 就类似于婚介所,不管你要不要,婚介所都有已经存在的对象,如果对象你想要,直接获取就行,而婚介所中存在的每个对象都独一无二

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

5、Spring配置说明

5.1 别名alias

bean.xml

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

    <!--别名:如果添加了别名,我们也可以通过别名来获取这个对象-->
    <alias name="user" alias="userNew"></alias>

5.2 Bean的配置

<!--
        id : bean的唯一标识符,也及时相当于我们学的对象名
        class : bean对象对应的全限定名: 包名 + 类名
        name : 给bean创建的对象起名字,name比起alias更高级,可以起多个别名
    -->
<bean id="userT" class="com.kuang.pojo.UserT" name="user2 u2, u3, u4" >
    <property name="name" value="西部开源"></property>
</bean>

5.3 import导入

这个import,一般用于团队开发;

可以将多个配置文件,导入合并为一个;

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

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



</beans>

image-20220802172234995

  • 项目多人开发,多个人复制不同的类开发,不同的类需要注册不同的bean中,我们就可以利用import将所有人的beans.xml合并成一个总的!

6、DI依赖注入

6.1 构造器注入

看4.1 4.2

Spring团队提倡构造函数注入

6.2 set注入【重点】

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

【环境搭建】

Student.java

package com.kuang.pojo;

import java.util.*;

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; //妻子,空指针null
    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> getHobbys() {
        return hobbys;
    }

    public void setHobbys(List<String> hobbys) {
        this.hobbys = hobbys;
    }

    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) +
                ", hobbys=" + hobbys +
                ", card=" + card +
                ", games=" + games +
                ", wife='" + wife + '\'' +
                ", info=" + info +
                '}';
    }
}

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"
       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="西安"></property>
    </bean>

    <bean id="student" class="com.kuang.pojo.Student">

        <!--第一种,普通值注入,String name-->
        <property name="name" value="秦疆"></property>

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

        <!--第三种,数组注入  String[] books-->
        <property name="books">
            <array>
                <value>红楼梦</value>
                <value>西游记</value>
                <value>三国演义</value>
            </array>
        </property>

        <!--第四种:List<String> hobbys-->
        <property name="hobbys">
            <list>
                <value>听歌</value>
                <value>敲代码</value>
                <value>看书</value>
            </list>
        </property>

        <!--第五种:Map<String,String> card;//学生卡-->
        <property name="card">
            <map>
                <entry  key="身份证" value="123712739171293"></entry>
                <entry  key="银行卡" value="234234234324344"></entry>
            </map>
        </property>

        <!--第六种: Set<String> games;-->
        <property name="games">
            <set>
                <value>LOL</value>
                <value>cf</value>
                <value>红警</value>
                <value>吃鸡</value>
            </set>
        </property>

        <!--第七种:String wife; //妻子,空指针null-->
        <property name="wife">
            <null></null>
        </property>

        <!--第八种:Properties info;//学生信息
            Properties
                key1=value1
                key2=value2
                key3=value3-->
        <property name="info">
            <props>
                <prop key="driver">231231</prop>
                <prop key="url">nan</prop>
                <prop key="username">root</prop>
                <prop key="password">231231</prop>
            </props>
        </property>
    </bean>

</beans>

测试类:

import com.kuang.pojo.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {

    public static void main(String[] args) {

        //引入applicationContext.xml配置文件,生成ApplicationContext,要获取对象的时候,只需要从context中获取就行
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //拿到student的对象 == 拿到student的bean
        Student student = (Student) applicationContext.getBean("student");

        //输出student对象中name属性的value值
        System.out.println(student.toString());


        /*
            Student{
                name='秦疆',
                address=Address{address='西安'},
                books=[红楼梦, 西游记, 三国演义],
                hobbys=[听歌, 敲代码, 看书],
                card={
                        身份证=123712739171293,
                        银行卡=234234234324344
                },
                games=[LOL, cf, 红警, 吃鸡],
                wife='null',
                info={
                    password=231231,
                    url=nan,
                    driver=231231,
                    username=root
                }
            }


         */

    }
}

测试结果:获取到了student对象的所有类型注入的值

image-20220803095506233

6.3 拓展方式

image-20220803103220607

具有p命名空间的xml快捷方式注入

userBean.xml

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

具有c命名空间的xml快捷方式注入

userBean.xml

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

User.java

package com.kuang.pojo;

public class User {

    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public User() {

    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

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

userBean.xml

<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命名空间注入,可以直接注入属性的值  p: property 属性注入-->
    <bean id="user" class="com.kuang.pojo.User" p:name="秦疆" p:age="18"></bean>

    <!-- c命名空间注入, c: constructor 构造器注入  User实体类中要写入构造方法-->
    <bean id="user2" class="com.kuang.pojo.User" c:age="18" c:name="狂神"></bean>
</beans>

测试类:

    @Test
    public void test2(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("userBean.xml");
        User user = (User) applicationContext.getBean("user",User.class);
        User user2 = (User) applicationContext.getBean("user2",User.class);

        System.out.println(user.toString());
        //User{name='秦疆', age=18}

        System.out.println(user2.toString());
        //User{name='秦疆', age=18}

    }

注意

p命名和c命名空间不能直接使用,需要导入xml头部约束!

6.4 Bean的作用域

image-20220803103516999

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B7LpyXuk-1660011316868)(https://s2.loli.net/2022/08/03/ogaMd528nmURvYl.png)]

1 单例模式【spring默认机制】singleton

scope="singleton"

image-20220803110844621

userBean.xml

<!-- c命名空间注入, c: constructor 构造器注入  User实体类中要写入构造方法   scope="singleton"-->
    <bean id="user2" class="com.kuang.pojo.User" c:age="18" c:name="狂神" scope="singleton"></bean>

测试类:

    @Test
    public void test2(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("userBean.xml");
        User user = (User) applicationContext.getBean("user",User.class);
        User user2 = (User) applicationContext.getBean("user",User.class);

        System.out.println(user.toString());
        //User{name='秦疆', age=18}

        System.out.println(user2.toString());
        //User{name='秦疆', age=18}

        /*
        hashcode()方法是干什么的?
            hashcode()方法主要配合基于散列的集合一起使用,比如HashSet、HashMap、HashTable。
            当集合需要添加新的对象时,先调用这个对象的hashcode()方法,得到对应的hashcode值,
            实际上hashmap中会有一个table保存已经存进去的对象的hashcode值,
            如果table中没有改hashcode值,则直接存入,
            如果有,就调用equals方法与新元素进行比较,相同就不存了,不同就存入。
         */
        System.out.println(user.hashCode());
        System.out.println(user2.hashCode());
        System.out.println(user == user2);
    }

测试结果:true

单例模式,从bean中拿到的对象是同一个

image-20220803110332137

2 原型模式 prototype

 scope="prototype"

image-20220803111231965

userBean.xml

    <!-- c命名空间注入, c: constructor 构造器注入  User实体类中要写入构造方法-->
    <bean id="user2" class="com.kuang.pojo.User" c:age="18" c:name="狂神" scope="prototype"></bean>

测试类: 同上

测试结果: false

  • 每次从容器中get的时候,都会产生一个新对象!

测试类:

    @Test
    public void test2(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("userBean.xml");
        User user = (User) applicationContext.getBean("user",User.class);
        User user2 = (User) applicationContext.getBean("user2",User.class);

        System.out.println(user.toString());
        System.out.println(user2.toString());

        System.out.println(user.hashCode());
        System.out.println(user2.hashCode());
        System.out.println(user == user2);
    }

测试结果: false

  • 每次从容器中get的时候,都会产生一个新对象!

image-20220803105954271

3 其余的 request、session、application

这些只能在web开发中使用到!

7、Bean的自动装配

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

在Spring中有三种装配方式:

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

7.1 测试

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

Cat.java

package com.kuang.pojo;

public class Cat {

    public void shout(){
        System.out.println("miao~");
    }
}

Dog.java

package com.kuang.pojo;

public class Dog {

    public void shout(){
        System.out.println("wang~");
    }
}

People.java

package com.kuang.pojo;

public class People {

    //人有猫、狗、名字
    private Cat cat;
    private Dog dog;
    private String name;

    public People() {
    }

    public People(Cat cat, Dog dog, String name) {
        this.cat = cat;
        this.dog = dog;
        this.name = 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;
    }


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

}

MyTest.java

import com.kuang.pojo.People;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {

    @Test
    public void test1(){

        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        People people = applicationContext.getBean("people", People.class);
        people.getDog().shout();
        people.getCat().shout();

    }
}

测试结果,环境搭建成功:

image-20220803133532031

7.2 ByName自动装配

通过byname,将cat、dog自动装配到people 的bean中

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="dog" class="com.kuang.pojo.Dog"></bean>
    <bean  id="cat" class="com.kuang.pojo.Cat"></bean>
    <bean id="people" class="com.kuang.pojo.People" autowire="byName">
        <property name="name" value="秦疆"></property>
    </bean>

</beans>

测试类结果:

image-20220803134031615

7.3 ByType自动装配【默认】

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

    <!--
        byName:会自动在容器上下文中查找,和自己对象set方法后的值对应的bendid!
                public void setCat(Cat cat) {this.cat = cat;}  setXXX
        byType:会自动在容器上下文中查找,和自己对象属性相同的bendid!
                private Cat cat;  属性类型不同
                private Dog dog;
                private String name;
    -->
    <bean  id="dog" class="com.kuang.pojo.Dog"></bean>
    <bean  id="cat" class="com.kuang.pojo.Cat"></bean>
    <bean id="people" class="com.kuang.pojo.People" autowire="byName">
        <property name="name" value="秦疆"></property>
    </bean>

</beans>

小结:

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

8、使用注解开发

注解实现自动装配

image-20220803135507985

jdk1.5支持的注解,spring2.5支持注解!

使用注解须知:

  1. 导入约束:context 约束
  2. 配置注解的支持:
<?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!

  • 先找byType,再找byName;
	@Autowired(required = false)
    private Cat cat;
    @Autowired
    private Dog dog;

科普:

@Nullable  字段标记了这个注解,说明这个字段可以为null;

@Resource

  • byName
@Resource(name = "cat")
private Cat cat;
@Resource(name = "dog")
private Dog dog;

区别:

  1. @Autowired 默认 byType 类型查找,当找不到时候,再通过byName

    @Resource 默认byName 类型查找,

当注入在IOC容器中该类型只有一个时,就通过ByType进行装配;
当注入在IOC容器存在多个同一类型的对象时,就是根据ByName进行装配;

@Autowired  直接在属性上使用 or 在set方法上使用(使用了@Autowired后可以将set方法删除)
    使用示例:
    @Autowired
    private Dog dog;
    使用@Autowired就可以不用写set方法了
    
如果现实的定义了Autowired属性为false,说明这个对象可以为null,否则不允许为空 <==> @Nullable
    @Autowired(required = false)
    private Cat cat;
    
    
@Qualifer + @Autowired <==> @Resource


@Nullable  可以为空
    使用示例:
    public People(@Nullable String name) {
        this.name = name;
    }

@Component

组件

User.java

@Component
public class User {


    public String name;

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

}

or

@Component
public class User {

    @Value("kuangshen")
    public String name;
}

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


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



</beans>
  • 衍生的注解

    @Component有几个衍生注解,我们在web开发中,会按照mvc三层架构分层!

    • dao【@Repository】
    • service【@Service】
    • controller【@Controller】
    • pojo【@Component】

    加上注解,就说明你这个类被spring托管了,

    这四个注解功能都是一样的,都是代表将某个类注册到spring中,装配bean!

小结:

xml与注解:

  • xml更加万能,适用于任何场合!维护方便

  • 注解,不是自己类使用不了,维护相对复杂

  • springboot使用注解比较方便,spring使用还是xml比价方便

  • 我们在使用的过程中,只需要注意一个问题,必须要注解生效:

    1. 开启注解标签支持
    2. 扫描指定包下的注解,这个包下的注解就会生效
        <!--开启注解标签-->
        <context:annotation-config/>
        <!--指定要扫描的包,这个包下面的注解就会生效-->
        <context:component-scan base-package="com.kuang"></context:component-scan>
    
    

9、使用Java的方式配置Spring

我们现在要完全不使用spring的 beans.xml 配置,全权交给Java来做!

JavaConfig 是 Spring 的一个子项目,在Spring4之后,它称为了一个核心功能!!!!

User.java

package com.kuang.pojo;


import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

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

    private String name;

    public String getName() {
        return name;
    }

    @Value("qinjiang")  //属性注入值
    public void setName(String name) {
        this.name = name;
    }

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

KuangConfig.java

package com.kuang.config;

import com.kuang.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;
import org.springframework.stereotype.Controller;

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

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

}

KuangConfig.java

package com.kuang.config;


import org.springframework.context.annotation.Configuration;

@Configuration
public class KuangConfig2 {

    // 将KuangConfig2配置类融合入KuangConfig中去 ————
}

MyTest.java

import com.kuang.config.KuangConfig;
import com.kuang.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContextExtensionsKt;

public class MyTest {

    public static void main(String[] args) {


        //如果完全使用了配置类方式去做,我们就只通过AnnotationConfig 上下文来获取容器,通过配置类的class对象加载!
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(KuangConfig.class);
        User getUser = (User) applicationContext.getBean("getUser");
        System.out.println(getUser.getName());

    }
}

测试结果:

image-20220804135945818

10、代理模式

  1. 为什么要学习代理模式?

    因为这就是SpringAOP的底层;

10.1 静态代理

什么是静态代理模式?

① 就相当于 房东 – 中介 – 我 之间的关系

image-20220804141342340

② 代理模式分为:静态代理 和 动态代理

角色分析:

  1. 抽象角色:一般会使用接口或者抽象类来解决(租房)
  2. 真实角色:被代理的角色(我,房东)
  3. 代理角色:代理真实角色,代理后,一般会做些附属工作(中介拟定合同,带你看房、收钱 之类的工作)
  4. 客户:访问代理对象的人!

代码步骤:

  1. 接口

    Rent.java

    package com.kuang.demo1;
    
    /*
        租房 : 接口
    
            房东 和 中介 的目的都是租房
     */
    public interface Rent {
    
        public void rent();
    }
    
    
  2. 真实角色

    Landlord.java

    package com.kuang.demo1;
    
    /*
        房东
     */
    public class Landlord implements Rent{
    
        //房东说:要出租房子
    
        public void rent() {
            System.out.println("房东:我要出租房子!");
        }
    
    
    }
    
    
  3. 代理角色

    Proxy.java

    package com.kuang.demo1;
    
    /*
        中介
            可以帮助房东发布租房的消息
            可以帮你签署合同
            可以带你去看房
            可以收中介费
    
     */
    public class Proxy implements Rent{
    
        //中介说:房东要出租房子
    
        private Landlord landlord;
    
        public Proxy(){
    
        }
    
        public Proxy(Landlord landlord) {
            this.landlord = landlord;
        }
    
        public void rent(){
            landlord.rent();
            seeHouse();
            hetong();
            fare();
        }
    
        //看房
        public void seeHouse(){
            System.out.println("中介带你看房");
        }
    
        //签合同
        public void hetong(){
            System.out.println("签租赁合同");
        }
    
        //收中介费
        public void fare(){
            System.out.println("收中介费");
        }
    }
    
    
  4. 客户端访问代理角色

    Client.java

    package com.kuang.demo1;
    
    /*
        客户 :我 访问中介的人
     */
    public class Client {
    
        //我找到中介,得到中介说房东要租房的信息
    
        public static void main(String[] args) {
    
            //先要房东存在,且要出租房子
            Landlord landlord = new Landlord();
            //将房东扔给中介
            Proxy proxy = new Proxy(landlord);
            //中介给我房东出租的房子,此外帮,做一个附属操作
            proxy.rent();
    
        }
    
    
    
    }
    
    

Client.java运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rEMdqMtJ-1660011316876)(C:\Users\16431\AppData\Roaming\Typora\typora-user-images\image-20220804151407378.png)]

优缺点:

好处:

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

缺点:

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

10.2 静态代理加深理解

要求在业务层加入日志功能:

业务需求:要求执行增删改查的时候输入日志;

**普通思路:**给每个增删改查的方法加入日志输出,执行一个,输出一个;但是这样做十分麻烦;

例如像这样:

image-20220804154939080

**代理思想:**做一个代理,对业务层的增删改查进行代理;代理做的事情就是实现日志功能;让dao层和controller层直接接触代理,不接触service层;

就是AOP思想,面向切面编程;

image-20220804154446139

代码示例:spring-08-proxy

UserService.java 接口
package com.kuang.demo2;

//Service层接口
public interface UserService {

    //抽象业务一般就写CRUD

    public void add();
    public void delete();
    public void update();
    public void query();

}

UserServiceImpl.java 真实角色
package com.kuang.demo2;

public class UserServiceImpl implements UserService {

    public void add() {
        System.out.println("增加了一个用户");
    }

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

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

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

LogProxy.java 代理角色
package com.kuang.demo2;

/*
    日志功能,代理来实现
 */
public class LogProxy implements UserService{

    //要代理UserServiceImpl,得先有UserServiceImpl
    UserServiceImpl userService = new UserServiceImpl();

    // 代理本身干的事情是让每个CRUD方法打印出日志;
    private void log(String msg){
        System.out.println("【log】"+msg+"方法执行了");
    }


    //新的CRUD方法,不仅要有原来的实现方法,也要有新增的日志方法

    public void add() {
        log("add");
        userService.add();//原来的实现方法
    }

    public void delete() {
        log("delete");
        userService.delete();//原来的实现方法
    }

    public void update() {
        log("update");
        userService.update();//原来的实现方法
    }

    public void query() {
        log("query");
        userService.query();//原来的实现方法
    }
}

Client.java 客户
package com.kuang.demo2;

public class Client {

    public static void main(String[] args) {

        //我要执行,得先有代理
        //LogProxy()在UserService层原有的基础上加入了日志功能,重新实现了UserService接口,所以,返回结果还是UserService类型
        UserService userService = new LogProxy();
        userService.add();
        userService.delete();
        userService.update();
        userService.query();
    }
}

代理后测试结果:

image-20220804161437430

10.3 动态代理

动态代理实现的三种方法:

  1. jdk实现动态代理—是基于接口的

    InvocationHandler

    Proxy

    在jdk1.8文档中查这两个类,就可以看到里面的方法,进行参考;

  2. cglib实现动态代理—是基于类的

  3. javasist实现动态代理—是基于java字节码的

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

    • 接口
    • 真实角色
    • 代理角色
    • 访问代理的客户
  • 动态代理的代理类是动态生成的,不是我们自己写的!

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

    • 基于接口—JDK动态代理【我们在这里使用】

    • 基于类:cglib

    • java字节码实现:javasist

      Javassist是一个开源的分析、编辑和创建Java字节码的类库

      cglib 、 javasist 能快速改变类的结构,或者动态生成类。

JDK动态代理-InvocationHandler

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

InvocationHandler 代理调用处理程序类

invoke:调用

  • public interface InvocationHandler;
    

    InvocationHandler是由代理实例的调用处理程序实现的接口

    每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。

  • Objectinvoke(Object proxy, 方法 method, Object[] args) 处理代理实例上的方法调用并返回结果。

JDK动态代理实例:

  1. jdk动态代理–基于接口( InvocationHandler、Proxy )的实例:

    spring-08-proxy

    • 抽象角色 租房接口
    • 真实角色 房东
    • 代理角色 中介:代理调用处理器–实现 InvationHandler类
    • 客户角色 我:访问代理的人

    Rent.java

    package com.kuang.demo3;
    
    /*
        租房 : 接口
    
            房东 和 中介 的目的都是租房
     */
    public interface Rent {
    
        public void rent();
    }
    
    

    Landlord.java

    package com.kuang.demo3;
    
    /*
        房东
     */
    public class Landlord implements Rent {
    
        //房东说:要出租房子
    
        public void rent() {
            System.out.println("房东:我要出租房子!");
        }
    
    
    }
    
    

    ProxyInvocationHandler.java

    package com.kuang.demo3;
    
    import com.kuang.demo3.Rent;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /*
        InvocationHandler 调用处理器
            invocation 调用     handler    处理
            是一个接口(是由代理实例的调用处理程序实现的接口)
            只要实现这个接口,就能自动生成代理,并返回代理结果
    
            每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。
    
            Object invoke(Object proxy, 方法 method, Object[] args)
            处理代理实例上的方法调用并返回结果。
    
            所有已知实现类:
                CompositeDataInvocationHandler ,
                EventHandler ,
                MBeanServerInvocationHandler ,
                RemoteObjectInvocationHandler
    
            InvocationHandler 在 java.lang.reflect       包中
                        Proxy 在 java.lang.reflect.Proxy 包中
    
            为某个接口创建Foo代理:
            Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class<?>[] { Foo.class },  handler);
    
     */
    
    //等我们会用到这个类,自动生成代理类!
    public class ProxyInvocationHandler implements InvocationHandler {
    
    
        //被代理的接口
        private Rent rent;
    
        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 {
    
            //代理添加的新功能
            seeHouse();
            fare();
    
            //动态代理的本质,就是使用反射机制实现!
            Object result = method.invoke(rent, args);
            return result;
        }
    
    
    
        private void fare() {
            System.out.println("收中介费!");
        }
    
        private void seeHouse() {
            System.out.println("中介带看房子!");
        }
    
    
    }
    
    

    Client.java

    package com.kuang.demo3;
    
    import com.kuang.demo3.Rent;
    
    public class Client {
    
        public static void main(String[] args) {
    
            //真实角色,客户要拿到信息,虽然有代理(中介),但是真实的角色(房东)也得存在
            Landlord landlord =  new Landlord();
            //代理角色 动态代理,没有代理类,不是真实存在的,是动态生成的,现在没有
            ProxyInvocationHandler pih = new ProxyInvocationHandler();
            //通过 proxyInvocationHandler 调用程序处理角色来处理我们要调用的接口对象!
            pih.setRent(landlord);
    
            //生成代理
            Rent proxy = (Rent) pih.getProxy();
    
            proxy.rent();
    
        }
    }
    
    

    测试结果:代理成功!

    image-20220805135126259

JDK动态代理总结模板:

ProxyInvocationHandler.java

package com.kuang.demo4;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/*
    JDK动态代理类模板: 代理Object
 */

//等我们会用到这个类,自动生成代理类!
public class ProxyInvocationHandler implements InvocationHandler {


    //1、被代理的接口
    private Object target;

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

    //2、生成得到代理类
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    //3、处理代理实例并返回结果
    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("【log】执行了"+msg+"方法");
    }

}

Client.java

package com.kuang.demo4;

import com.kuang.demo2.UserService;
import com.kuang.demo2.UserServiceImpl;

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();
        proxy.query();
        proxy.delete();
        proxy.update();
    }

}

UserService.java

package com.kuang.demo2;

//Service层接口
public interface UserService {

    //抽象业务一般就写CRUD

    public void add();
    public void delete();
    public void update();
    public void query();

}

UserServiceImpl.java

package com.kuang.demo2;

public class UserServiceImpl implements UserService {

    public void add() {
        System.out.println("增加了一个用户");
    }

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

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

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

测试结果:Client.java运行结果,代理成功!

spring-08-proxy

demo2(Service ServiceImpl) and demo4(Client ProxyInvocationHandler)

image-20220805141341498

动态代理的好处:

  • 可以使真实角色的操作更加纯粹!不用关注一些公共的业务

  • 公共的业务就交给了代理角色,实现了业务的分工!

  • 公共业务发生扩展的时候,方便集中!

  • 一个动态代理类代理的是一个接口,一般就是对应的一类业务!

  • 一个动态代理类可以代理多个类,只是实现了同一个接口即可!

静态代理和动态代理区别:

  • 静态代理使用场景:

    要代理的service业务层有接口的时候,使用静态代理,直接将接口重新实现,而重新实现的代理里面包含新的业务功能+原有的业务,而客户(也就是controller层)不再调用原来的service层,而是调用代理的service层;

    静态代理是将代理写死的!

  • 动态代理使用场景:

    没有接口的时候,只有类,这时候要实现代理,就只能使用动态代理,通过类自动生成代理对象;这里面就是反射思想,通过类的class对象创建实例;

11、AOP 面向切面编程

11.1 AOP是什么?

  • AOP: Aspect Oriented Programming 面向切面编程

  • 是OOP的一种延伸;

  • AOP框架:Aspects 框架

  • Spring框架把Aspects框架也集成进来,用来实现AOP面向切面编程

  • 面向切面编程实现就是为了让开发人员专注业务逻辑代码,提高开发效率

  • 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

  • 是函数式编程的一种衍生范型。

  • 利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序可用性,提高开发效率

AOP 面向切面编程作用:

像类似于事务处理、日志、权限判断这些代码在类中很多方法中都要使用,如果每个方法中都要写这些代码,就很繁琐,所以把这些都提取为一个工具类,哪个方法要用它,就在哪里方法中注入就行,这样也更好维护。

POP、OOP、AOP 是什么意思?区别是什么?_宋丹敏的博客-CSDN博客_aop oop pop

image-20220805144219915

11.2 AOP在Spring中的作用?

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

所有增删改完了之后都要通过commit()方法提交事务;

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

img

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CyKDtVN0-1660011316883)(https://s2.loli.net/2022/08/05/zMWrPfNdXuU2GlC.png)]

11.3 Spring中AOP如何实现?

导AOP织入包 aspectjweaver

pom.xml
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.9.1</version>
    <scope>runtime</scope>
</dependency>
log/Log.java
package com.kuang.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;


/*
    MethodBeforeAdvice  前置教育
 */
public class Log implements MethodBeforeAdvice {

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

log/AfterLog.java
package com.kuang.log;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

/*
    AfterReturningAdvice   后置通知
 */
public class AfterLog implements AfterReturningAdvice {

    public void afterReturning(Object returnValue, Method method, Object[] objects, Object target) throws Throwable {

        System.out.println("【afterlog】执行了"+method.getName()+"方法,返回结果为:"+returnValue);
    }
}

service/UserService.java
package com.kuang.service;

public interface UserService {

    //业务层CRUD方法
    public void add();
    public void delete();
    public void update();
    public void query();


}

service/UserServiceImpl.java
package com.kuang.service;

public class UserServiceImpl implements UserService{

    public void add(){
        System.out.println("add了一个用户");
    }

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

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

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

方式一:使用SpringAPI接口

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

    
    <!--方式一:使用原始Spring API接口-->
    <!--配置aop:需要导入aop的约束-->
    <aop:config>
        <!--1、找到将日志功能插入到业务层的位置-->
        <!--pointcut切入点: pression:表达式  execution:(要插入日志方法到接口中,exection()括号里面是要执行的位置!)-->
        <aop:pointcut id="pointcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>

        <!--2、将前置日志,后置日志log,afterLog类引入到切入点里面-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/><!--执行环绕增加-->
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>


    </aop:config>
</beans>

方式二:自定义来实现AOP

diy/DiyPointCut.java
package com.kuang.diy;

public class DiyPointCut {

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

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

    

    <!--方式二:自定义类-->
    <bean id="diy"  class="com.kuang.diy.DiyPointCut"/>

    <aop:config>
        <!--自定义切面,ref引入类-->
        <aop:aspect ref="diy">
            <!--切入点-->
            <aop:pointcut id="point" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
            <!--通知-->
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>

        </aop:aspect>
    </aop:config>


</beans>
MyTest.java执行结果:

image-20220808081750310

方式三:使用注解实现

diy/AnnotationPointCut.java
package com.kuang.diy;


import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
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("环绕前");

        //执行方法(前 Before())
        Object proceed = jp.proceed();

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

        //Signature signature = jp.getSignature(); //获得签名
        //System.out.println("signature"+signature);
        //System.out.println("proceed:"+proceed);


        //执行方法(后 After())
    }

}

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

   

    <!--方式三:使用注解实现AOP-->
    <!--注册bean-->
    <bean id="applicationContext" class="com.kuang.diy.AnnotationPointCut"/>
    <!--开启注解支持   JDK(默认  proxy-target-class="true")  cglib( proxy-target-class="false" )-->
    <aop:aspectj-autoproxy/>

</beans>
MyTest.java
import com.kuang.service.UserService;
import com.kuang.service.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {

    public static void main(String[] args) {

        //要用spring容器中的对象,先拿到ApplicationContext
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //从ApplicationContext中取出想要拿到的对象
        UserService userService = (UserService) applicationContext.getBean("userService");
        //通过拿到的类,调用方法
        userService.add();

    }
}

MyTest.java运行结果:

image-20220808094617307

12、整合MyBatis

步骤:

  1. 导入相关jar包

  2. 编写配置文件

  3. 测试

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>com.kuang</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>spring-10-mybatis</artifactId>

    <dependencies>
        <!--1 junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!--2 mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>
        <!--3 mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.2</version>
        </dependency>
        <!--4 spring-webmvc-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.18</version>
        </dependency>
        <!--5 spring-jdbc spring操作数据库需要这个-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.2.RELEASE</version>
        </dependency>
        <!--6 aopwear aop织入-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.9.1</version>
        </dependency>
        <!--7 mybatis-spring spring整合mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.7</version>
        </dependency>
    </dependencies>

</project>

12.1 回忆mybatis

步骤:

  1. 编写实体类
  2. 编写核心配置文件
  3. 编写接口
  4. 编写Mapper.xml
  5. 测试

pojo/Users.java

package com.kuang.pojo;

import lombok.Data;

@Data  //省略写 get\set\构造\toString
public class Users {

    private String id;
    private String name;
    private String pwd;
}

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>

    <properties resource="jdbc.properties"/>

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


    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="Mapper/UserMapper.xml"/>
    </mappers>
</configuration>

jdbc.properties

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=root

mapper/UserMapper.java

package com.kuang.mapper;

import com.kuang.pojo.Users;

import java.util.List;

public interface UserMapper {

    public List<Users> selectUser();
}

Mapper/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.kuang.mapper.UserMapper">

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

MyTest.java

import com.kuang.mapper.UserMapper;
import com.kuang.pojo.Users;
import jdk.internal.util.xml.impl.Input;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import org.springframework.context.ApplicationContext;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MyTest {

    @Test
    public void test1() throws IOException {
        //获取SqlsessionFactoryBuilder
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        //读取流,获取sqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
        //获取sqlsession,设为true,自动提交
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        //获取要测试的类
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //调用接口中要测试的方法
        List<Users> usersList = userMapper.selectUser();
        for (Users users : usersList) {
            System.out.println(users);
        }

        //关闭sqlsession
        sqlSession.close();

    }
}

测试结果;

image-20220808132012058

12.2 方式一:MyBatis-Spring

中文文档官网:mybatis-spring – http://mybatis.org/spring/zh/index.html

  • 将 MyBatis 代码无缝地整合到 Spring 中。

  • 允许 MyBatis 参与到 Spring 的事务管理之中,

    创建映射器 mapper 和 SqlSession 并注入到 bean 中,将 Mybatis 的异常转换为 Spring 的 DataAccessException

  • 可以做到应用代码不依赖于 MyBatis,Spring 或 MyBatis-Spring。

步骤:

  1. 导jar包

    mybatis-spring jar包 spring-jdbc jar包

  2. 编写数据源DataSource配置

  3. sqlSessionFactory

    引入数据源DataSource

    导入mybatisConfig核心配置文件

    绑定mapper

  4. sqlSessionTemplate 生成sqlSession

  5. 需要给接口加实现类【不同】

  6. 将自己写入实现类,注入MyBatis中

  7. 测试使用

实例:

导入mybatis-spring spring-jdbc jar包
<!--5 spring-jdbc spring操作数据库需要这个-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.22</version>
</dependency>


<!--7 mybatis-spring spring整合mybatis-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.7</version>
</dependency>
pojo/User.java
package com.kuang.pojo;

import lombok.Data;

@Data  //省略写 get\set\构造\toString
public class Users {

    private String id;
    private String name;
    private String pwd;
}

mapper/UserMapper.java
package com.kuang.mapper;

import com.kuang.pojo.Users;

import java.util.List;

public interface UserMapper {

    public List<Users> selectUser();
}

mapper/UserMapperImpl.java
package com.kuang.mapper;

import com.kuang.pojo.Users;
import org.mybatis.spring.SqlSessionTemplate;

import java.util.List;

public class UserMapperImpl implements UserMapper{

    //我们的所有操作在原来,都使用sqlSession来执行,现在,使用sqlSessionTemplated来执行;
    private SqlSessionTemplate sqlSession;

    //通过set方法注入
    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }

    //输出列表
    public List<Users> selectUser() {
        //获取UserMapper对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}

resources/Mapper/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.kuang.mapper.UserMapper">

    <select id="selectUser" resultType="users">
        select * from user
    </select>
</mapper>
applicationContext.xml 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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--applicaitonContext.xml 专门负责注册mapper接口bean,其他配置文件导入即可-->

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

    <!--注册UserMapperImpl-->
    <bean id="userMapper" class="com.kuang.mapper.UserMapperImpl">
        <!--userMapperImpl中使用了sqlSessionTemplate,要将参数引入-->
        <property name="sqlSession" ref="sqlSession"/>
    </bean>
</beans><?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">

    <!--applicaitonContext.xml 专门负责注册mapper接口bean,其他配置文件导入即可-->

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

    <!--注册UserMapperImpl-->
    <bean id="userMapper" class="com.kuang.mapper.UserMapperImpl">
        <!--userMapperImpl中使用了sqlSessionTemplate,要将参数引入-->
        <property name="sqlSession" ref="sqlSession"/>
    </bean>
</beans>
mybatis-config.xml mybaits核心配置文件
<?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>

<!--    <properties resource="jdbc.properties"/>-->

    <!--别名-->
    <typeAliases>
        <package name="com.kuang.pojo"/>
    </typeAliases>

    <!--设置-->
<!--    <settings>
        <setting name="" value=""/>
    </settings>-->


<!--    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="Mapper/UserMapper.xml"/>
    </mappers>-->
</configuration>
spring-dao.xml spring整合mybatis配置文件
<?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">

    <!--DataSource:
        使用Spring的数据源替换MyBatis的配置   c3p0 dbcp druid
        我们这里使用Spring提供的JDBC: org.springframwork.jdbc.datasource -->
    <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=Asia/Shanghai&amp;useUnicode=true&amp;characterEncoding=utf8&amp;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 name="configLocation" value="classpath:mybatis-config.xml"/>
        <!--绑定Mapper-->
        <property name="mapperLocations" value="classpath:Mapper/*.xml"/>
    </bean>

    <!--生成sqlSession的模板 SqlSessionTemplate就是我们使用的sqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!--SqlSessionTemplate只能通过构造方法注入;因为它没有set方法-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>

    <!--注册UserMapperImpl-->
    <bean id="userMapper" class="com.kuang.mapper.UserMapperImpl">
        <!--userMapperImpl中使用了sqlSessionTemplate,要将参数引入-->
        <property name="sqlSession" ref="sqlSession"/>
    </bean>
</beans>
MyTest.java
import com.kuang.mapper.UserMapper;
import com.kuang.pojo.Users;
import jdk.internal.util.xml.impl.Input;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MyTest {


    @Test
    public void test2(){
        //将bean中注册的类导入applicationContext
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //从applicationContext中获取要操作的对象
        UserMapper userMapper = context.getBean("userMapper",UserMapper.class);
        //测试对象中的方法
        for (Users users : userMapper.selectUser()) {
            System.out.println(users);
        }
    }
}

测试结果:

image-20220808150957158

12.3 方式二:extends SqlSessionDaoSupport

mybatis-spring官网中文文档:http://mybatis.org/spring/zh/sqlsession.html

UserMapperImpl2.java

package com.kuang.mapper;

import com.kuang.pojo.Users;

import org.mybatis.spring.support.SqlSessionDaoSupport;

import java.util.List;

/*
    spring整合mybatis方式二: extends SqlSessionDaoSupport
*/
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{


    public List<Users> selectUser() {

/*        //获取sqlSessionTemplate,直接通过getSqlSession方法就可以获取
        SqlSession sqlSession = getSqlSession();
        //获取测试的接口类
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //测试方法
        return mapper.selectUser();*/

        return getSqlSession().getMapper(UserMapper.class).selectUser();
    }
}

applicationContext.xml

<bean id="userMapper2" class="com.kuang.mapper.UserMapperImpl2">
    <!--方式二只需要注入sqlSessionFactory即可,没有属性-->
    <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>

MyTest.java

    @Test
    public void test3(){
        //将bean中注册的类导入applicationContext
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //从applicationContext中获取要操作的对象
        UserMapper userMapper = context.getBean("userMapper2",UserMapper.class);
        //测试对象中的方法
        for (Users users : userMapper.selectUser()) {
            System.out.println(users);
        }
    }

测试结果:

image-20220808152934745

13、声明式事务

13.1 回顾事务

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

事务的ACID原则:

  • 原子性 [atomicity](javascript:😉
    • 把一组业务当成一个业务来做;要么都成功,要么都失败!
  • 一致性 [consistency](javascript:😉
    • 资源加载,提交要保持一致,要么都提交,要么都失败;
  • 隔离性 [isolation](javascript:😉
    • 多个业务可能会操作同一个资源,防止数据损坏
  • 持久性 [durability](javascript:😉
    • 事务一旦提交,无论系统发生什么问题,结果都不会被影响,被持久化的写到存储器中!

事务分为:声明式事务和编程式事务

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

13.2 声明式事务

mybatis-spring中文文档:http://mybatis.org/spring/zh/transactions.html#configuration

声明式事务

配置声明式事务:

在声明式的事务处理中,要配置一个切面,其中就用到了propagation 传播,表示打算对这些方法怎么使用事务,是用还是不用,

其中propagation有七种配置,

REQUIRED、 默认是REQUIRED 必须的

SUPPORTS、

MANDATORY、

REQUIRES_NEW、

NOT_SUPPORTED、

NEVER、NESTED。

image-20220808175430730

实例:

add(User user); 和 delete(int id); 两个方法要么都执行,要么都不执行;

UserMapper.java
package com.kuang.mapper;

import com.kuang.pojo.User;

import java.util.List;

public interface UserMapper {

    public List<User> selectUser();

    /*
        模拟事务失败
     */
    //添加一个用户
    public int add(User user);

    //删除一个用户
    public int delete(int id);

}

UserMapperImpl.java
package com.kuang.mapper;

import com.kuang.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.support.SqlSessionDaoSupport;

import java.util.List;

public  class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {

    //我们的所有操作在原来,都使用sqlSession来执行,现在,使用sqlSessionTemplated来执行;
    private SqlSessionTemplate sqlSession;


    //通过set方法注入
    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }

    //输出列表
    public List<User> selectUser() {


        //事务操作:插入和删除要么都执行,要么都不执行
        User user = new User(9,"10小王","123");
        UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
        mapper.add(user);
        mapper.delete(9);


        //获取UserMapper对象,调用对象中的方法

        return mapper.selectUser();
    }

    public int add(User user) {
        return getSqlSession().getMapper(UserMapper.class).add(user);
    }

    public int delete(int id) {
        return getSqlSession().getMapper(UserMapper.class).delete(id);
    }
}

User.java
package com.kuang.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data  // == get、set、toString
@AllArgsConstructor // == 有参构造
@NoArgsConstructor  // == 无参构造
public class User {

    private int id;
    private String name;
    private String pwd;


}

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

    <insert id="add" parameterType="User">
        insert into user (id,name,pwd) value (#{id},#{name},#{pwd})
    </insert>

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

    <select id="selectUser" resultType="User">
        select id,name,pwd from user
    </select>
</mapper>
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"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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">


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

    <!--配置bean-->
    <bean id="userMapper" class="com.kuang.mapper.UserMapperImpl" >
        <property name="sqlSessionTemplate" ref="sqlSession" ></property>
    </bean>

    <!--spring事务处理 配置声明式事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg name="dataSource" ref="dataSource" />
    </bean>
    <!--结合aop实现事务的织入-->
    <!--配置事务的通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--给哪些方法配置事务-->
        <!--配置事务的传播特性:propagation传播 required必须的 -->
        <tx:attributes>
            <tx:method name="add" propagation="REQUIRED"/>
            <tx:method name="delete" propagation="REQUIRED"/>
            <tx:method name="update" propagation="REQUIRED"/>
            <tx:method name="query" read-only="true"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

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

</beans>
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>

    <!--alias-->
    <typeAliases>
        <typeAlias type="com.kuang.pojo.User" alias="User"/>
        <package name="com.kuang.pojo"/>

    </typeAliases>

    <!--set-->

</configuration>
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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--DataSource:
        使用Spring的数据源替换MyBatis的配置   c3p0 dbcp druid
        我们这里使用Spring提供的JDBC: org.springframwork.jdbc.datasource -->
    <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=Asia/Shanghai&amp;useUnicode=true&amp;characterEncoding=utf8&amp;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 name="configLocation" value="classpath:mybatis-config.xml"/>
        <!--绑定Mapper-->
        <property name="mapperLocations" value="classpath:Mapper/*.xml"/>
    </bean>

    <!--生成sqlSession的模板 SqlSessionTemplate就是我们使用的sqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!--SqlSessionTemplate只能通过构造方法注入;因为它没有set方法-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>


</beans>

测试成功,add和delete方法要么都执行了,要么都没有执行,没有出现add了一条用户信息后,数据库中多了一条用户信息,delete没有成功;

主要就是银行取款存款操作;

为什么需要事务?

  • 如果不配置事务,可能存在数据提交不一致的情况;

    比如:银行存钱操作

  • 如果我们不在spirng中去配置声明式事务,我们就需要在代码中手动配置事务;

  • 事务在项目的开发中十分重要,涉及到数据的完整性和一致性;

  • 事务的ACID原则;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码元宋大米

感谢小主大赏,留言可进互助群~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值