Spring整体学习笔记-IoC依赖注入-AOP代理-整合Spring-Mybatis

13 篇文章 0 订阅
2 篇文章 0 订阅
本文详细介绍了Spring框架的核心概念,包括IoC(依赖注入)原理、Bean的创建方式、DI的setter注入、别名、作用域,以及AOP(面向切面编程)的实现。同时,深入探讨了Spring如何整合Mybatis,提供了两种不同的整合方式。文章最后简要提及了Spring的事务管理,展示了如何通过容器管理事务。
摘要由CSDN通过智能技术生成

Spring

简介

行业春天

解决企业级开发的复杂性,使现有的技术更容易使用,本身是个大杂烩,整合了现有的技术框架

不要重复造轮子

  • SSH:Struct2+Spring+Hibernate
  • SSM:SpringMVC+Spring+Mybatis

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

官方下载地址:https://repo.spring.io/release/org/springframework/spring/
可以下载到所有的版本

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

SpringWebMVC依赖文本

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

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

两个包:

  • springWebMVC
  • springJDBC

Spring优点

  • Spring是一个开源的免费的框架(容器)
  • Spring是一个轻量级,非入侵式框架!
    • 不会对原来项目有影响
  • 控制反转(IOC),面向切面编程(AOP)
  • 支持事务处理,对框架整合的支持

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

Spring组成

spring组成的七大模块

image-20200821075431512

  • 核心容器(SpringCore)
    • 核心容器提供Spring框架的基本功能。spring以bean的方式组织和管理Java应用的各个组件及其关系,spring使用BeanFactory来产生和管理Bean,是工厂模式的实现,BeanFactory使用控制反转(IoC)模式将应用的配置和依赖性规范与实际的应用程序代码分开
  • 应用上下文(Spring Context)
    • Spring上下文是一个配置文件,向spring提供上下文信息,spring上下文包括企业服务、、、、
  • Spring面向切面编程(Spring AOP)
    • AOP(Aspect Oriented Programming)
    • 通过配置管理特性,SpringAOP模块直接将面向方法的编程功能集成在了Spring框架中,Spring管理的任何对象都支持AOP,SpringAOP模块基于Spring的应用程序中的对象提供了事务管理服务,通过使用SpringAOP,不用依赖EJB组件,就可以将声明性事务管理集成在应用程序中
  • JDBC和DAO模块(Spring DAO)
    • Dao(Data Access Object)
    • JDBC、DAO的抽象层,提供了有意义的异常层次结构实现,可用该结构来管理异常处理,和不同数据库提供商抛出的错误信息,异常层次结构简化了错误处理,并且极大的降低了需要编写的代码数量,比如打开和关闭链接。
  • 对象实体映射(Spring ORM)
    • ORM(Object Relational Mapping)
    • Spring插入了若干个ORM框架,提供了ORM对象的关系工具,其中包括Hibernate,JDO和IBatisSQL Map等,所有这些都遵从Spring的通用事务和DAO异常层次结构
  • Web模块(Spring Web)
    • web上下文模块建立应用程序上下文模块之上,基于web的应用程序提供了上下文,所以spring框架支持与Struts集成,web模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作
  • MVC模块(SpringWebMVC)
    • MVC(Model View Controller)
    • MVC框架是一个全功能的构建Web应用程序的MVC实现,通过策略接口,MVC框架编程高度可配置的,MVC容纳了大量视图技术,其中包括JSP,POI等,模型由JavaBean来构成,存放于m当中,而视图是一个接口,负责实现模型,控制器表示逻辑代码,由c的事情。spring框架的功能可以用在任何J2EE服务器当中,大多数功能也适用于不受管理的环境,spring的核心要点就是支持不绑定到特定J2EE服务的可重用业务和数据的访问对象,毫无疑问这样的对象可以在不同的J2EE环境,独立应用程序和测试环境之间重用

Spring介绍

image-20200820153254168

  • SpringBoot
    • 快速开发脚手架
    • 可以快速开发单个微服务,把一个功能单独提取出来就是一个微服务
    • 约定大于配置,主要学习他的配置
    • 学习SpringBoot的前提是要掌握Spring和SpringMVC
  • SpringCloud
    • SpringCloud基于SpringBoot实现的

IOC理论推导

之前使用的流程:

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

在之前的业务中,用户的需求会影响到我们原来的代码,需要根据用户需求去修改源代码,如果程序代码量巨大,修改一次的成本十分昂贵

使用set接口来实现也是一种动态实现 值的注入

  • 之前,程序是主动创建对象,控制权在程序员手上
  • 使用了set注入后,程序不再具有主动性,而是变成了被动的接受对象

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

以前的架构

image-20200821105321848

现在的架构

image-20200821105416863

IOC本质

  • 是一种设计思想,DI(依赖注入)是实现IOC的一种方法,原先创建对象由程序自己控制,控制反转后,创建转移给第三方,获得依赖对象的方式反转了

image-20200821105756534

  • IoC是Spring框架的核心内容
    • 可以使用XML配置
    • 可以使用注解
    • 不配置也行,自动装填
  • 控制反转时一种通过描述(XML或注解)并通过第三方生产或获取特定对象的方式,在Spring中实现控制反转的时IoC容器,其实现方法是依赖注入(Dependency Injection ,DI)

HelloSpring

