35讲——spring

35讲——spring

完结版【Spring_20】

【环境】
JDK
C:\Users\夏天的风>java -version
java version "9.0.4"
Java(TM) SE Runtime Environment (build 9.0.4+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.4+11, mixed mode)
maven    
D:/java_Moudle/apache-maven-3.6.1    

1、spring【Spring】

1.1、 spring 简介

spring的目的解决企业应用开发的复杂性,他是免费开源的,设计层面框架解决的是业务逻辑层和其他各层的松耦合问题。

只需要编写应用的业务逻辑,提高生产效率,可以用spring开发所有的 java 应用程序

一个系统可以生成各种对对象,这些对象都需要管理,对象之间可能有着依赖关系,例如: 一个班级 由老师和学生组成。那么我们需要有一个容器来管理这些对象。spring 中把每一个需要管理的对象 称为 spring bean 而spring 管理这些 bean 的容器 称为 spring IOC 容器

Spring 的扩展依赖是 AOP 面向切面编程,AOP 可以将分散在各个业务逻辑代码中相同的代码,通过横向切割的方式抽取到一个独立的模块中,比如用户模块有性能监控和异常处理,订单模块也有类似的功能,那么就可以通过AOP 把性能监控和异常处理提出来,统一实现。

有了 spring 可以快速搭建 Java 工程,快速进行业务需求开发

Spring 的重要作用: 使现有的技术更加容易使用,是一个整合了现有技术框架的大杂烩

spring官网: https://spring.io/

<!-- ================= spring-webmvc 注意 有 漏洞 这是跟着教程先用着 Start ================= -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.0.RELEASE</version>
</dependency>
<!-- ================= spring-webmvc 注意 有 漏洞 这是跟着教程先用着 Start ================= -->

【优点】

  • spring 是一个开源的免费的框架(容器)
  • Spring 是一个轻量级的,非入侵式(入侵式:就是在添加某工具时,会破坏原有的生态)的框架
  • 控制反转(IOC) 面向切面编程(AOP)【面试高频】
  • 支持事务的处理,对框架整合的支持!

1.2、组成 (七大模块、三大思想)

img

【七大模块】

Spring 由七大模块组成,分别是Spring 核心容器(Spring Core)、应用上下文(Spring Context)、Spring面向切面编程(Spring AOP)、JDBC和DAO模块(Spring DAO)、对象实体映射(Spring ORM)、Web模块(Spring Web)以及MVC模块(SpringWebMVC)。

1、核心容器(Spring Core)

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

2、应用上下文(Spring Context)

实现了ApplicationContext接口,Spring的上下文,拓展了核心容器,提供事件处理、国际化等功能。它还提供了一些企业级服务的功能,提供了JNDI、EJB、RMI的支持。

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

提供切面支持,是个轻量级的容器。Spring管理的任何对象都支持AOP,SpringAOP模块基于Spring的应用程序中的对象提供了事务管理服务,通过使用SpringAOP,就可以将声明性事务管理集成在应用程序中。

4、JDBC和DAO模块(Spring DAO)

提供对JDBC的支持,还提供了DAO的支持,提供事务支持。

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

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

ORM:Object Relational Mapping,指对象实体映射。Spring插入了若干个ORM框架,提供了ORM对象的关系工具,其中包括Hibernate,JDO和IBatisSQL Map等,所有这些都遵从Spring的通用事务和DAO异常层次结构。

6、Web模块(Spring Web)

拓展了Spring上下文,提供Web应用上下文,对Web开发提供功能上的支持,如请求、表单、异常等。

7、MVC模块(SpringWebMVC)

MVC框架是一个全功能的构建Web应用程序的MVC实现,通过策略接口,MVC框架编程高度可配置的,MVC容纳了大量视图技术,其中包括JSP,POI等,模型由JavaBean来构成,存放于m当中,而视图是一个接口,负责实现模型,控制器表示逻辑代码,由c的事情。

spring框架的功能可以用在任何J2EE服务器当中,大多数功能也适用于不受管理的环境,spring的核心要点就是支持不绑定到特定J2EE服务的可重用业务和数据的访问对象,毫无疑问这样的对象可以在不同的J2EE环境,独立应用程序和测试环境之间重用。

【三大思想】

IOC思想:Inversion of control,控制反转,强调的是原来在程序中创建Bean的权利反转给第三方。在传统开发模式中,我们需要这个对象的话,需要直接new出自己所需要的对象,也就是所依赖的对象由自己控制。而有了IOC容器后,我们将所需要的对象直接注入到IOC容器里,不需要直接创建对象,当我们需要时,我们可以直接从容器中获取相应的对象。也就是说我们将所需要的对象交给第三方管理,当我们需要时,我们不需要直接创建对象,我们可以从第三方那里获取自已所需要的对象。

DI思想:Dependency Injection,依赖注入,强调Bean之间的关系,这种关系第三方负责去设置。也就是说DI思想是用来实现松耦合,以便于开发测试。

aop思想:ASpect Oriented Programming,面向切面编程,功能的横向抽取,主要实现方式Proxy。它是面向对象编程oop的升华。oop是纵向对一个事物的抽象,一个对象包括静态的属性信息,包括动态的方法信息等。而aop是横向的对不同事物的抽象,属性与属性、方法与方法、对象与对象都可以组成一个切面,而用这种思维去设计编程的方式叫做面向切面编程。

1.3、拓展

image-20231029155243136

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

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


2、IOC 理论 推导【Spring_01_IOC01】

【Spring_01_IOC01】

在我们之前的业务中,用户的需求可能会影响到我们原来的代码,我们需要根据用户的需求去修改代码 !如果策划给你续代码量十分大,修改一次的成本代价十分昂贵!

我们使用一个 Set 接口 让多个实现类 实现同一个接口时,我们可以根据需求 set 改变 实现的方式

// 利用 set 接口实现动态实现方式的注入
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
  • 之前,程序是主动创建对象! 控制权在 代码上
  • 使用 set 注入后,代码不在具有主动性,而是变成被动的接受对象

这种思想,从本质上解决了问题,我们不用管理对象的创建了。系统的耦合性大大降低,可以更加专注在业务的实现上,这就是 IOC 的原型!

image-20231030202628832

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

IOC 是Spring框架的核心内容,使用很多种方式完美的实现了 IOC,可以使用 xml 配置,也可以使用注解,新版本的Spring 也可以零配置 IOC。

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

image-20231030203012080

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

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


3、Hello Spring【Spring_02_HelloSpring】

3.1、导入 Jar 包

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

3.2、 编写实体类

package com.xiao.pojo;

public class Hello {

    private String str;


    public void setStr(String str) { // 使用 Spring  一定要 set 方法
        this.str = str;
    }

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

3.3 编写 配置文件 ApplicationContext.xml

configUration Application 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--bean就是java对象 , 由Spring创建和管理-->
<!-- 使用 Spring 来创建对象,在Spring 这些都称为 Bean
    类型 变量名 = new 类型();
    Hello hello = new Hello();

    id = 变量名 hello
    class = new 的对象 要写完全限定名;
    property 相当于给对象中的属性设置一个值
-->
    <bean id="hello" class="com.xiao.pojo.Hello">
        <!--注意: 这里的name并不是属性 , 而是set方法后面的那部分(setStr) , 首字母小写-->
        <!--引用另外一个bean , 不是用value 而是用 ref-->
        <property name="str" value="Spring"/><!-- ref: 引用 Spring 容器中创建好的对象    value: 具体的值,基本数据类型!    -->
<!--   本质是调用了 setStr() 方法 如果你实体类中 没有 set 方法 会报错-->
    </bean>

</beans>

3.4、测试

import com.xiao.pojo.Hello;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void MyTest(){
        // 获取 Spring 的上下文对象 可以传递多个 (类路径xml配置应用上下文)
        ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");

        // 我们的对象现在都在 Spring 中管理了,我们要使用 直接去里面取出来就可以

        Hello hello = (Hello)context.getBean("hello");

        System.out.println(hello.toString());
    }
}
Result