制作一个简单演示,如何将new对象交给第三方来做,用户可以通过修改xml文件来实现IoC操作

  • 导入包
  • 配置类加载路径应用上下文xml文件
  • 编写JavaBean
  • 测试,通过上下文获取bean来new对象
  1. 导入包

    1. <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-webmvc</artifactId>
          <version>5.2.7.RELEASE</version>
      </dependency>
      
    2. 主要是这个,其他随意

  2. 配置类加载路径应用上下文xml文件

    1. 文件名,spring官方文档是有说的,官方推荐起名为ApplicationContext

    2. 配置xml元数据

    3. 官方有给初始模板

    4. <?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="..." class="...">  
              <!-- collaborators and configuration for this bean go here -->
          </bean>
      
          <bean id="..." class="...">
              <!-- collaborators and configuration for this bean go here -->
          </bean>
      
          <!-- more bean definitions go here -->
      
      </beans>
      
  3. 编写Bean

    1. @Data
      public class Hello {
          private String str;
      }
      
    2. 写好bean之后要写入ApplicationContext

    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
              https://www.springframework.org/schema/beans/spring-beans.xsd">
      
          <bean id="Hello" class="com.haoyun.POJO.Hello">
              <property name="str" value="haoyun"/>
          </bean>
      </beans>
      
    4. 设置一个id,对里面的成员变量可以进行设置

    5. 设置好的Bean会有一个小标志

    6. image-20200821134856756

  4. 测试

    1. public class MyTest {
          @Test
          public void BeanTest(){
              ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("Beans.xml");
              Hello hello = (Hello) classPathXmlApplicationContext.getBean("Hello");
              System.out.println(hello.toString());
          }
      }
      
    2. 填应用上下文的位置,产生出了ApplicationContext对象就能获取里面的Bean了,产生Bean对象

IOC创建对象的方式

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

  2. 使用有参构造,通过构造器创建

    1. @Data
      public class Hello {
          private String name;
          public Hello(String name){
      
          }
      }
      
    2. 添加了一个有参构造函数

    3. <bean id="Hello" class="com.haoyun.POJO.Hello">
          <property name="name" value="sdafsdf"/>
      </bean>
      
    4. 配置没变

    5. 爆出错误提示

    6. image-20200821160505519

    7. 没有找到默认的构造函数

    8. 这时就要看官方文档了,这里找到一篇中文的官方文档

    9. https://www.docs4dev.com/docs/zh/spring-framework/5.1.3.RELEASE/reference/core.html#beans

    10. image-20200821161323423

    11. 里面讲到一个有参构造器的依赖注入问题

依赖注入的两种变体

DI依赖注入存在两种主要变体:ConstructorSetter 的两种依赖注入

Constructor

  1. image-20200821162141834

  2. <beans>
        <bean id="thingOne" class="x.y.ThingOne">
            <constructor-arg ref="thingTwo"/>
            <constructor-arg ref="thingThree"/>
        </bean>
    
        <bean id="thingTwo" class="x.y.ThingTwo"/>
    
        <bean id="thingThree" class="x.y.ThingThree"/>
    </beans>
    
  3. 有参构造函数以这种形式注册,然后包括了下列三种形式

  4. <bean id="exampleBean" class="examples.ExampleBean">
        <constructor-arg type="int" value="7500000"/>
        <constructor-arg type="java.lang.String" value="42"/>
    </bean>
    
  5. 如果参数是简单类型就添加type和value,(根据类型的)

  6. <bean id="exampleBean" class="examples.ExampleBean">
        <constructor-arg index="0" value="7500000"/>
        <constructor-arg index="1" value="42"/>
    </bean>
    
  7. 还可以使用索引,从0开始(根据索引的)

  8. <bean id="exampleBean" class="examples.ExampleBean">
        <constructor-arg name="years" value="7500000"/>
        <constructor-arg name="ultimateAnswer" value="42"/>
    </bean>
    
  9. 消除歧义,加入name,直接规定argument 的name(根据参数名的)

Setter

调用no-argument构造函数,或static工厂方法来实例化Bean

ApplicationContext管理Beans的Constructor和setter的DI,

可以使用properties的格式,但是一般使用XML,或者注解@Controller等

Constructor和Setter是可以混合使用的

Setter注入应该要有合理的默认值,否则,应该添加not-null检查

  • 可以由xml、JavaCode、annotations指定
  • 每个property(属性)或构造函数参数都要设置value的实际定义,或是容器的另一个引用,如参数不是基本类型,就要找到容器中对应bean的reference(引用)

循环依赖

  • classA通过构造函数实注入实现classB,classB又通过构造函数注入实现classA,这种配置bean的相互注入,会被SpringIoC判定为循环reference,并抛出BeanCurrentlyInCreationException
  • 要求beanA和beanB之间必须有一个被完全初始化,再注入另一个,典型的先有鸡还是先有蛋的问题

Setter-Based example

  1. public class ExampleBean {
    
        private AnotherBean beanOne;
    
        private YetAnotherBean beanTwo;
    
        private int i;
    
        public void setBeanOne(AnotherBean beanOne) {
            this.beanOne = beanOne;
        }
    
        public void setBeanTwo(YetAnotherBean beanTwo) {
            this.beanTwo = beanTwo;
        }
    
        public void setIntegerProperty(int i) {
            this.i = i;
        }
    }
    
  2. <bean id="exampleBean" class="examples.ExampleBean">
        <!-- setter injection using the nested ref element -->
        <property name="beanOne">
            <ref bean="anotherExampleBean"/>
        </property>
    
        <!-- setter injection using the neater ref attribute -->
        <property name="beanTwo" ref="yetAnotherBean"/>
        <property name="integerProperty" value="1"/>
    </bean>
    
    <bean id="anotherExampleBean" class="examples.AnotherBean"/>
    <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
    
  3. 和constructor-based的差距真不大,只是多了一点环节而已

  4. <bean id="exampleBean" class="examples.ExampleBean">
        <!-- constructor injection using the nested ref element -->
        <constructor-arg>
            <ref bean="anotherExampleBean"/>
        </constructor-arg>
    
        <!-- constructor injection using the neater ref attribute -->
        <constructor-arg ref="yetAnotherBean"/>
    
        <constructor-arg type="int" value="1"/>
    </bean>
    
    <bean id="anotherExampleBean" class="examples.AnotherBean"/>
    <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
    
  5. wc,东西太多了,根本翻不完,形式太多,表达的又是相近的意思

最简单就直接

<bean id="Hello" class="com.haoyun.POJO.Hello">
	<constructor-arg name="name" value="haoyun"/>
</bean>

真是没事找事

别名alias

<bean id="Hello" class="com.haoyun.POJO.Hello">
    <constructor-arg name="name" value="haoyun"/>
</bean>
<alias name="Hello" alias="safsdf"/>

原来的名字也能用,别名也能用

这个又是没用的,直接再bean标签使用name也能起别名,里面的内容,用逗号或分号或空格都能起别名

<bean id="Hello" class="com.haoyun.POJO.Hello" name="hello afsdfaf,sadfasdf">

import配置文件

之前官网说一个正规的beans命名为applicationContext.xml,这是一个总的配置文件,项目中可以有多个配置文件,这样的好处是,可以由多个人编写,最后汇总,汇总到主配置文件applicationContext.xml

ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml","Beans.xml");

创建ApplicationContext实例的时候可以选用多个配置文件

image-20200821173614726

或者只选择applicationContext.xml配置文件,将其他的配置文件导入applicationContext.xml

<import resource="Beans.xml"/>
<import resource="Beans1.xml"/>
<import resource="Beans2.xml"/>

多个Beans中配置相同的内容是没关系的

DI依赖注入

几种方式注入,Constructor注入,setter注入,拓展方式注入

Set方式注入

  • 依赖注入:set注入
    • 依赖:Bean对象的创建依赖于容器
    • 注入:Bean对象中的说有属性,由容器来注入

搭建环境

  1. 复杂类型
  2. 真实测试对象

直接把官网的举例的所有类型一次做出来

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


    <bean id="address" class="com.haoyun.POJO.Address"/>

    <bean id="student" class="com.haoyun.POJO.Student">
        <!--普通值注入,value-->
        <property name="name" value="asdfafd"/>
        <!--Bean注入,ref-->
        <property name="address" ref="address"/>
        <!--数组注入-->
        <property name="books">
            <array>
                <value>asdfsdf</value>
                <value>asdfasd</value>
                <value>sdfasdfa</value>
                <value>asdfasdfqwer</value>
            </array>
        </property>
        <!--list-->
        <property name="hobbys">
            <list>
                <value>adf</value>
                <value>wer</value>
                <value>trey</value>
            </list>
        </property>
        <!--map-->
        <property name="card">
            <map>
                <entry key="asdf" value="asdfsf"/>
                <entry key="asdfsdf" value="asdfsf"/>
                <entry key="asddsaff" value="asdfsf"/>
            </map>
        </property>
        <!--set-->
        <property name="games">
            <set>
                <value>sadf</value>
                <value>ssadf</value>
                <value>sdadf</value>
            </set>
        </property>
        <!--null-->
        <property name="wife">
            <null/>
        </property>
        <!--properties-->
        <property name="info">
            <props>
                <prop key="sadf">werqwer</prop>
                <prop key="sasdf">werqwer</prop>
                <prop key="saddf">werqwer</prop>
            </props>
        </property>
    </bean>

</beans>
  • value
  • ref
  • list、map、set、properties
  • null
  • 其他类型

p-namespace、c-namespace

命名空间,这两个能进行的操作其他的操作也能实现,还不一定简便了,要先添加配置支持扩展使用命名空间

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

添加在applicationContext.xml上,才可以使用,p和c标签

p表示property,用于有无参构造函数的bean对象的依赖注入操作

c表示Constructor,用于有 有参构造函数的bean对象的依赖注入操作

<!--c:constructor   需要有参构造器-->
<bean id="User" class="com.haoyun.POJO.User" c:age="12" c:name="asdfsf"/>

<!--p:property  需要无参构造器-->
<bean id="User2" class="com.haoyun.POJO.User" p:age="13" p:name="sdf"/>

测试

@Test
public void BeanTest(){
    ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    //Student student = (Student) classPathXmlApplicationContext.getBean("student");
    User user = (User) classPathXmlApplicationContext.getBean("User");
    User user2 = (User) classPathXmlApplicationContext.getBean("User2");
    System.out.println(user.toString());
    System.out.println(user2.toString());

}

Bean-Scopes作用域

image-20200821221256695

image-20200821221402150

  • Singleton 单例
  • Prototype 原型
  • request 请求
  • session 会话
  • application 应用
  • WebSocket 连接

主要使用前两种

Singleton 单例模式

单例模式是Spring的默认机制

只管理Singleton bean 的一个共享实例,带有一个或多个match bean定义的id到导致只返回一个特定的bean实例

定义的bean限定为singleton时,只创建该bean定义的object的一个实例,存储在singleton缓存中,后续的请求都引用缓存中的实例

image-20200821223016035

只创建一个实例
这个共享实例被共享到每个协作对象中

Spring中的singleton bean概念于singleton pettern的概念不同,如果在单个 Spring 容器中为特定 class 定义一个 bean,则 Spring 容器将创建该_ bean 定义所定义的 class 的一个且仅一个实例。

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

<!-- the following is equivalent, though redundant (singleton scope is the default) -->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>

Demo

  • <bean id="studentTest" class="com.haoyun.POJO.Student" >
        <property name="address" ref="address"/>
    </bean>
    <bean id="studentTest2" class="com.haoyun.POJO.Student" >
        <property name="address" ref="address"/>
    </bean>
    <bean id="address" class="com.haoyun.POJO.Address" p:address="address" scope="singleton" />
    
  • address是单例模式的,根据单例模式的性质,应该值会创建一个实例,那只创建一个实例,那些被创建的实例中的address成员使用的应该是同一个引用

  • @Test
    public void BeanTest(){
        ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student studentTest = (Student) classPathXmlApplicationContext.getBean("studentTest");
        Student studentTest2 = (Student) classPathXmlApplicationContext.getBean("studentTest2");
        System.out.println(studentTest);
        System.out.println(studentTest2);
        System.out.println(studentTest == studentTest2);
        System.out.println(studentTest.getAddress() == studentTest2.getAddress());
    }
    
  • image-20200821225802113

  • 得出的结果是两个对象是不同的,但是address是同一个对象,使用同一个reference

ProtoType 原型模式

bean部署的不是单例模式的原型范围都会创建一个新的bean实例,并对该特定bean发出请求,

image-20200821231231713

每个bean都是一个单独的对象

配置和组装object交给client,client code必须清理prototype-scoped object释放原型beans所拥有的昂贵资源,之后可以尝试自定义bean post-processor,释放资源

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

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

其余的request,session,application,这些只能在web开发中使用到,自行理解