【思考】

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

这个过程就叫控制反转:

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

依赖注入: 就是利用 set方法注入的

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


3.5、 附加修改案例一【Spring_01_IOC01】为【Spring_03_IOC02】

【修改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
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="daoImpl" class="com.xiao.dao.UserDaoImpl"/>
    <bean id="mysqlImpl" class="com.xiao.dao.UserDaoMySqlImpl"/>
    <bean id="oracleImpl" class="com.xiao.dao.UserDaoOraclelmpl"/>

    <bean id="userServiceImpl" class="com.xiao.service.UserServiceImpl">
<!-- ref : 引用我们配置好的 bean || 我们只要改变 不同 的 bean 就能实现 切换实现类 -->
        <property name="userDao" ref="oracleImpl"/>
    </bean>

</beans>

【测试】

@Test
public void MyTest(){
    // 获取 Spring 的上下文对象 可以传递多个
    ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");

    UserServiceImpl userServiceImpl = (UserServiceImpl)context.getBean("userServiceImpl");
    userServiceImpl.getUser();
}
Result

从改代码到改配置


4、Spring 配置【Spring_05】

【Spring_05】

4.1 别名

<bean id="user01" class="com.xiao.pojo.User">
    <property name="name" value="项晓忠"   />
</bean>
<!--  ======================== 默认无参构造创建Bean End ========================   -->
<!--  ======================== 5、Spring 配置 Start ========================   -->
<!--  =========== 5.1、别名 Start  ==========   -->
<alias name="user01" alias="aliasUser"/>
<!-- user01 取了一个别名为 abcUser 原名仍然能用 -->
<!--  =========== 5.1、别名 配置 End ==========   -->

4.2、Bean 配置

<!--
id: bean 的唯一标识符,可以看做 对象名
class: bean 对象所对应的 完全限定名 : 包名+类型
name: 也是别名,而且更强大   空格逗号分号隔开 可以输入多个别名
-->
<bean id="user02" class="com.xiao.pojo.User" name="user2,u2,u3">
        <property name="name" value="项晓忠"   />
</bean>

4.3、import

import 一般用于团队开发使用,他可以将多个配置文件,导入合并为一个(隐性去重)

<?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">
    <import resource="ApplicationContext.xml"/>
    <import resource="ApplicationContext01.xml"/>
    <import resource="ApplicationContext02.xml"/>
</beans>
我在容器中提取 user01时,他把 user02 也实例化了,说明一个配置文件中,他至少是默认实例化所有对象的(纠正)
//这句话就已经实例化容器内的所有对象了
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
----

5、bean的注入方式

5.1 、构造器注入 (IOC创建bean(对象)方式)【Spring_04_IOC03】

【Spring_04_IOC03】

【无参构造】

  1. 默认使用无参构造创建Bean

    <!-- 
     public User() {
    System.out.println("User 无参构造");
    }-->
    <bean id="user" class="com.xiao.pojo.User">
            <property name="name" value="项晓忠"   />
    </bean>
    

【有参构造】

  1. 下标构造

    <!--  根据第几个参数 来赋值  -->
           <bean id="user02" class="com.xiao.pojo.User02">
               <constructor-arg index="0" value="xxz"/>
               <property name="age" value="18"   />
           </bean>
    
  2. 类型构造

    <!--  根据参数类型 来赋值 -->
    <bean id="user02" class="com.xiao.pojo.User02">
        <constructor-arg type="java.lang.String" value="有参构造(参数类型)创建Bean"/>
    </bean>
    
  3. 名称构造 【推荐】

    <!--  根据参数名字 来赋值 -->
    <bean id="user02" class="com.xiao.pojo.User02">
        <constructor-arg name="name" value="3.有参构造(参数名字)创建Bean"/>
    </bean>
    

猜想 同一个容器里的 对象相同

@Test
public void myTest01(){
    // 1. 实例化容器 (实例化容器内的所有对象|| 没有无参构造会报错  ) 
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
// 猜想 同一个容器里的 对象相同
    User02 user02 = (User02)applicationContext.getBean("user02"); // 有参构造
    User02 user002 = (User02)applicationContext.getBean("user02"); // 有参构造
    
    System.out.println("user02==user002---是否相同--->"+(user02==user002));// true
}

5.2、DI 依赖注入 (Dependency Injection) 【重点】【Spring_06】

【Spring_06】

  • 依赖注入: 其实是 Set 注入(没有set 方法是行不通的)
    • 依赖: ben 对象的创建依赖于容器
    • 注入: bean 对象中的所有属性,由容器来注入!

【环境搭建】

  1. 复杂类型

    @Getter
    @Setter
    @ToString
    public class Address {
        private String address;
    
    }
    
  2. 真实测试对象

    @Getter
    @Setter
    @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. 复杂类型的注入

    <?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="address" class="com.xiao.pojo.Address">
            <property name="address" value="浙江省丽水市白云街道*********"/>
        </bean>
    
        <bean id="student" class="com.xiao.pojo.Student">
    <!--    1. 普通值注入用value    -->
    <!--        <property name="name" value="项晓忠"/>-->
            <property name="name">
                <value>项晓忠</value>
            </property>
    <!--    2. bean注入用ref    -->
            <property name="address" ref="address"/>
    <!--    3. 数组注入用array    -->
            <property name="books" >
                <array>
                    <value>《西游记》</value>
                    <value>《水浒传》</value>
                    <value>《红楼梦》</value>
                    <value>《三国演义》</value>
                </array>
            </property>
    <!--    4. List注入    -->
            <property name="hobbys">
                <list>
                    <value>听歌</value>
                    <value>看电影</value>
                    <value>敲代码</value>
                </list>
            </property>
    <!--    5. map注入    -->
            <property name="card">
                <map>
                    <entry key="身份证" value="111111222222223333"/>
                    <entry key="银行卡" value="148918491161916434"/>
                </map>
            </property>
    <!--    5. set注入    -->
            <property name="games">
                <set>
                    <value>英雄联盟</value>
                    <value>王者荣耀</value>
                    <value>穿越火线</value>
                </set>
            </property>
    <!--    6. properties注入    -->
            <property name="info">
                <props>
                    <prop key="class">201752班</prop>
                    <prop key="sex">男</prop>
                    <prop key="username">root</prop>
                    <prop key="password">123456</prop>
                </props>
            </property>
    <!--    7. null注入    -->
            <property name="wife">
                <null/>
            </property>
    <!--    8. 空注入    -->
            <property name="girlFriend" value=""/>
        </bean>
    
    </beans>
    
  4. Result

    Student(name=项晓忠, address=Address(address=浙江省丽水市白云街道*********), books=[《西游记》, 《水浒传》, 《红楼梦》, 《三国演义》], hobbys=[听歌, 看电影, 敲代码], card={身份证=111111222222223333, 银行卡=148918491161916434}, games=[英雄联盟, 王者荣耀, 穿越火线], info={password=123456, sex=男, class=201752班, username=root}, wife=null, girlFriend=)
    
    Process finished with exit code 0
    
    

5.3 拓展方式(p、c 命名空间)【Spring_07】

【Spring_07】


pojo

Address
package com.xiao.pojo;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class Address {
    private String address;

}
User
package com.xiao.pojo;

import lombok.*;

@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private String name;
    private String age;

    private Address address;

}

UserContext.xml【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:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="address" class="com.xiao.pojo.Address" p:address="浙江省丽水市白云街道*********"/>
<!--
       P标签(Property): xmlns:p="http://www.springframework.org/schema/p"     使用 set 注入 依赖get set
       C标签(Constructor): xmlns:c="http://www.springframework.org/schema/c"  使用 Constructor 注入 依赖构造器
-->
    
<!--  ========== p命名空间注入(set 注入) Start ==========  -->
<!-- p命名空间注入(set 注入),可以直接注入属性的值: property
基本类型  p:name="xxxx";
引用类型  p:address-ref="xxxx";
-->
    <bean id="pUser" class="com.xiao.pojo.User" p:name="项晓忠-set" p:age="24-set" p:address-ref="address"/>
<!--  ========== p命名空间注入(set 注入) End ==========  -->


<!--  ========== c命名空间注入(Constructor 注入) Start ==========  -->
    <!-- p命名空间注入(set 注入),可以直接注入属性的值: property
    基本类型  p:name="xxxx";
    引用类型  p:address-ref="xxxx";
    -->
    <bean id="cUser" class="com.xiao.pojo.User" c:name="项晓忠—Constructor" c:age="24—Constructor" c:address-ref="address"/>
<!--  ========== c命名空间注入(Constructor 注入) End ==========  -->

</beans>

Test

import com.xiao.pojo.User;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void MyTest01(){
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("UserContext.xml");

        // ============  p命名空间 ===================
        User pUser = applicationContext.getBean("pUser", User.class);// 通过反射 来定义类型 就不需要强转了
        System.out.println(pUser.toString());

        // ============  c命名空间 ===================
        User cUser = applicationContext.getBean("cUser", User.class);
        System.out.println(cUser.toString());

    }
}

Result

Result

【注意】

  • p c 命名空间不能直接使用 需要导入 xml约束

           xmlns:p="http://www.springframework.org/schema/p" <!-- 依赖 set 必须有无参构造-->
           xmlns:c="http://www.springframework.org/schema/c" <!-- 依赖 Constructor 必须有有参构造-->
    

5.4、bean 的scope(作用域)【Spring_08】

【Spring_08】


image-20231103212929937


【Sample】

<!--    默认单例模式  (可以显示定义)
        单例模式    scope="singleton"   true    一个容器一个对象
        原型模式    scope="prototype"   false   一次获取一个对象
-->
    <bean id="user"  class="com.xiao.pojo.User" c:name="项晓忠" c:age="18" scope="prototype"/>

【Test】

import com.xiao.pojo.User;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {

    @Test
    public void myTest(){
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user01 = applicationContext.getBean("user", User.class);
        User user02 = applicationContext.getBean("user", User.class);
        System.out.println(user01==user02);
        /*
        单例模式    scope="singleton"   true    一个容器一个对象
        原型模式    scope="prototype"   false   一次获取一个对象
        */

    }
}

6、Bean 的自动装配【Spring_09】

【Spring_09】


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

在 Spring中有三种装配的方式

  1. 在xml中显示装配(第五章内容)
  2. 在java 中显示配置 (没学到)
  3. 隐式的自动装配 bean 【重要】

6.1、手动装配

<!--  ==============  手动 装配  Start ============== -->
    <bean id="dog" class="com.xiao.pojo.Dog"/>
    <bean id="cat" class="com.xiao.pojo.Cat"/>

    <bean id="person" class="com.xiao.pojo.Person">
<!--        使用这种 set 注入要设置无参构造  -->
        <property name="name" value="项晓忠" />
        <property name="dog" ref="dog" />
        <property name="cat" ref="cat" />
    </bean>
<!--  ==============  手动 装配  End ============== -->

6.2、byName自动装配

    <!--  ==============  自动 装配 byName  Start ==============
    autowire="byType" 自动在容器上下文中查找,和自己对象 set 方法后面对应的 bean Id例如  SetCat -> cat
    spring 会把set后第一个字符小写!(不知道真的假的)
    autowire="byType" 自动在容器上下文中查找,和自己对象属性类型对应的 bean  Cat -> cat_byType乱写没事 (他只看类型 不看id,甚至可以不用 id) 【如果存在多个相同类型 那么就报错】
    -->
    <bean id="dog" class="com.xiao.pojo.Dog"/>
    <bean id="cat" class="com.xiao.pojo.Cat"/>

    <bean id="person" class="com.xiao.pojo.Person" autowire="byName">
<!--        使用这种 set 注入要设置无参构造  -->
        <property name="name" value="项晓忠" />
    </bean>
<!--  ==============  自动 装配 byName  End ============== -->

6.3、byType自动装配

<!--  ==============  自动 装配 byType  Start ==============
autowire="byType" 自动在容器上下文中查找,和自己对象 set 方法后面对应的 bean Id例如  SetCat -> cat
spring 会把set后第一个字符小写!(不知道真的假的)
autowire="byType" 自动在容器上下文中查找,和自己对象属性类型对应的 bean  Cat -> cat_byType乱写没事 (他只看类型 不看id,甚至可以不用 id) 【如果存在多个相同类型 那么就报错】
-->

    <bean id="dog" class="com.xiao.pojo.Dog"/>
    <bean id="cat_byType乱写没事" class="com.xiao.pojo.Cat"/>

    <bean id="person" class="com.xiao.pojo.Person" autowire="byType">
<!--        使用这种 set 注入要设置无参构造  -->
        <property name="name" value="项晓忠" />
    </bean>
<!--  ==============  自动 装配 byType  End ============== -->

【注意】

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

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

jdk 1.5 支持的注解 Spring2.5 就支持注解了

注解的使用是要优于xml 配置的

【步骤】

  1. 导入约束

    xmlns:context="http://www.springframework.org/schema/context"   
    
    xsi:schemaLocation="http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd"    
    
  2. 配置注解的支持

    <context:annotation-config/>
    
  3. Sample

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

6.4.1、autowired【Spring_10】

【Spring_10】

@Autowired是一种注解,可以对成员变量、方法和构造函数进行标注,来完成自动装配的工作,@Autowired标注可以放在成员变量上,也可以放在成员变量的set方法上,也可以放在任意方法上表示,自动执行当前方法,如果方法有参数,会在IOC容器中自动寻找同类型参数为其传值

这里必须明确:@Autowired是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Qualifier使用;

百度说是根据类型装配 但是测试发现,同类型下 有一个同属性名 也能成功调用

所以初步猜想: 先匹配类型,再匹配 id

Person

package com.xiao.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.lang.Nullable;

import javax.annotation.Resource;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {

    @Nullable // 属性可以为空
    private String name;


    @Resource(name = "dogResource")//@Resource  需要依赖 javaee-api
    private Dog dog;
    // 如果定义了 required = false 说明这个对象可以为 null 否则不允许为空 默认true  不允许为空

    @Autowired(required = false)
    @Qualifier("catQualifier") //@Autowired是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Qualifier使用;多同类型bean 时可以使用 @Qualifier 好像依赖于 @Qualifier
    private Cat cat;
}

@Resource 依赖 我看它没有添加依赖 可是我要加依赖才能成功运行

        <!-- 添加 Javax Annotation -->
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>8.0.1</version>
            <scope>provided</scope>
        </dependency>

@Autowired是现根据类型进行匹配,然后再根据名称进行匹配。若类型匹配到一个,就直接注入;若类型匹配到多个,会根据名称进行匹配,名称匹配到一个,就注入,名称匹配到多个,就会报错。