Bean的自动装配

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

在spring中有三种装配方式

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

XML Configuration Demo

  • 原先使用的方法

  • <bean id="cat" class="com.haoyun.POJO.Cat"/>
    <bean id="dog" class="com.haoyun.POJO.Dog"/>
    
    <bean id="People" class="com.haoyun.POJO.People">
        <property name="name" value="haoyun"/>
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>
    </bean>
    
  • @Test
    public void CatDogTest(){
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        People people = (People) classPathXmlApplicationContext.getBean("People");
        people.getCat().shout();
        people.getDog().shout();
        classPathXmlApplicationContext.registerShutdownHook();
    }
    
  • 其他类需要使用ref

  • 现在加入两个自动装配的autowire选项

  • byName

    • <bean id="cat" class="com.haoyun.POJO.Cat"/>
          <bean id="dog" class="com.haoyun.POJO.Dog"/>
      <!--    <bean id="dog1" class="com.haoyun.POJO.Dog"/>如果这里的id更改,就找不到了-->
          <!--byName:会自动再容器上下文查找,和自己对象set方法后面值对应的bean id-->
          <bean id="People" class="com.haoyun.POJO.People" autowire="byName">
              <property name="name" value="haoyun"/>
          </bean>
      
  • byType

  • <bean id="cat" class="com.haoyun.POJO.Cat"/>
        <bean class="com.haoyun.POJO.Dog"/>
    <!--    <bean id="dog" class="com.haoyun.POJO.Dog"/> 但是不能配重,全局只能有一个-->
        <!--连bean-id都可以不用-->
        <!--byType:自动在容器的上下文查找,和自己对象相同类型bean-->
        <bean id="People" class="com.haoyun.POJO.People" autowire="byType">
            <property name="name" value="haoyun"/>
        </bean>
    

Annotation-based Container Configuration

使用annotation配置更加简洁,Spring都支持这两种配置风格

spring2.0引用了@Required
spring2.5采用通用的方法来驱动spring依赖注入@Autowired
2.5还添加了JSR-250注释的支持,@PostConstruct和@PreDestroy
(初始化方法)(销毁方法)

需要注册单独的Bean定义,和context命名空间

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

    <context:annotation-config/>

</beans>

主要是添加这三句话

@Autowired

  • 可以放在构造函数上

  • public class MovieRecommender {
    
        private final CustomerPreferenceDao customerPreferenceDao;
    
        @Autowired
        public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
            this.customerPreferenceDao = customerPreferenceDao;
        }
    
        // ...
    }
    
  • 如果目标bean仅定义在一个开头的构造函数,则不需要在这样的构造函数上使用@Autowired,但是如果有几个构造器可用,则必须注释至少一个构造器,告诉容器使用哪一个

  • 还可以放在setter方法上

  • public class SimpleMovieLister {
    
        private MovieFinder movieFinder;
    
        @Autowired
        public void setMovieFinder(MovieFinder movieFinder) {
            this.movieFinder = movieFinder;
        }
    
        // ...
    }
    
  • 可以放在方法上

  • public class MovieRecommender {
    
        private MovieCatalog movieCatalog;
    
        private CustomerPreferenceDao customerPreferenceDao;
    
        @Autowired
        public void (MovieCatalog movieCatalog,
                CustomerPreferenceDao customerPreferenceDao) {
            this.movieCatalog = movieCatalog;
            this.customerPreferenceDao = customerPreferenceDao;
        }
    
        // ...
    }
    
  • 可以应用于字段,甚至可以与构造函数混合使用

  • public class MovieRecommender {
    
        private final CustomerPreferenceDao customerPreferenceDao;
    
        @Autowired
        private MovieCatalog movieCatalog;
    
        @Autowired
        public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
            this.customerPreferenceDao = customerPreferenceDao;
        }
    
        // ...
    }
    
  • 这里使用的@Autowired注解的(member,set,constructor,method)都需要在xml中注入声明,否则在运行时找不到相匹配的(match)

  • <context:annotation-config/>
    
    <bean id="cat" class="com.haoyun.POJO.Cat"/>
    <bean id="dog" class="com.haoyun.POJO.Dog"/>
    
    <bean id="People" class="com.haoyun.POJO.People" p:name="asdfadf"/>
    
  • 需要注入声明

  • 还可以对array类型的字段和方法使用@Autowired

  • public class MovieRecommender {
    
        @Autowired
        private MovieCatalog[] movieCatalogs;
    
        // ...
    }
    
  • set集合

  • public class MovieRecommender {
    
        private Set<MovieCatalog> movieCatalogs;
    
        @Autowired
        public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
            this.movieCatalogs = movieCatalogs;
        }
    
        // ...
    }
    
  • Map

  • public class MovieRecommender {
    
        private Map<String, MovieCatalog> movieCatalogs;
    
        @Autowired
        public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
            this.movieCatalogs = movieCatalogs;
        }
    
        // ...
    }
    
  • required=false,这个对象可以为nul
  • public class SimpleMovieLister {
    
        private MovieFinder movieFinder;
    
        @Autowired(required = false)
        public void setMovieFinder(MovieFinder movieFinder) {
            this.movieFinder = movieFinder;
        }
    
        // ...
    }
    
  • 这个构造函数不传参也可以

  • 使用@Nullable也可以表示可以为null

  • public class SimpleMovieLister {
    
        @Autowired
        public void setMovieFinder(@Nullable MovieFinder movieFinder) {
            ...
        }
    }
    
  • 使用限定自动装配

  • @Qualifier("")

  • public class MovieRecommender {
    
        @Autowired
        @Qualifier("main")
        private MovieCatalog movieCatalog;
    
        // ...
    }
    
  • 算了太长

  • 完全看不完

  • 说下视频中讲解的主要内容吧

  • @Autowired如何实现

    • 先以ByType方式实现

    • 在context中寻找使用@Autowired注解使用的同类型的注解声明,

    • <bean  class="com.haoyun.POJO.Cat"/>
      
      <bean  class="com.haoyun.POJO.Dog"/>
      
      <bean id="People" class="com.haoyun.POJO.People" p:name="asdfadf"/>
      
    • 没有bean-id都没关系,先是根据type寻找

    • <bean  id="cat1" class="com.haoyun.POJO.Cat"/>
      <bean  id="cat2" class="com.haoyun.POJO.Cat"/>
      
      <bean id="dog1" class="com.haoyun.POJO.Dog"/>
      <bean id="dog2" class="com.haoyun.POJO.Dog"/>
      
      <bean id="People" class="com.haoyun.POJO.People" p:name="asdfadf"/>
      
    • 但是出现这种类型相同,就不知道怎么选了,就会报错,需要加入一个bean-id限定@Qualifier("")

    • image-20200822095308174

    • 就会引用到bean-id为dog1上的注入声明

  • @Resouce注解也有同样的用处,不过用的比较多的是@Autowired

  • Resouce是先查找byName的,不行再查找byType

  • Autowired先查找byType,再查找byName

  • @Resource注解中有很多可使用的参数

  • image-20200822095926413

  • name和type的意思应该很明了

  • image-20200822100325650

  • NoUnique不唯一

  • @Resource(type = Dog.class)
    @Resource(name = "cat1")
    
  • 总结一下:

    • @Autowired先以bytype方式查找,之后以byName方式查找

      • 可以定义在很多处位置,但是直接定义在成员上最方便
      • 可以配合@Qualifier注解进行限定
      • 内置required参数可以允许参数为null和@Nullable功能一致
      • 这个注解是spring的
    • @Resource以byName方式查找,之后以byType方式查找

      • 内置很多参数,使用方便
      • 这个注解是java的,在spring还是多用spring的注解吧

使用注解开发

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

image-20200822203315532

  1. bean

  2. 属性如何注入

    1. 在applicationContext.xml中指定要扫描的包,才能使用注解

    2. <!--指定要扫描的包,这个包小的注解就会生效-->
      <context:component-scan base-package="com.haoyun.POJO"/>
      
  3. 衍生的注解

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

      1. image-20200822210108725
      2. 添加后应该看得到叶子
    2. dao【@Repository】

      1. @Repository
        public class User {
            @Value("haoyun")
            private String name ;
        }
        
    3. service【@Service】

      1. @Service
        
    4. controller【@Controller】

      1. @Controller
        

    四个注解功能是一样的,都是代表将某个类注册到Spring中,装配Bean

  4. 自动装配

    1. 之前说过
  5. 作用域

    1. @Scope()

    2. @Scope("singleton")
      
    3. 标记什么模式

  6. 小结

    1. XML与注解
    2. XML适用于任何场合,维护简单
    3. 注解:不是自己的类使用不了,维护相对复杂,
    4. 最佳配合方法
      1. xml管理Bean
      2. 注解只负责完成属性注入
      3. 还是觉得xml好用,配合lambox好用

使用Java的方式配置Spring

完全不使用XML配置文件,全部使用Java的方式来做

  • image-20200822223650533

  • @Configuration
    @ComponentScan("com.haoyun.POJO")
    public class HaoyunConfig {
        @Bean
        public User UserBean() {
            return new User();
        }
    
    }
    
  • 经过@Configuration的配置HaoyunConfig已经成为一个配置类了,类似于Context.xml配置文件

  • @ComponentScan设置查找范围

  • @Bean相当于之前写的Bean标签,id就是方法名

  • 方法的返回值相当于class

  • @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface Configuration {
        @AliasFor(
            annotation = Component.class
        )
        String value() default "";
    
        boolean proxyBeanMethods() default true;
    }
    
  • Configuration的源码中包含了@Component,说明Configuraiton也是一个Component

  • 测试

  • @Test
    public void UserTest() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(HaoyunConfig.class);
        //"com.haoyun.controller"
        /*要与HaoyunConfig中@Bean的method方法名对应*/
        User user = (User) context.getBean("UserBean");
        System.out.println(user.getName());
    
    }
    
  • image-20200822221450569

  • 进入ApplicationContext就可以看到里面有很多实现类实现了这个接口

  • ApplicationContext的实现类有很多,ClassPathXmlApplictionContext已经学习过,从xml文件中加载应用上下文

  • 现在要通过注解配置应用上下文,所以使用AnnotationConfigApplicationContext

  • AnnotationConfigApplicationContext构造函数有几种

  • image-20200822225620412

  • 传递class的string的,还有一个bean工厂的

  • 选用class或配置类的路径

  • 如果有多个@Configuration,可以使用@Import注解,加入配置类

  • @Configuration
    @ComponentScan("com.haoyun.POJO")
    @Import(config2.class)
    public class HaoyunConfig {
        @Bean
        public User UserBean() {
            return new User();
        }
    
    }
    

JavaConfig是spring的一个子项目,是spring4之后的核心功能

代理模式

SpringAOP的底层,SpringAOP和SpringMVC必问

代理模式:

  • 静态代理
  • 动态代理

静态代理

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

租房例子

image-20200823073151826

客户找中介,中介找房东,实现租房的方法,还能添加一些附属操作

代码步骤:

  • 接口
  • 真实角色
  • 代理角色
  • 客户端访问角色

代理模式的好处:

  • 可以使真实角色的操作更加存粹,不用去关注一些公共的业务
  • 公共业务交给代理角色,实现业务分工
  • 公共业务发生扩展时,方便管理

缺点:

  • 一个真实角色就会产生一个代理角色,代码量增多

image-20200823074342349

如果一条线已经做出来了,突然要在中间加一个功能,不能去修改原有的业务代码,在公司中是大忌,只能在原有的基础上添加功能,通过代理,调用的是原有的功能,但是在调用原有的功能的同时能像上面的租房的例子一样,增加一些功能,要去学习下面向对象七大特性

动态代理

静态代理每代理一个被代理角色就要重新编写一个静态代理对象,代码量也十分庞大

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

动态代理分两大类:

  • 基于接口的动态代理
    • JDK原生的动态代理【这里使用】
  • 基于类的动态代理
    • 基于类的cglib
  • 但是现在用的比较多的是基于java字节码来实现的JavaAssist