@Resource是根据名称进行匹配,然后再根据类型进行匹配。若名称匹配到一个,就直接注入;若名称匹配到多个,就会根据类型匹配,类型匹配到一个,就注入,类型匹配到多个,就会报错。若@Resource(name=“xxx”),则会根据xxx进行匹配,若匹配到一个,就注入;没有找到就报错,不会在根据类型进行匹配。

扩展:
image-20231104014604315

idea 直接可以写 .md


7、使用注解开发【Spring_11】

【Spring_11】


在 Spring 4 之后,要使用注解开发,必须保证 aop 的包导入了

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.0.RELEASE</version>
</dependency>
<!--我们这个包含了 aop 包 所以不需要导入了-->
spring-webmvc(包含了 aop)

使用注解需要导入context约束,增加注解的支持!

Smaple

       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd"
<?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.xiao"/> <!--    指定要扫描的包这个包下的注解就会生效 -->
</beans>

  1. bean

    package com.xiao.pojo;
    
    import lombok.ToString;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    
    // 注意 注解的包是否正确 org.springframework....
    
    @Component//组件注解 说明他被Spring 管理了  等价于  <bean id="user" class="com.xiao.pojo.User"/> id 默认首字母小写
    @ToString
    public class User {
        public String name01 ="项晓忠";
    
    
  2. 属性如何注入

        @Value("项晓忠") // 注解赋值 等价于 <property name="name" value="项晓忠"/>
        public String name02 ;
        public String name03 ;
        @Value("项晓忠")
        public void setName03(String name03) {
            this.name03 = name03;
        }
    
  3. 衍生的注解

    @Component  有几个衍生注解,我们在 web 开发中,会按照 mvc 三层架构分层
    1. Dao -->@Repository
    2. Dao -->@Service
    3. Dao -->@Controller
    这四个注解的功能都是一样的,都是将某个类注册到 Spring 中 装配 Bean 等价于 @Component
    
  4. 自动装配 配置

    请移步 第六小节 [6、Bean 的自动装配【Spring_09】]

  5. 作用域

    @Scope("prototype") // 作用域 singleton prototype  等等
    
  6. 小结

    【xml 与 注解】

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

    【xml 与 注解 最佳实践】

    • xml 用来 管理bean

    • 注解只负责完成属性的注入

    • 我们在使用过程中,注意必须让注解生效,开启注解的支持 和 context约束

             xmlns:context="http://www.springframework.org/schema/context"
             xsi:schemaLocation="
              http://www.springframework.org/schema/context
              https://www.springframework.org/schema/context/spring-context.xsd"
      
          <context:annotation-config/><!--开启注解的支持-->
          <context:component-scan base-package="com.xiao"/> <!--    指定要扫描的包这个包下的注解就会生效 -->
      

8、使用 Java 的方式配置 Spring 【Spring_12】(代替 xml 配置)

【Spring_12】


我们现在完全不使用 SPring 的xml 配置了 ,全权交给 Java 来做

JavaConfig 是 Spring 的一个子项目 Spring 4 后 他成为了 一个核心功能

【两种配置 Spring 方式 已学习的 xml 和 本小节的 纯注解】

applicationContext的所有实现

Pojo

package com.xiao.pojo;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

//@Component 用于声明一个类为Spring的组件,而 @Bean 用于声明一个方法为创建Bean的方法。 在被应用的bean 类上 声明 为 Spring 组件 在 Spring 配置上 @Bean 创建一个 Bean  配合使用
@Component //
@Getter // Bean id 等价与 getUser方法名
@ToString
public class User {
    @Value("项晓忠") // 注入值
    private String name;
}

MyConfig00(等价于 ApplicationContext.xml)

package com.xiao.config;

import com.xiao.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;

// MyConfig00 @Configuration 后本身也会被Spring 容器托管,注册到容器中,因为他本来就是一个 @Component
// @Configuration 代表这是一个配置类 , 等价于我们之前的 ApplicationContext.xml
@Configuration
@ComponentScan("com.xiao.pojo") // 扫描包 指定要扫描的包这个包下的注解就会生效 等价于 <context:component-scan base-package="com.xiao"/>
@Import(MyConfig01.class)// 模拟导入 配置类
public class MyConfig00 {


    /*@Bean
      作用: 注册到容器中,被 Spring 接管了 等价于 <bean id ="getUser方法名" class="xxx.xxx.xxx"/>
      该方法的名称,相当于 bean 标签的 id
      该方法的返回值,相当于 bean 标签的 class 属性
      */
    @Bean
    public User getUser(){
        return new User(); // 返回要注入到 bean 中的对象
    }
}

Test

import com.xiao.config.MyConfig00;
import com.xiao.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;


public class MyTest12 {


    @Test
    public void myTest12(){
        /*
        ApplicationContext 是一个接口 可以用 xml 实现 也可以用 纯 java 实现 等等....
        注意这是纯注解用 java类的实现上下文   new    AnnotationConfigApplicationContext(xxx.class);
        区别于使用 xml 的实现上下文           new   ClassPathXmlApplicationContext("xxx.xml");
         */
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig00.class);

        User user = applicationContext.getBean("getUser", User.class); // Bean id 等价与 getUser方法名

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

    }
}

这种纯 Java 的配置方式,在 SpringBoot 中随处可见 (初步猜想SSM 可以该改成 SpringBoot+SpringMVC+MyBatis? )

Spring Boot 则是一种简化和加速Spring应用程序开发的框架就是 Spring 升级版


9 、代理模式【Spring_13】

23种设计模式之一

代理模式是 Spring Aop 的底层 (面向切面编程 的底层实现)


代理模式的分类

  • 静态代理
  • 动态代理

9.1 静态代理【Spring_13->demo01】

【Spring_13->demo01】


角色分析:

  • 抽象角色: 一般会使用接口或者抽象类来做

  • 真实角色:被代理的角色

  • 代理角色: 代理真实角色,代理真实角色后,我们一般会做一些附属操作

  • 客户:访问代理对象的人

  1. 接口

    package com.xiao.demo01;
    // 租房
    public interface Rent {
        public void rent();
    }
    
    
  2. 真实角色

    package com.xiao.demo01;
    
    // 房东
    public class Host implements Rent{
    
        @Override
        public void rent() {
            System.out.println("房东要出租房子了!");
        }
    }
    
    
  3. 代理角色

    package com.xiao.demo01;
    
    public class Proxy implements Rent {
    
    
        /*对类的功能的扩展,要多用组合,少用继承。
        对于类的扩展,在面向对象的编程过程中,我们首先想到的是类的继承,由子类继承父类,从而完成了对子类功能的扩展。但是,面向对象的原则告诉我们,对类的功能的扩展要多用组合,而少用继承。其中的原因有以下几点:
        第一、子类对父类的继承是全部的公有和受保护的继承,这使得子类可能继承了对子类无用甚至有害的父类的方法。换句话说,子类只希望继承父类的一部分方法,怎么办?
        第二、实际的对象千变万化,如果每一类的对象都有他们自己的类,尽管这些类都继承了他们的父类,但有些时候还是会造成类的无限膨胀。
        第三、 继承的子类,实际上需要编译期确定下来,这满足不了需要在运行内才能确定对象的情况。而组合却可以比继承灵活得多,可以在运行期才决定某个对象。*/
    
        private Host host;
    
        public Proxy() {
        }
    
        public Proxy(Host host) {
            this.host = host;
        }
    
        @Override
        public void rent() {
            this.seeHouse();
            host.rent();
            this.signContract();
            this.getFare();
        }
    
        // 看房
        public void seeHouse(){
            System.out.println("中介带你看房");
        }
        // 签合同
        public void signContract(){
            System.out.println("中介和你签合同");
        }
        // 收中介费
        public void getFare(){
            System.out.println("中介收你中介费");
        }
    }
    
    
  4. 客户端访问代理角色

    package com.xiao.demo01;
    
    // 我 : 租房子的人
    public class Client {
    
        public static void main(String[] args) {
            // 房东要出租房子
            Host host = new Host();
    
            // 本来直接找房东
            host.rent();
            System.out.println("===============");
            // 代理  帮房东租房子,但是 代理一般会有附属操作
            Proxy proxy = new Proxy(host);
    
            // 你不用面对房东,直接找中介租房即可
            proxy.rent();
        }
    
    }
    
    