需要了解两个类:Proxy,InvocationHandle

  • Proxy代理
  • InvocationHandle调用处理程序

image-20200823081152845

通过反射的方式,判断被代理对象的类型,处理代理实例

public class ProxyInvocationHandler implements InvocationHandler {
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),this);
    }
    

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        
        Object result  = method.invoke(target,args);
        return result;
        
    }
}

这个代理对象能添加功能,也能执行被代理对象的方法

AOP

image-20200823135537461

不改变原有的业务,增加功能

AOP在Spring中的作用

  • 横切关注点:跨越应用程序多个模块的方法或功能,于我们业务逻辑无关,但需要关注的部分
  • 切面(aspect):横切关注点被模块化的特殊对象,是一个类
  • 通知(Adivce):切面必须要完成的工作,类中的一个方法
  • 目标(Target):被通知的对象
  • 代理(Proxy):向目标对象应用通知后创建的对象
  • 切入点(PointCut):切面通知 执行的“地点”的定义
  • 连接点(JointPoint):于切入点匹配的执行点

image-20200823140201540

SpringAOP中,通过Advice定义横切逻辑,支持五种类型的的advice

image-20200823140425900

AOP不改变原有代码的情况下,去增加新的功能

使用Spring实现AOP

使用Spring的API接口

  • 导入包

    • <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
          <version>1.9.5</version>
      </dependency>
      
  • 添加applicationContext.xml配置支持

    • xmlns:aop="http://www.springframework.org/schema/aop"
      http://www.springframework.org/schema/aop
      https://www.springframework.org/schema/aop/spring-aop.xsd
      
  • 找到对应的接口和实现类,实现类就是被代理对象

    • public interface UserService {
          public void add();
          public void delete();
          public void update();
          public void select();
      }
      
    • 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 select() {
              System.out.println("select");
      
          }
      }
      
    • 现在制作一个动态代理的横切关注点,需要加在原有的业务前,原有业务执行前,执行这个切片

  • 制作切片

    • public class Log implements MethodBeforeAdvice {
          ///**
          // * @param method:要执行目标对象的方法
          // * @param args:参数
          // * @param target:目标对象
          // * @throws Throwable
          // */
          public void before(Method method, Object[] args, Object target) throws Throwable {
              System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行");
      
          }
      }
      
    • before中是切片执行的方法,通知方式是before,前置通知

  • 把这个横切关注点加入beans,和

    • <bean id="log" class="com.haoyun.log.Log"/>
      <bean id="UserServiceImpl" class="com.haoyun.service.UserServiceImpl"/>
      
  • applicationContext.xml配置AOP切入点,切片环绕

    • <aop:config>
          <!--切入点:expression:表达式(要执行的位置,UserServiceImpl.要切入的方法)-->
          <aop:pointcut id="pointcut" expression="execution(* com.haoyun.service.UserServiceImpl.*(..))"/>
          <!--执行环绕增加-->
          <!--把log切片,切入到pointcut切入点上-->
          <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
      </aop:config>
      
  • 测试

    • @Test
      public void ProxyTest() {
          ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
          /*代理的是一个接口*/
          UserService userserviceImpl = (UserService) context.getBean("UserServiceImpl",UserService.class);
          userserviceImpl.add();
      }
      
    • image-20200823152603918

    • 执行add前会先执行log切面

使用自定义类

  • public class aspectDiy {
        public void beforeDiy(){
            System.out.println("beforeDiy");
        }
        public void afterDiy(){
            System.out.println("afterDiy");
        }
    }
    
  • <aop:config>
        <aop:aspect ref="aspectDip">
            <!--切入点-->
            <aop:pointcut id="pointcut" expression="execution(* com.haoyun.service.UserServiceImpl.*(..))"/>
            <!--通知,切入到哪个点上-->
            <aop:before method="beforeDiy" pointcut-ref="pointcut"/>
            <aop:after method="afterDiy" pointcut-ref="pointcut"/>
        </aop:aspect>
    
    </aop:config>
    
  • 选择对应的方法切入就行

  • beforeDiy
    add
    afterDiy
    

使用注解实现

  • 先开启注解支持

  •     <aop:aspectj-autoproxy/>
        <context:component-scan base-package="com.haoyun.pointCut"/>
    
  • @Aspect
    @Component
    public class AnnotationPointCut {
        @Before("execution(* com.haoyun.service.UserServiceImpl.*(..))")
        public void before(){
            System.out.println(AnnotationPointCut.class.getName()+"   before");
        }
        @After("execution(* com.haoyun.service.UserServiceImpl.*(..))")
        public void after(){
            System.out.println(AnnotationPointCut.class.getName()+"   after");
        }
    
    }
    
  • 和之前一样测试就行

规范点的化使用第一种方法吧,切面小的话使用第二种方法,第三种方法也不是很简便,要设置切入点还挺麻烦,第一和第二种方法的切入点都可以复用,第三种方法切入点不好复用,差点意思

整合Mybatis

回顾Mybatis