【静态代理模式的优点】

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

【静态代理模式的缺点】

  • 一个真实角色就会产生一个代理角色: 代码量会翻倍(因为你要调用真实角色的功能) 开发效率变低 (动态代理解决这个问题)

9.2、 静态代理加深理解【Spring_13->demo02】

就是在不改变原有代码上,增加一个代理类,来实现

【Spring_13->demo02】


  • 主要思想 不改变原有代码

我们的接口需求

package com.xiao.demo02;

public interface UserService {
// 增删改查
    public void add();
    public void delete();
    public void update();
    public void query();

}

我们已经做好实现类 但是客户后来想 每次执行时产生日志 怎么办呢?

package com.xiao.demo02;

// 真实对象
public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("add一个用户");
    }

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

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

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

    // 改变公司原有的业务代码 是大忌
}

我们可以用代理来实现 目的 并且不改动原代码

package com.xiao.demo02;

public class UserServiceProxy implements UserService{


    private UserServiceImpl UserServiceImpl;

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

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

    @Override
    public void delete() {
        this.log("delete");
        UserServiceImpl.delete();
    }

    @Override
    public void update() {
        this.log("update");
        UserServiceImpl.update();
    }

    @Override
    public void query() {
        this.log("query");
        UserServiceImpl.query();
    }

    private void log(String msg){
        System.out.println("【INFO】 调用了-->"+msg);
    }

}

image-20231105030941487


9.3、动态代理 【Spring_13->demo03】

【Spring_13->demo03】

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是动态生成的,不是我们直接写好的
  • 动态代理分为两大类: 基于接口的动态代理,基于类的动态代理
    • 基于接口 -- jdk 动态代理
    • 基于类 -- cglib
    • 基于java字节码 -- javasist

需要了解两个类: proxy :代理 invocationHandler 调用处理程序


  1. 代理和真实角色都需要实现
package com.xiao.demo03;
// 租房
public interface Rent {
    public void rent();
}
  1. 真实角色 纯粹的出租房子
package com.xiao.demo03;

// 房东
public class Host implements Rent {

    @Override
    public void rent() {
        System.out.println("房东要出租房子了!");
    }
}
  1. 这次不同了 我们不写静态的代理换成反射的动态代理
package com.xiao.demo03;

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

//等我们会用这个类 自动生成代理类
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);
    }

    @Override // 处理代理实例 并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 动态代理的本质 就是使用反射机制实现!

        seeHouse();
        Object result = method.invoke(rent, args);
        signContract();
        getFare();
        return result;
    }


    // 看房
    public void seeHouse(){
        System.out.println("中介带你看房");
    }
    // 签合同
    public void signContract(){
        System.out.println("中介和你签合同");
    }
    // 收中介费
    public void getFare(){
        System.out.println("中介收你中介费");
    }
}
  1. 使用过程
package com.xiao.demo03;

public class Client {
    public static void main(String[] args) {

        // 真实角色
        Host host = new Host();

        // 代理角色 : 现在没有
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        // 通过调用程序角色来处理我们要调用的接口对象
        pih.setRent(host);
        // 这里的 proxy 是动态生成的 我们并没有写
        Rent proxy = (Rent)pih.getProxy();

        proxy.rent();

    }
}
  1. Result
Result

【动态代理的优点】 看代码理解

package com.xiao.demo04;

public class Client {
    public static void main(String[] args) {
        // 真实角色
        com.xiao.demo04.UserServiceImpl userService = new UserServiceImpl();
        // 代理角色 不存在  【这个万能代理 只需要重新设置真实角色 就可以实现复用】
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        pih.setRent(userService); // 需要代理的真实对象
        UserService proxy = (UserService)pih.getProxy(); // 动态生成代理类

        proxy.add();
        proxy.delete();
        proxy.update();
        proxy.query();
    }
}
  • 可以使真实角色的操作更加纯粹 ,不用关注一些公共的业务
  • 公共的业务就交给代理角色 ,实现了业务的分工
  • 公共业务发生拓展的时候,方便集中管理
  • 一个动态代理代理的是一个接口,一般就是对应的一类业务
  • 一个动态代理类可以代理多个类,只要实现了同一个接口即可

9.4、 动态代理加深理解【Spring_13->demo04】

  1. 首先我们有一顿业务需求 是需要真实角色和代理角色都要实现的

    package com.xiao.demo04;
    
    public interface UserService {
        // 增删改查
        public void add();
        public void delete();
        public void update();
        public void query();
    
    }
    
    
  2. 真实角色

    package com.xiao.demo04;
    
    // 真实对象
    public class UserServiceImpl implements UserService {
        @Override
        public void add() {
            System.out.println("add一个用户");
        }
        @Override
        public void delete() {
            System.out.println("delete一个用户");
        }
    
        @Override
        public void update() {
            System.out.println("update一个用户");
        }
    
        @Override
        public void query() {
            System.out.println("query一个用户");
        }
    
        // 改变公司原有的业务代码 是大忌
    }
    
    
  3. 这个时候 我们改成 【万能动态代理】实现

    package com.xiao.demo04;
    
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    //等我们会用这个类 自动生成代理类  【万能代理】
    public class ProxyInvocationHandler implements InvocationHandler {
        // 被代理的接口
        private Object target;
    
        public void setRent(Object target) {
            this.target = target;
        }
    
        // 生成得到的代理类
        public Object getProxy(){
            return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(), this);
        }
    
        @Override // 处理代理实例 并返回结果
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 动态代理的本质 就是使用反射机制实现!
            log(method.getName()); // 我们可以利用反射 动态的获取 真实角色调用的 方法名词
            Object result = method.invoke(target, args);
    
            return result;
        }
        // 如果客户觉得 UserServiceImpl 功能太小 想加个日志功能 怎么实现呢 如同demo02?
    
        public void log(String msg){
            System.out.println("【录入日志】"+msg);
        }
    
    
    }
    
    
  4. executeProgress

    package com.xiao.demo04;
    
    public class Client {
        public static void main(String[] args) {
            // 真实角色
            com.xiao.demo04.UserServiceImpl userService = new UserServiceImpl();
            // 代理角色 不存在  【这个万能代理 只需要重新设置真实角色 就可以实现复用】
            ProxyInvocationHandler pih = new ProxyInvocationHandler();
            pih.setRent(userService); // 需要代理的真实对象
            UserService proxy = (UserService)pih.getProxy(); // 动态生成代理类
    
            proxy.add();
            proxy.delete();
            proxy.update();
            proxy.query();
        }
    }
    
    
  5. Result

    Result

10、AOP

10.1、什么是AOP

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

img

10.2、Aop在Spring中的作用

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

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

1

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

img

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

10.3、使用 Spring 实现 Aop

我们现在用 Spring 代替 动态代理来做之前的 切面log方法

【重点】 使用 Spring 实现 Aop 需要导入一个 依赖包

<!-- ======= 使用 Spring 实现 Aop Start =======  -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
    <scope>runtime</scope>
</dependency>
<!-- ======= 使用 Spring 实现 Aop End =======  -->

【Aop xml 配置】

xmlns:aop="http://www.springframework.org/schema/aop"
也可以自己动手改
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">