步骤:

  • 导入相关包

    • junit
    • mybatis
    • myssql
    • spring
    • aop
    • mybatis-spring
  • 编写配置文件

  • 测试

  • 导包

  • <dependencies>
           <dependency>
               <groupId>junit</groupId>
               <artifactId>junit</artifactId>
               <version>4.12</version>
               <scope>test</scope>
           </dependency>
           <dependency>
               <groupId>mysql</groupId>
               <artifactId>mysql-connector-java</artifactId>
               <version>8.0.11</version>
           </dependency>
           <dependency>
               <groupId>org.springframework</groupId>
               <artifactId>spring-aop</artifactId>
               <version>5.2.7.RELEASE</version>
           </dependency>
           <dependency>
               <groupId>org.projectlombok</groupId>
               <artifactId>lombok</artifactId>
               <version>1.18.12</version>
           </dependency>
           <dependency>
               <groupId>org.springframework</groupId>
               <artifactId>spring-webmvc</artifactId>
               <version>5.2.7.RELEASE</version>
           </dependency>
           <dependency>
               <groupId>org.mybatis</groupId>
               <artifactId>mybatis</artifactId>
               <version>3.5.2</version>
           </dependency>
           <dependency>
               <groupId>org.springframework</groupId>
               <artifactId>spring-jdbc</artifactId>
               <version>5.2.7.RELEASE</version>
           </dependency>
           <dependency>
               <groupId>org.aspectj</groupId>
               <artifactId>aspectjweaver</artifactId>
               <version>1.9.5</version>
           </dependency>
           <dependency>
               <groupId>org.mybatis</groupId>
               <artifactId>mybatis-spring</artifactId>
               <version>2.0.5</version>
           </dependency>
     
       </dependencies>
       <build>
           <resources>
               <resource>
                   <directory>src/main/resources</directory>
                   <includes>
                       <include>**/*.properties</include>
                       <include>**/*.xml</include>
                   </includes>
                   <filtering>true</filtering>
               </resource>
               <resource>
                   <directory>src/main/java</directory>
                   <includes>
                       <include>**/*.properties</include>
                       <include>**/*.xml</include>
                   </includes>
                   <filtering>true</filtering>
               </resource>
           </resources>
       </build>
    
  • 标准几个东西,顺便导入spring整合要用的一些包

  • 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>
        <typeAliases>
            <package name="com.haoyun.POJO"/>
        </typeAliases>
        
        
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                    <property name="username" value="root"/>
                    <property name="password" value="123456"/>
                </dataSource>
            </environment>
        </environments>
        <mappers>
            <package name="com.haoyun.mapper"/>
        </mappers>
    </configuration>
    
  • User

  • @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        private int id;
        private String name;
        private String pwd;
    }
    
  • UserMapper

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

  • <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.haoyun.mapper.UserMapper">
        <select id="selectUser" resultType="user">
            select * from user
        </select>
    </mapper>
    
  • Test

  • @Test
    public void UserMapper(){
        String resource  ="mybatis-config.xml";
        InputStream inputStream = null  ;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = build.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = mapper.selectUser();
        for (User user : users) {
            System.out.println(user);
        }
        sqlSession.close();
    }
    

mybatis-spring官方文档

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

image-20200823204409516

这里面涉及到mybatis-spring包的版本问题,如果spring版本和mybatis是5.0和3.5版本以上的,要使用Mybatis-spring2.0以上的包

image-20200823204805451

最好就通过阅读mybatis-spring文档来学习

http://mybatis.org/spring/zh/getting-started.html

整合Mybatis方式一

spring的IoC特性,原先的Mybatis中的new对象方法都将制作为bean,进行依赖注入

  • 整体结构

  • image-20200823220010511

  • mybatis-spring的配置将一点点代替原有的mybatis-config.xml中的配置

  • spring-dao.xml可以专门存放dao层的设置

    • mybatis都有这几个操作
    • 使用SqlSessionFactoryBean读取连接数据库配置文件
    • 产生sqlSessionFactory
    • 再产生sqlSession
    • 然后getMapper执行操作
  • 都可以一一代替

    • 配置数据源dateSource

      • <!--DateSource:使用spring的数据源替换mybatis的设置:c3p0、dbcp、druid-->
        <bean id="dateSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
            <property name="username" value="root"/>
            <property name="password" value="123456"/>
        </bean>
        
      • mybatis中的配置数据源操作

    • 给sqlSessionFactory注入dateSource

      • <!--sqlSessionFactory-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <!--配置数据源-->
            <property name="dataSource" ref="dateSource" />
            <!--mybatis的核心配置文件,但是可以不写-->
            <property name="configLocation" value="classpath:mybatis-config.xml"/>
            <!--相当于mappe package 添加mybatis.xml配置文件-->
            <property name="mapperLocations" value="classpath:com/haoyun/mapper/UserMapper.xml"/>
            <!--mybatis配置文件中能配置的在这都能配置-->
        </bean>
        
    • 然后是注入sqlSession

      • <!--SqlSessionTemplate:就是SqlSession-->
        <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
            <!--只能使用构造器注入sqlSessionFactory,因为SqlSessionTemplate没有set方法-->
            <constructor-arg index="0" ref="sqlSessionFactory"/>
        </bean>
        
      • 这里的注入方法是官网给出的,因为进入SqlSessionTemplate的源码查看,是没有set方法的所以只能通过构造函数注入

    • mybatis-config中原有的操作,在spring中都能设置,不过还是建议留一点在mybatis-config.xml中,证明有mybatis-config.xml这个文件

    • 原有的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>
            <typeAliases>
                <package name="com.haoyun.POJO"/>
            </typeAliases>
        <!--    <settings>-->
        <!--        <setting name="" value=""/>-->
        <!--    </settings>-->
            <!--一般会留下几个设置,证明使用了mybatis-->
            
        <!--    <environments default="development">-->
        <!--        <environment id="development">-->
        <!--            <transactionManager type="JDBC"/>-->
        <!--            <dataSource type="POOLED">-->
        <!--                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>-->
        <!--                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>-->
        <!--                <property name="username" value="root"/>-->
        <!--                <property name="password" value="123456"/>-->
        <!--            </dataSource>-->
        <!--        </environment>-->
        <!--    </environments>-->
        <!--    <mappers>-->
        <!--        <package name="com.haoyun.mapper"/>-->
        <!--    </mappers>-->
        </configuration>
        
    • 配置好spring-dao.xml,要多写一个实现UserMapper接口的实现类,用于注入

    • 之前是通过getMapper,然后找到对应的mapper,才能产生对应的对象

    •     UserMapper mapper = sqlSession.getMapper(UserMapper.class);
      
    • 现在要通过注入bean来产生对象,这个对象需要注入一个sqlSession才行,sqlsession就是sqlSessionTemplate,再实现类添加一个set方法注入sqlSessionTemplate,sqlSessionTemplate已将在上面注入好,id为sqlSession

    • 添加这个实体类的作用就是能注入sqlSession

    • public class UserMapperImpl implements UserMapper {
          private SqlSessionTemplate sqlSession;
      
          public void setSqlSession(SqlSessionTemplate sqlSession) {
              this.sqlSession = sqlSession;
          }
      
          public List<User> selectUser() {
              UserMapper mapper = sqlSession.getMapper(UserMapper.class);
              return mapper.selectUser();
      
          }
      }
      
    • 代理调用的是mapper.selectUser,这里又映射到UserMapper.xml的selectUser

    • <mapper namespace="com.haoyun.mapper.UserMapper">
          <select id="selectUser" resultType="user">
              select * from user
          </select>
      </mapper>
      
    • 有了实体类就要给他注入bean

    • 在applicationContext.xml中注册,因为spring-dao中编写的配置基本已经定形,不必更改了

    • <?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">
          <import resource="spring-dao.xml"/>
      
          <bean id="userMapper" class="com.haoyun.mapper.UserMapperImpl">
              <property name="sqlSession" ref="sqlSession"/>
          </bean>
      </beans>a
      
    • import spring-dao.xml配置

    • 给userMapper,注入sqlSession,使用的reference引用已经写好的sqlSession bean

    • 测试

      • @Test
        public void UserMapper(){
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
            List<User> users = userMapper.selectUser();
            for (User user : users) {
                System.out.println(user);
            }
        }
        
      • 读取applicationContext配置文件后,直接getBean,就拿到了实体类对象,就可以调用里面的方法

    • 这个整合,也就是以springIoC的特性把、读取数据源、产生SqlSessionFactory,产生sqlSession的操作都进行了依赖注入化,通过第三方获取特定的对象,实现解耦

整合Mybatis方式二

image-20200823233604756

  • 官方文档提供了一个抽象支持类,能提供一个sqlsession,调用getSqlSession就能得到一个SqlSessionTemplate,需要注入一个SqlSessionFactory或SqlSessionTemplate

  • 注入SqlSessionFactory,就不用产生SqlSessionTemplate,这样在制作spring-dao.xml配置文件时能少注入一个SqlSessionTemplate

  •     <!--SqlSessionTemplate:就是SqlSession-->
    <!--    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">-->
    <!--        &lt;!&ndash;只能使用构造器注入sqlSessionFactory,因为SqlSessionTemplate没有set方法&ndash;&gt;-->
    <!--        <constructor-arg index="0" ref="sqlSessionFactory"/>-->
    <!--    </bean>-->
    
  • 开始注入

  • UserMapperImpl2

  • public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{
        /*省去了注入的过程*/
        public List<User> selectUser() {
            return getSqlSession().getMapper(UserMapper.class).selectUser();
        }
    }
    
  • spring-dao.xml

  • <bean id="UserMapperImpl2" class="com.haoyun.mapper.UserMapperImpl2">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
    
  • 注入sqlSessionFactory

  • 测试方法和之前一样

声明式事务

回顾事务transaction

  • 事务在项目开发中十分重要,涉及到数据的一致性
  • 事务ACID原则
    • 原子性
    • 一致性
    • 隔离性
      • 多个业务可能操作同一个资源,防止数据损坏
    • 持久性
      • 事务一旦提交,无论系统发生什么问题,结果都不会被影响

这里举了一个例子,进行insert操作和delete操作,insert操作成功,delete操作失败,根据ACID原则,这一组业务应该一起成功或者一起失败

在官方文档中介绍了两种事务的实现类型

http://mybatis.org/spring/zh/transactions.html#configuration

  • 交由容器管理事务
  • 编程式事务管理
    • 根据AOP的特性,最好以横切关注点的方式将切片切入,而不是修改已经写好的业务代码,所以编程式事务管理了解下就行,少用
交由容器管理事务
  • 创建一个 DataSourceTransactionManager 对象的,传入DateSource

    • <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg ref="dataSource" />
      </bean>
      
  • 添加spring的事务命名空间

    • <tx:jta-transaction-manager />
      
  • 找一个容器配置事务,平常不这么丢进去

    •   <property name="transactionFactory">
          <bean class="org.apache.ibatis.transaction.managed.ManagedTransactionFactory" />
        </property>  
      

但是举例是结合AOP进行的事务管理

  • 添加事务的命名空间

  • 可以手打,会自动添加依赖支持

  • <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
    
           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
            http://www.springframework.org/schema/tx/spring-tx.xsd">
    
  • <tx:jta-transaction-manager/>
    
  • 检查下支持是否进来了

  • 然后给入数据源创建DataSourceTransactionManager对象,这句是写死的

  • <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg ref="dateSource" />
    </bean>
    
  • <!--结合AOP实现事务织入-->
    <!--配置事务的传播特性,这个属性在晚上搜,事务的传播特性-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="add" propagation="REQUIRED"/><!--这个属性默认的-->
            <tx:method name="delete" propagation="REQUIRED"/>
            <tx:method name="update" propagation="REQUIRED"/>
            <tx:method name="query" read-only="true"/><!--只读-->
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    <aop:config>
        <aop:pointcut id="txPointCut" expression="execution(* com.haoyun.mapper.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
    </aop:config>
    
  • <select id="selectUser" resultType="user">
        select * from user
    </select>
    <insert id="insertUser" parameterType="user">
        insert into mybatis.user (id,name,pwd) values (#{id},#{name},#{pwd})
    </insert>
    <delete id="deleteUser" parameterType="int">
        deletes from user where id = #{id}
    </delete>
    
  • 这里的delete是故意写错的,测试会不会insert会不会回滚

  • public interface UserMapper {
        List<User> selectUser();
        int insertUser( User user);
        int deleteUser( int id);
    }
    
  • public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{
        /*省去了注入的过程*/
        public List<User> selectUser() {
    
            User user = new User();
            user.setId(14);
            user.setName("haoyun");
            user.setPwd("123");
            UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
            mapper.insertUser(user);
            mapper.deleteUser(5);
            return getSqlSession().getMapper(UserMapper.class).selectUser();
    
        }
    
        public int insertUser(User user) {
            return getSqlSession().getMapper(UserMapper.class).insertUser(user);
        }
    
        public int deleteUser(int id) {
            return getSqlSession().getMapper(UserMapper.class).deleteUser(id);
        }
    }
    
  • selectUser中放了insert和delete操作,delete操作失败后,会回滚,不会插入进去

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值