10.3.1、方式一: 使用原生 Spring API 接口【Spring_14】

【Spring_14】


ProjectContructure
  1. interface
package com.xiao.service;

public interface UserService {
// 增删改查
    public void add();
    public void delete();
    public void update();
    public void query();

}
  1. Implment
package com.xiao.service;

// 真实对象
public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("add一个用户");
    }

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

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

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

    // 改变公司原有的业务代码 是大忌
}
  1. 方法前置通知
package com.xiao.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

// 前置通知 方法前
public class BeforeLog implements MethodBeforeAdvice {

/*
method: 要执行的目标对象的方法
args: 参数
target: 目标对象
 */
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable { //如果这里参数 不清楚我们可以下载源码看  Object o  Object target
        System.out.println(target.getClass().getName()+"============"+method.getName()+"============执行前!!!");
    }
}

  1. 方法后置通知
package com.xiao.log;

import org.springframework.aop.AfterReturningAdvice; // 有多个 After 接口可供实用

import java.lang.reflect.Method;

public class AfterLog implements AfterReturningAdvice {

    @Override// returnValue 返回值
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println(method.getClass().getName()+"============"+method.getName()+"============执行后,结果为-->"+returnValue);
    }
}
  1. 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
        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="userServiceImpl" class="com.xiao.service.UserServiceImpl"/>
    <bean id="afterLog" class="com.xiao.log.AfterLog"/>
    <bean id="beforeLog" class="com.xiao.log.BeforeLog"/>

<!-- 方式一: 使用原生 Spring API 接口 -->
<!--    配置 aop: 需要导入 aop 约束 -->
    <aop:config>
    <!--  切入点: expression 表达式 execution(要执行的位置 ! * * * * * )  || .. 表示所有 * 通配-->
        <aop:pointcut id="pointcut" expression="execution(* com.xiao.service.UserServiceImpl.*(..))"/>

    <!--    执行环绕增加-->
        <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>
</beans>

execution 详解

execution()是最经常使用的切点函数,其语法以下所示:函数
 整个表达式能够分为五个部分:this
 一、execution(): 表达式主体。代理
 二、第一个*号:表示返回类型,*号表示全部的类型。对象
 三、包名:表示须要拦截的包名,后面的两个句点表示当前包和当前包的全部子包,com.sample.service.impl包、子孙包下全部类的方法。blog
 四、第二个*号:表示类名,*号表示全部的类。接口
 五、*(..):最后这个星号表示方法名,*号表示全部的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。
  1. Test
import com.xiao.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        // 动态代理的是接口 : 注意点 【和动态代理挂勾 面试的时候可以谈谈】
        UserService userServiceImpl = applicationContext.getBean("userServiceImpl", UserService.class);

        userServiceImpl.add();

    }
}
  1. Result
Result

10.3.2、方式二: 自定义来实现 Aop【Spring_15】


Constructure

【环境不变】

把 log 集成到一个 log 类中 本来是分别实现 after 和 before 接口

package com.xiao.log;

public class DiyAspect {

    public  void diyBefore(){
        System.out.println("===================== 执行前 =====================");
    }

    public  void diyAfter(){
        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
        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 id="diy" class="com.xiao.log.DiyAspect"/>
    <!--注册实现类-->
    <bean id="userServiceImpl" class="com.xiao.service.UserServiceImpl"/>


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

</beans>

Result

result
#### 10.3.3、方式三: 注解来实现 Aop【Spring_16】
  1. 参考 自定义实现 Aop 不同的是用注解来完成

    package com.xiao.log;
    
    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;
    
    // 方式三: 使用注解实现 AOP
    @Aspect // 标注类为切面
    public class AnnotationPointCut {
    
        @Before("execution(* com.xiao.service.UserServiceImpl.*(..))") // 注意 注解的路径  org.aspectj.lang.annotation
        public  void diyBefore(){
            System.out.println("===================== 执行前 =====================");
        }
        @After("execution(* com.xiao.service.UserServiceImpl.*(..))") // 注意 注解的路径  org.aspectj.lang.annotation
        public  void diyAfter(){
            System.out.println("===================== 执行后 =====================");
        }
    
        // 在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点
        @Around("execution(* com.xiao.service.UserServiceImpl.*(..))") // 注意 注解的路径  org.aspectj.lang.annotation
        public void around(ProceedingJoinPoint joinPoint) throws Throwable {
            System.out.println(" ----------------- 环绕前 ----------------- ");
            // 执行方法
            Object proceed = joinPoint.proceed();
    
            Signature signature = joinPoint.getSignature();// 获得签名
            System.out.println("signature: "+signature);
            System.out.println(proceed);
    
            System.out.println("----------------- 环绕后 ----------------- ");
        }
    }
    
  2. 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
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    
        <!--方式三: 注解实现 AOP (切入面)
        proxy-target-class="false" 默认 false JDK
        proxy-target-class="true" 默认 false cglib -->
        <aop:aspectj-autoproxy proxy-target-class="true"/>
    
        <!--注意需要添加 切面 自动代理 的支持-->
        <bean id="annotationPointCut" class="com.xiao.log.AnnotationPointCut"/>
        <!--注册实现类-->
        <bean id="userServiceImpl" class="com.xiao.service.UserServiceImpl"/>
    
    
    </beans>
    
  3. Result

    Result

奇怪的执行顺序

【总结】

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

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


11、 整合 Mybatis

【步骤】

  1. 导入相关 jar 包
  2. 编写配置文件
  3. 测试

11.1、mybatis环境准备

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

代码略 就是 之前 学 mybatis 的时候 查询 User 的数据 而已 传送门 【Spring_17】


11.1.1 mybatis-Spring

<!-- ~~~~~~~~~~~~  mybatis spring Start   ~~~~~~~~~~~~  -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.1.0</version>
</dependency>
<dependency>
<!-- ~~~~~~~~~~~~  mybatis spring End   ~~~~~~~~~~~~  -->
   
什么是 MyBatis-Spring?

MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。它将允许 MyBatis 参与到 Spring 的事务管理之中,创建映射器 mapper 和 SqlSession 并注入到 bean 中,以及将 Mybatis 的异常转换为 Spring 的 DataAccessException。 最终,可以做到应用代码不依赖于 MyBatis,Spring 或 MyBatis-Spring。

MyBatis-Spring  官网

MyBatis-Spring 需要以下版本: (本次演示用的是第二套 详细 见【Spring_17】 中的 pom.xml 配置)

MyBatis-Spring MyBatis Spring Framework Spring Batch Java
3.0 3.5+ 6.0+ 5.0+ Java 17+
2.1 3.5+ 5.x 4.x Java 8+
2.0 3.5+ 5.x 4.x Java 8+
1.3 3.4+ 3.2.2+ 2.1+ Java 6+

11.2、整合MyBatis 和 Spring 方式一【Spring_18】

这个是 17 的升级版 我保留了 17 可以对比学习 【Spring_18】

<!-- ~~~~~~~~~~~~  Spring  jdbc Start   ~~~~~~~~~~~~  
这两个包 版本要一致并兼容 要不然会报错 java.lang.NoSuchMethodError-->
    <artifactId>spring-jdbc</artifactId>
    <artifactId>spring-webmvc</artifactId>
  1. 在原有的基础上 多个了 一个 实现类

    因为 Spring 虽然不用 new 对象了 但是他需要一个类 ,这样能和 Aop 配合使用

package com.xiao.mapper;

import com.xiao.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;

import java.util.List;

public class UserMapperImpl implements UserMapper {

    //  我们在实现类中注入 sqlSessionTemplate(相当于 sqlSession) 让他直接能用
    private SqlSessionTemplate sqlSessionTemplate;

    public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
        this.sqlSessionTemplate = sqlSessionTemplate;
    }

    @Override
    public List<User> getUserList() {
        // 这里和 mybatis 一样 调用 sqlSession 获取 接口.class
        UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class);
        List<User> userList = mapper.getUserList();
        return userList;
    }


}

  1. spring-dao.xml (之一)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- ======= 这里专注于 操作数据库  ======= -->
    <!--    DataSource: 使用 Spring 的数据源替换 mybatis 的配置 c3p0  dbcp  druid
            我们这里使用 Spring 提供的 JDBC : org.springframework.jdbc.datasource
    -->

    <!-- 按住 Ctrl 点击包 也能看源码 -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <!-- name 是规定的 可以点出来的 -->
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;useSSL=true&amp;characterEncoding=utf8"/>
        <property name="username" value="root"/>
        <property name="password" value="xxz123456"/>
    </bean>


    <!--    sqlSessionFactory   需要 mybatis-spring 依赖-->
    <!--    第二步  使用 mybatis-Spring下SqlSessionFactoryBean 构建 sqlSessionFactory
因为我们还是需要使用 sqlSession
SqlSessionFactoryBuilder.build(dataSource)  ~~> sqlSessionFactory ~~> sqlSession
用 spring 无非是
SqlSessionFactoryBean.setDataSource(dataSource)
setConfigLocation(configLocation) ~~ 关联mybatis 或者说接管mybatis
setMapperLocations (mapperLocation) ~~ 注册 mapper
~~> SqlSessionFactoryBean ~~> sqlSessionTemplate
-->
    <!--    class 声明 这个 bean 是什么类-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!--    绑定 mybatis 配置文件  相当于整合配置 其实 spring 能代替 mybatis 的所有配置 configLocation 不是 configuration-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath*:com/xiao/mapper/*.xml"/>

        <!--    classpath*  不加 *  就不能使用 通配符 狂神没加 我可能因为版本高了 所以要加 -->
    </bean>
    <!--    第三步 构建 sqlSessionTemplate-->
    <!-- sqlSessionTemplate: 就是我们使用的 sqlSession-->
    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
        <!--只能使用 构造器注入 sqlSessionFactory 因为他没有set 方法-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>


</beans>

PS: 在写路径或报名时,不要有空格!!!

  1. 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:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- ======= 这里专注于 操作bean  ======= -->
    <!--整合过来-->
<import resource="spring-dao.xml"/>

    <bean id="userMapper" class="com.xiao.mapper.UserMapperImpl">
        <!--setSessionTemplate set 注入 sqlSessionTemplate-->
        <property name="sqlSessionTemplate" ref="sqlSessionTemplate"/>
    </bean>

</beans>
  1. Test
package com.xiao.mapper;

import com.xiao.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

public class MyTest {
    @Test
    public void springTest(){
        ApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("ApplicationContext.xml");
        UserMapper userMapper =
                applicationContext.getBean("userMapper", UserMapper.class);
        List<User> userList = userMapper.getUserList();
        for (User user : userList) {
            System.out.println(user.toString());
        }
//        spring整合mybatis之后,由spring管理的sqlSeesion在sql方法(增删改查等操作)执行完毕后就自行关闭了sqlSession,不需要我们对其进行手动关闭
    }
}

  1. Test
/*
在最开始的时候我们在接口边上写实体类,通过手动写的 jdbc 工具类的调用来实现,增删改查,十分繁琐,特别是建立连接和拼接 sql 语句上,容易出错。 是用实现类来进行操作的。
有了 mybatis 框架后,使用 接口和 xml 配置的方式来实现 增删改查 通过 mybatisUitl 工具类来调用 sqlSession来操作,发生了 革命性的变化,特别是 各种标签 使 sql 语句 变的方便使用,
再加上 Spring 后 将整合 mybatis 和 spring 让对象被Spring 接管 层次分明 更加方便维护和快速开发

*/

11.3、整合MyBatis 和 Spring 方式二【Spring_19】

【Spring_19】

  1. 改变实现类 继承 SqlSessionDaoSupport 后无需声明和注入 sqlSessionTemplate
package com.xiao.mapper;

import com.xiao.pojo.User;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.support.SqlSessionDaoSupport;

import java.util.List;

public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {

    /* 原本需要声明和注入 sqlSessionTemplate 
    现在 通过继承 SqlSessionDaoSupport 后的 getSqlSession() 后拿到 sqlSession就去除这部分
    */
     
    @Override
    public List<User> getUserList() {
        SqlSession sqlSession = getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.getUserList();

        return  userList;
        // 可以仅精简为一行 return  getSqlSession().getMapper(UserMapper.class).getUserList();
    }

}
  1. spring-dao.xml配置 去除了第三步 注入 sqlSession 因为继承 DaoSupport 自带 sqlSession 但是需要传入 sqlSessionFactory
<?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:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- ======= 这里专注于 操作数据库  ======= -->
    <!-- 第一步 用 spring 数据库资源管理 构建数据库资源 DriverManagerDataSource-->

    <!--    第二步  使用 mybatis-Spring下SqlSessionFactoryBean 构建 sqlSessionFactory
    -->
    
   <!-- 去除了第三步 -->
    <!--    第三步 构建 sqlSessionTemplate
    如果使用第二种方式 就不需要 外部给他 sqlSessionTemplate了 因为他继承了 SqlSessionDaoSupport 自己带了 sqlSession
    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg index="0" ref="SqlSessionFactoryBean"/>
    </bean>-->

</beans>
  1. ApplicationContext.xml 原本注入 sqlSession 现在注入 sqlSessionFactory
<?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:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- ======= 这里专注于 操作bean  ======= -->
<!--    导入 整合 Spring-->
    <import resource="spring-dao.xml"/>


<!--    把 UserMapperImpl 托管到 Spring 中
    <bean id="userMapper" class="com.xiao.mapper.UserMapperImpl">
        <property name="sqlSessionTemplate" ref="sqlSessionTemplate"/>
    </bean>-->
<!-- 使用第二种方式就不用 提供 sqlSession  需要提供上家 sqlSessionFactory -->
    <bean id="userMapper" class="com.xiao.mapper.UserMapperImpl">
        <property name="sqlSessionFactory" ref="SqlSessionFactoryBean"/>
    </bean>

</beans>
  1. Test

更牛 mybatis-plus 通用mapper


12、 声明式事务【Spring_20】

【Spring_20】

1、什么是事务

  • 把一组业务当成一个业务来做,要么都成功,要么都失败
  • 确保数据的完整性和一致性

事务ACID 原则

A 原子性 Atomiciry

C 一致性 Consistency

I 隔离性 Isolation

D 持久性

2. spring 中的事务管理

我们先制造一个事务,

比如

我添加某用户 删除某用户 但是 我删除语句写错了 看会不会添加成功

如果没有事务那么肯定会添加成功的

  • 声明式事务 说白了就是 AOP 我们直接在 spring-dao.xml 下加以下代码 改一下 expression 即可

  • 编程式事务: 需要在代码中进行事务的管理 【没讲 有空 再自行拓展】

    初步猜测 我们有一个 sqlSessionTemplate 这个 等价于 sqlSession 所以我们可以利用它 来 sqlSession.rollback() 进行异常回滚

  1. UserMapper.java (interface)
package com.xiao.mapper;

import com.xiao.pojo.User;

import java.util.List;

public interface UserMapper {
    List<User> getUserList();

    // ======= 创建一个事务 Start =======
    // 添加一个用户
    int addUser(User user);

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

    List<User> selectList();
    // ======= 创建一个事务 End   =======
}
  1. UserMapper.xml
  • 这里的 delete 我们故意写错造成一个 事务异常 看看 add 方法会不会回滚
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xiao.mapper.UserMapper">

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


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

    <delete id="deleteUser" parameterType="String">
        deletes from User
    <where>
    id = #{id}
    </where>
    </delete>

</mapper>
  1. UserMapperImpl.java (Implements)

这里要注意的是: 我们在配置 expression 中扫描了

execution(* com.xiao.mapper.*.*(..)

这部分的内容 也就意味着 只会在 com.xiao.mapper 下的所有类所有方法所有返回类型生效 。

打比方 你在测试方法里 做 实现类的内容 那么是不存在事务的

因为测试方法不再该 expression 中

注意Expression的生效包路径
@Override
public List<User>  selectList() {
    UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
    int count = mapper.addUser(new User(5, "5", "5"));
    System.out.println("add 受影响的行数:"+count);
    count = mapper.deleteUser(3);
    System.out.println("delete 受影响的行数:"+count);
    List<User> userList = this.getUserList();
    return userList;
}
  1. spring-dao.xml (配置文件之一添加 Aop 切面即可)
    <!-- ========================== 声明式事务 Start ========================== -->
    <!-- 1. 配置声明式事务   -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 2. 结合 AOP 实现事务的植入 -->
    <!-- 配置事务通知  tx 要导入约束-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!--    给什么方法配置事务 -->
    <!--    配置事务的传播特性 propagation   默认 REQUIRED 其实一般就是用默认其他 了解即可
    如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务。 -->
        <tx:attributes>
            <tx:method name="add" propagation="REQUIRED"/> <!-- 如果 方法里包含 这个 add 就执行事务 -->
            <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>
        <!--  表示匹配任意的返回类型  mapper下的所有类 所有方法 无所谓参数
         注意: 只有在 expression 下配置了 并且整个过程 都在该范围内执行 才有事务
         一开始的时候 我在 test.Test 类里 调用  addUser() deleteUser() 测试 发现是不行的 后知后觉
         expression 相当于作用域-->
        <aop:pointcut id="txPointCut" expression="execution(* com.xiao.mapper.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
    </aop:config>

    <!-- ========================== 声明式事务 End ========================== -->
  1. Test
package com.xiao.mapper;

import com.xiao.pojo.User;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

public class MyTest {


    @Test//  添加 id "30" 删除一个  id "30"
    public void selectUser(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        UserMapper userMapper = context.getBean("userMapper", UserMapper.class);

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

}
  1. Result

拓展

1. java不同的包下相同的类名的问题与解决办法

Java中的类以包进行分类组织,当程序中需要用到某个包下的类时,可以以该类的全限定名进行引用。这样,不同的包中的类就可以同名,不会产生混淆。

但是这样就可能导致引用的时候会产生一些问题。

第一个问题,是Spring中自动注入的问题。

Spring自动注入不同包下的相同类名的类会有点问题。

Spring并不支持不同包下的类名相同的设定。这是因为默认的Spring检索Bean的唯一id(@Service,@Component等注解中的name属性)为类名(Class Name),并不包含包名(Package Name)的信息。

解决的方式有两种:

1.对Bean进行显示命名,例如@Service("userService")。

2.使用XML的方式去声明Bean。

<bean class="com.yanggb.userService">  
...  
</bean>  

这样的话beanName就是类的完全名(PackageName + ClassName)了。

第二个问题,不同的jar里面相同的包名类名怎么去区别导入。

这个问题与标题不符,标题是不同的包,这里的问题是不同的jar,虽然跑题了,但是还是可以归类到这里。

具体问题是,如果两个jar包里面有相同的包,有相同的类名,如果同时引用了这两个jar,就可能会产生引用的类不对的问题。

要知道这个问题产生的原因,就要了解JVM加载的方法。JVM在加载包的时候,是按CLASSPATH的路径从上往下找,找到第一个后就将这个包引用。

这时如果包名和类名都相同,那么JVM就没法区分了。一般来说我们用的IDE都是会提示发生冲突而报错的,如果不报错的话,那么就只有第一个包被引入,即在CLASSPATH路径下排在前面的包,第二个包会在classLoader加载类的时候判断重复而忽略。

知道了JVM加载包的方法,那么解决问题就很容易了,只需要将需要引用包的jar放在前边就可以了,也就是说放在CLASSPATH路径下前沿的位置。

2. Spring相关官方文档

Spring相关官方文档:
Spring Boot中文文档
Spring Framework中文文档
Spring Cloud中文文档
Spring Security中文文档
Spring Session中文文档
Spring AMQP中文文档
Spring Data JPA中文文档
Spring Data JDBC中文文档
Spring Data Redis中文文档

常见错误

Class not found: "MyTest" 我们在移动项目后,不知道 什么原因造成 Idea 某些配置出错;修改回配置即可
通过 target 目录我们发现 他是没有把资源和测试目录打包出去,我们添加 xml 配置 让他们一起打包出去即可
父目录保持干净
类文件具有错误的版本 61.0, 应为 53.0 Error:(4, 26) java: 无法访问org.mybatis.spring.SqlSessionTemplate
错误的类文件: /D:/java_Moudle/apache-maven-3.6.1/maven_repo/org/mybatis/mybatis-spring/3.0.2/mybatis-spring-3.0.2.jar!/org/mybatis/spring/SqlSessionTemplate.class
类文件具有错误的版本 61.0, 应为 53.0
请删除该文件或确保该文件位于正确的类路径子目录中。
1. 确认 版本兼容 2. 删除多余版本
Unrecognised tag: 'repositories' Some problems were encountered while building the effective settings Unrecognised tag: 'repositories' (position: START_TAG seen ...\n\t... @23:16) @ D:\java_Moudle\apache-maven-3.6.1\conf\settings.xml, line 23, column 16
Unrecognised tag: 'repositories' (position: START_TAG seen ...\n\t... @23:16) @ D:\java_Moudle\apache-maven-3.6.1\conf\settings.xml, line 23, column 16
java.lang.NoSuchMethodError java.lang.NoSuchMethodError: org.springframework.beans.factory.config.BeanDefinition.getResolvableType()Lorg/springframework/core/ResolvableType;
...
重复注册mapper Caused by: java.lang.IllegalArgumentException: Mapped Statements collection already contains value for com.xiao.mapper.UserMapper.getUserList. please check com/xiao/mapper/UserMapper.xml and file [Z:\BaiduSyncdisk\同步空间\University\Program\ObjOriented\JavaPro\Java\JavaStudy\SSM框架\Project\Spring_Study\Spring_19\target\classes\com\xiao\mapper\UserMapper.xml]
mysql SSL 异常 一、异常信息
WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification. 翻译过来就是:
警告:不建议在未验证服务器身份的情况下建立SSL连接。根据MySQL 5.5.45+, 5.6.26+和5.7.6+的要求,如果没有设置显式选项,则必须默认建立SSL连接。为了符合不使用SSL的现有应用程序,将verifyServerCertificate属性设置为“false”。您需要通过设置useSSL=false显式禁用SSL,或者设置useSSL=true并为服务器证书验证提供信任存储

最后我是发现,他把我mysql的 driver 版本改到8了 我改回 5.1.47 就好了,也就是说 降低版本即可
org.xml.sax.SAXParseException Caused by: org.xml.sax.SAXParseException; lineNumber: 46; columnNumber: 17; cvc-complex-type.2.4.c: 通配符的匹配很全面, 但无法找到元素 'aop:config' 的声明。 解决方法添加 Aop 约束即可
posted @ 2023-11-08 09:57  项晓忠  阅读(11)  评论(0编辑  收藏  举报