Spring学习笔记

Spring

1. 简介

整合现有的技术框架

开源免费框架

轻量级非入侵式

控制反转(IOC),面向切面编程(AOP)

支持事务的处理,对框架整合的支持

现代化的JAVA开发:基于Spring的开发

下载地址

官网

GitHub

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

1.1 Spring组成及拓展

由7个定义良好的模块组成,构建在核心容器智商,核心容器定义了创建、配置以及管理bean的方式

每个模块功能如下:

 

  • 核心容器:提供 Spring 框架的基本功能。主要组件是 BeanFactory,是工厂模式的实现。BeanFactory 使用控制反转(IOC) 将应用程序的配置和依赖性规范与实际的应用程序代码分开。

  • Spring 上下文:配置文件,向 Spring 框架提供上下文信息。

  • Spring AOP:通过配置管理特性,将面向切面的编程功能 , 集成到了 Spring 框架中。容易使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。不用依赖组件,将声明性事务管理集成到应用程序中。

  • Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化错误处理,极大降低需要编写的异常代码数量。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。

  • Spring ORM:Spring 框架插入了若干个 ORM 框架,提供 ORM 的对象关系工具。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。

  • Spring Web 模块:建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。Spring 框架支持与 Jakarta Struts 的集成。简化处理多部分请求以及将请求参数绑定到域对象的工作。

  • Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术。

1.2 Spring Boot

快速开发的脚手架

基于它可以快速开发单个微服务

约定大于配置

需要完全掌握Spring和SpringMVC

1.3 Spring Cloud

基于Spring Boot实现的

2. IOC

新建空白maven项目

不再管理对象的创建,更多关注业务的实现

IoC就是对象由Spring来创建、管理和装配

2.1 IOC理论推导

2.1.1 分析实现

在用到mysql或者oracle之类的东西的地方不去实现它,留出一个接口使用set

UserDao接口

public interface UserDao{
    public void getUser(){
        System.out.println("MySQL获取用户数据");
    }
}

Dao实现类

public class UserDaoImpl implements UserDao{
    @Override
    public coid getUser(){
        System.out.println("获取用户数据");
    }
}

UserService接口

public interface UserService{
    public void getUser();
}

Service实现类

UserServiceLmpl 类实现了 UserService 接口,并且提供了一个 setter 方法来设置 userDao 对象,通过调用 getUser() 方法获取用户信息。这样的设计可以通过依赖注入的方式,将具体的数据库访问实现类(即 userDao)动态地注入到 UserServiceLmpl 类中,使得程序的耦合度降低,更具有灵活性和可扩展性

public class UserServiceLmpl implements UserService{
    private UserDao userDao;
    //利用set实现
    public void setUserDao(UserDao userDao){
        this.userDao = userDao;
    }
    @Override
    public void getUser(){
        userDao.getUser();
    }
}

增加UserDao的实现类

public class UserDaoOracleImpl implements UserDao{
    @Override
    public void getUser(){
        Systemm.out.println("Oracle获取用户数据");
    }
}

测试类

@Test
public void test(){
   UserServiceImpl service = new UserServiceImpl();
   service.setUserDao( new UserDaoMySqlImpl() );
   service.getUser();
   //那我们现在又想用Oracle去实现呢
   service.setUserDao( new UserDaoOracleImpl() );
   service.getUser();
}

IOC本质

  • 控制反转IOC是一种设计思想,DI(依赖注入)是实现IOC的一种方法

  • 是一种通过描述(xml或注解)并通过第三方去生产或获取特定对象的方式。

  • 在Spring中实现控制反转的是IoC容器,实现方法是依赖注入DI

 

 

3. HelloSpring

3.1 编写代码

导入jar包

spring需要导入comments-logging进行日志记录,使用maven自动下载对应依赖项

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.1.10.RELEASE</version>
</dependency>
<!--lombok-->
<dependency>
     <groupId>org.projectlombok</groupId>
     <artifactId>lombok</artifactId>
     <version>1.18.10</version>
</dependency>

Hello实体类

package com.kuang.pojo;

import lombok.*;

@Data
public class Hello {

    private String str;
}

spring文件:beans.xml

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

    <!--使用Spring来创建对象,在Spring这些都称为Bean

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

    id = 变量名
    class = new 的对象;
    property 相当于给对象中的属性设置一个值!
    -->
    <bean id="hello" class="com.kuang.pojo.Hello">
        <property name="str" value="Spring"/>
    </bean>
</beans>

测试

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

public class MyTest {
    public static void main(String[] args) {
        //获取Spring的上下文对象!
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //对象都在Spring中管理,要使用直接去取
        Hello hello = (Hello) context.getBean("hello");
        System.out.println(hello.toString());
    }
}
  • Hello对象是由Spring创建的

  • Hello对象的属性是由Spring容器设置的,这个过程叫做控制反转

  • 控制:对象是Spring来创建

  • 反转:程序本身不创建对象,变成被动接收对象

3.2 修改

新增Spring配置文件beans.xml

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

   <bean id="MysqlImpl" class="com.kuang.dao.impl.UserDaoMySqlImpl"/>
   <bean id="OracleImpl" class="com.kuang.dao.impl.UserDaoOracleImpl"/>

   <bean id="ServiceImpl" class="com.kuang.service.impl.UserServiceImpl">
       <!--注意: 这里的name并不是属性 , 而是set方法后面的那部分 , 首字母小写-->
       <!--引用另外一个bean , 不是用value 而是用 ref-->
       <property name="userDao" ref="OracleImpl"/>
   </bean>
</beans>

测试

public void test2(){
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    UserServiceImpl serviceImpl = (UserServiceImpl) context.getBean("ServiceImpl");
   serviceImpl.getUser();
}

3.3 IoC创建对象方式

3.3.1 通过无参构造方法来创建

User.java

public class User{
    private String name;
    public User(){
        System.out.println("user无参构造方法")
    }
    public void setName(String name){
        this.name = name;
    }
    public void show(){
        System.out.println("name=" + name);
    }
}

beans.xml

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

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

测试类

@Test
public void test(){
   ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
   //在执行getBean的时候, user已经创建好了 , 通过无参构造
   User user = (User) context.getBean("user");
   //调用对象的方法 .
   user.show();
}

  

3.3.2 通过有参构造方法来创建

在配置文件加载时,管理的对象已经初始化

UserT.java

public class UserT{
    private String name;
    public UserT(String name){
        this.name = name;
    }
    public void setName(String name){
        this.name = name;
    }
    public void show(){
        System.out.println("name="+name);
    }
}

beans.xml

<!--第一种:index参数下标-->
<bean id="UserT" class="com.kuang.pojo.UserT">
    <!--index构造方法下标从0开始-->
    <constructor-arg index="0" value="hahahaha"/>
</bean>
<!--第二种:参数名字-->
<bean id="UserT" class="com.kuang.pojo.UserT">
    <!--name:参数名-->
    <constructor-arg name="name" value="hahahaha"/>
</bean>
<!--第三种:参数类型-->
<bean id="UserT" class="com.kuang.pojo.UserT">
    <constructor-arg type="java.lang.String" value="hahahaha"/>
</bean>

测试

@Test
public void testT(){
    ApplicationContext context = new ClassPathApplicationContext("beans.xml");
    UserT user = (UserT)context.getBean("userT");
    user.show();
}

3.4 Spring配置

3.4.1 别名

alias设置别名,为bean设置别名,可以设置多个

<!--设置别名:在获取Bean的时候可以使用别名获取-->
<alias name="userT" alias="userNew"/>
3.4.2 Bean的配置
  • bean就是java对象,由spring创建和管理

  • id是bean的唯一标识符。

  • 没有配置id那么name就是默认标识符;如果配置id又配置name,那么name是别名。

  • name可以配置多个别名,用逗号、分号、空格隔开

  • 如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象

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

import实现团队合作

<import resource="{path}/beans.xml"/>

4. DI依赖注入

 

 

4.1 概念以及构造器注入

  • 依赖注入

  • 依赖DI:指Bean对象的创建依赖于容器,bean对象的依赖资源

  • 注入:指Bean对象所依赖的资源,由容器来设置和装配

4.2 Set注入

要求被注入的属性必须有set方法,set方法的方法名=set + 属性首字母大写

属性是boolean类型:没有set方法,是is

测试pojo类

Address.java封装

public class Address{
    private String address;
    //调用getAddress()方法来获取address成员变量的值
    public String getAddress(){
        return address;
    }
    //使用setAddress(String address)方法来设置address成员变量的值
    public void setAddress(String address){
        this.address = address;
    }
}

Student.java

package com.kuang.pojo;

import java.util.*;
import lombok.*;

@Data
public class Student {

    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbys;
    private Map<String,String> card;
    private Set<String> games;
    private String wife;
    private Properties info;
}
4.2.1 常量注入

Spring容器在实例化该bean时,会自动调用Student类的setName()方法,将"value"的值传递给该方法,从而设置Student对象的name属性为"leo"

<bean id-"student" class="com.kuang.pojo.Student">
    <property name="name" value="leo"/>
</bean>

测试

@Test
public void test01(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applictaionContext.xml");
    Student student = (Student)context.getBean("student");
    System.out.println(student.getName());
}
4.2.2 Bean注入

这里的值是一个引用ref

表示要给Student对象的address属性注入addr这个bean的实例

 <bean id="addr" class="com.kuang.pojo.Address">
     <property name="address" value="地点"/>
 </bean>
 <!--除了通过setProperty方式设置name属性的值为"Leo"外,还使用了property元素的ref属性-->
 <bean id="student" class="com.kuang.pojo.Student">
     <property name="name" value="Leo"/>
     <property name="address" ref="addr"/>
 </bean>
4.2.3 数组注入

使用了property元素并命名为"books",内部包含了一个array元素,并在其中使用了多个value元素。这些value元素包含要注入到Student对象的books属性中的字符串值

<bean id="student" class="com.kuang.pojo.Student">
    <property name="name" value="小明"/>
    <property name="address" ref="addr"/>
    <property name="books">
        <array>
            <value>西游记</value>
             <value>红楼梦</value>
             <value>水浒传</value>
        </array>
    </property>
</bean>

Spring容器在实例化Student对象后,会自动将数组的值作为参数,调用Student类的setBooks()方法,实现对books属性的注入操作。这样,Student对象就可以使用这个数组作为其books属性的值,方便在应用程序中使用和处理这些书籍。与之前的方式相比,数组注入方式更适合注入一组相关值的场景,例如书籍列表、物品列表等。

4.2.4 List注入
  • 定义了一个名为hobbys的属性,并使用property元素将其设置为一个列表。

  • 在列表内部,使用了list元素,并在其中使用了多个value元素。每个value元素包含要注入到hobbys属性中的字符串值,分别为"听歌"、"看电影"和"爬山"。

  • Spring容器会自动将这个列表作为参数,调用对应的setter方法(例如setHobbys())来完成对hobbys属性的注入操作。这样,对象就可以使用这个列表作为其hobbys属性的值,方便使用和处理不同的爱好信息。

 <property name="hobbys">
     <list>
         <value>听歌</value>
         <value>看电影</value>
         <value>爬山</value>
     </list>
 </property>
4.2.5 Map注入
  • 使用了map元素,并在其中使用了多个entry元素。每个entry元素包含一个key和一个value,用于表示映射的键值对关系。在这个例子中,"中国邮政"是键,"456456456465456"是对应的值;"建设"是另一个键,"1456682255511"是对应的值。

  • 实例化对象后,Spring容器会将这个映射作为参数,调用对应的setter方法(例如setCard())来完成对card属性的注入操作。

  • 映射注入方式适用于注入具有键值对关系的数据

 <property name="card">
     <map>
         <entry key="中国邮政" value="456456456465456"/>
         <entry key="建设" value="1456682255511"/>
     </map>
 </property>
4.2.6 set注入

在集合内部,使用了set元素,并在其中使用了多个value元素。每个value元素包含一个元素值,表示要注入到games属性中的值。

集合注入方式适用于注入存储一组无序数据的场景

 <property name="games">
     <set>
         <value>LOL</value>
         <value>BOB</value>
         <value>COC</value>
     </set>
 </property>
4.2.7 null注入
  • 使用null元素表示要将null值注入到wife属性中。这种方式适用于当某个属性没有具体的值时,可以将其设置为null。

<property name="wife"><null/></property>
4.2.8 properties注入
  • 在属性集内部,使用了props元素,并在其中使用了多个prop元素。每个prop元素包含一个key和一个value,用于表示属性集的键值对关系。

  • Spring容器会将这个属性集作为参数,调用对应的setter方法(例如setInfo())来完成对info属性的注入操作。这样,对象就可以使用这个属性集作为其info属性的值,方便进行键值对的访问和处理。

 <property name="info">
     <props>
         <prop key="学号">20190604</prop>
         <prop key="性别">男</prop>
         <prop key="姓名">小明</prop>
     </props>
 </property>

 

 

4.3 C命名和P命名空间注入

User.java:没有有参构造器

import lombok.*;

@Data
public class User{
    private String name;
    private int age;
}
  • P命名空间注入:在头文件中加入约束文件

     <!--导入约束 : xmlns:p="http://www.springframework.org/schema/p"-->
     
     <!--P(属性: properties)命名空间 , 属性依然要设置set方法-->
     <bean id="user" class="com.kuang.pojo.User" p:name="Leo" p:age="18"/>

  • C命名空间注入(构造器注入):在头文件中加入约束文件

     <!--导入约束 : xmlns:c="http://www.springframework.org/schema/c"-->
     <!--C(构造: Constructor)命名空间 , 属性依然要设置set方法-->
     <bean id="user" class="com.kuang.pojo.User" c:name="Lep" c:age="18"/>

    测试

    @Test
     public void test02(){
         ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
         User user = (User) context.getBean("user");
         System.out.println(user);
     }

4.4 Bean的作用域

4.4.1Bean简介

组成应用程序的主体及由Spring IoC容器所管理的对象,bean就是由IoC容器初始化、装配及管理的对象

img

4.4.2 Singleton
  • 作用域为Singleton时,Spring IoC容器中只会存在一个共享的bean实例

  • 所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。

  • Singleton是单例类型,在创建起容器时同时自动创建一个bean对象,每次获取到的对象都是同一个对象。

  • Singleton作用域是Spring中的缺省作用域。

  • 在XML中将bean定义成singleton配置方式

    <bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" scope="singleton"></bean>

测试

public void test03(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    User user = (User)context.getBean("user");
    User user2 = (User)context.getBean("user");
    System.out.println(user == user2);
}
4.4.3 Prototype
  • 作用域为Prototype,表示一个bean定义对应多个对象实例。

  • Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。

  • Prototype是原型类型,在创建容器时没有实例化,当获取bean的时创建一个对象,每次获取到的对象不是同一个对象。

  • 对有状态的bean使用prototype作用域,对无状态的bean则应该使用singleton作用域。

  • 配置bean定义成prototype:

     <bean id="account" class="com.foo.DefaultAccount" scope="prototype"/>  
      或者
     <bean id="account" class="com.foo.DefaultAccount" singleton="false"/>
4.4.4 Request
  • 作用域为Request,表示在一次HTTP请求中,一个bean定义对应一个实例

  • 每个HTTP请求都会有各自的bean实例,依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。

  • 考虑下面bean定义:

     <bean id="loginAction" class=cn.csdn.LoginAction" scope="request"/>
  • 每次HTTP请求,Spring容器根据loginAction bean的定义创建全新LoginAction bean实例,该loginAction bean实例仅在当前HTTP request内有效,可以根据需要更改所建实例的内部状态

  • 其他请求中根据loginAction bean定义创建的实例,不会看到这些特定于某个请求的状态变化。当处理请求结束,request作用域的bean实例将被销毁。

4.4.5 Session
  • 作用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例。

  • 该作用域仅在基于web的Spring ApplicationContext情形下有效。

  • 考虑下面bean定义:

 <bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

5. 自动装配Bean

5.1 自动装配说明

  • 自动装配是使用spring满足bean依赖的一种方法

  • spring会在应用上下文中为某个bean寻找其依赖的bean

  • 三种装配机制:

    • 在xml中显式配置

    • 在java中显式配置

    • 隐式的bean发现机制和自动装配

  • 两个实现操作

    • 组件扫描:自动发现应用上下文中所创建的bean

    • 自动装配:spring自动满足bean之间的依赖(IoC/DI)

5.2 测试环境搭建

 

  • 新建一个项目

  • 创建两个实体类cat和dog

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

  • 新建用户类User

    import lombok.*;
    @Data
    public class User{
        private Cat cat;
        private Dog dog;
        private String str;
    }
  • Spring配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">
    
       <bean id="dog" class="com.kuang.pojo.Dog"/>
       <bean id="cat" class="com.kuang.pojo.Cat"/>
    
       <bean id="user" class="com.kuang.pojo.User">
           <property name="cat" ref="cat"/>
           <property name="dog" ref="dog"/>
           <property name="str" value="qinjiang"/>
       </bean>
    </beans>
  • 测试

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

5.3 byName

autowire byName按名称自动装配:自动装配避免类似字母缺漏等错误,简化配置

  • 查找其类中所有set方法名,获得将set去掉并首字母小写的字符串

  • 在spring容器中寻找是否有此字符串名称的id对象,有就取出注入,没有就报空指针异常

  • 修改bean配置,增加属性

    <bean id="user" class="com.kuang.pojo.User" autowire="byName">
        <property name="str" value="Leo"/>
    </bean>

5.4 byType

需要保证同一类型的对象在spring容器中唯一,如果不唯一就会报异常NoUniqueBeanDefinitionException

  • 将user的bean修改为autowire="byType"

  • 测试

  • 注册dog2的bean对象

  • 测试后报错

  • 删除dog2,修改dog的bean名称测试

    <bean id="dog" class="com.kuang.pojo.Dog"/>
    <bean id="cat" class="com.kuang.pojo.Cat"/>
    <bean id="dog2" class="com.kuang.pojo.Dog2"/>
    <bean id="user" class="com.kuang.pojo.User" autowire="byType">
       <property name="str" value="Leo"/>
    </bean>

5.5 使用注解

利用注解的方式注入属性

在spring配置文件中引入context文件头

xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd

开启属性注解支持

<context:annotation-config/>

5.6 小结

5.6.1 @Autowired
  • @Autowired是按类型自动转配的,不支持id匹配。

  • 需要导入 spring-aop的包

测试:

去除User类中的set方法,使用@Autowired注解

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

   public Cat getCat() {
       return cat;
  }
   public Dog getDog() {
       return dog;
  }
   public String getStr() {
       return str;
  }
}

配置文件内容

<context:annotation-config/>

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

测试

@Autowired(required=false) 说明:false,对象可以为null;true,对象必须存对象,不能为null。

//如果允许对象为null,设置required = false,默认为true
@Autowired(required = false)
private Cat cat;
5.6.2 @Qualifier
  • @Autowired根据类型自动装配,加@Qualifier则可以根据byName的方式自动装配

  • @Qualifier不能单独使用。

测试:

  • 配置文件修改内容,保证类型存在对象。名字不为类的默认名

<bean id="dog1" class="com.kuang.pojo.Dog"/>
<bean id="dog2" class="com.kuang.pojo.Dog"/>
<bean id="cat1" class="com.kuang.pojo.Cat"/>
<bean id="cat2" class="com.kuang.pojo.Cat"/>
  • 没有加Qualifier测试报错

  • 在属性上加Qualifier注解

@Autowired
@Qualifier(value = "cat2")
private Cat cat;
@Autowired
@Qualifier(value = "dog2")
private Dog dog;
5.6.3 @Resource
  • @Resource如有指定的name属性,先按该属性进行byName方式查找装配;

  • 再进行默认byName方式进行装配;

  • 都不成功,按byType的方式自动装配。

  • 都不成功,报异常。

实体类:

public class User {
   //如果允许对象为null,设置required = false,默认为true
   @Resource(name = "cat2")
   private Cat cat;
   @Resource
   private Dog dog;
   private String str;
}

beans.xml

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

<bean id="user" class="com.kuang.pojo.User"/>

测试

配置文件2:beans.xml , 删cat2

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

实体类只保留注解

@Resource
private Cat cat;
@Resource
private Dog dog;

先进行byName查找,失败;再进行byType查找,成功。

5.6.4 @Autowired与@Resource异同:
  • @Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。

  • @Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false

  • @Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

  • 它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。

6. 注解

   

在配置文件中引入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">
</beans>

6.2 Bean的实现

  • 指定注解扫描包

    <context:component-scan base-package="com.kuang.pojo"/>
  • 指定包下编写类增加注解

    @Component("user")
    //相当于配置文件中<bean id="user" class="当前注解的类"/>
    public class User{
        public String name = "Leo";
    }
  • 测试

    @Test
    public void test(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationCContext("beans.xml");
        User user = (User)applicationContext.getBean("user");
        System.out.println(user.name);
    }

6.3 属性注入

  • 不使用提供的set方法:直接在名上添加@value("值")

    @Component("user")
    // 相当于配置文件中 <bean id="user" class="当前注解的类"/>
    public class User {
       @Value("Leo")
       // 相当于配置文件中 <property name="name" value="Leo"/>
       public String name;
    }
  • 使用set方法:在set方法上添加@alue("值")

    @Component("user")
    public class User {
       public String name;
       @Value("Leo")
       public void setName(String name) {
           this.name = name;
      }
    }

6.4 衍生注解

加上注解相当于给这个类交给spring管理装配

三个衍生注解:

  • @Controller:web层

  • @Service:service层

  • @Repository:dao层

6.5 作用域@scope

  • singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。

  • prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收

@Controller("user")
@Scope("prototype")
public class User {
   @Value("Leo")
   public String name;
}

6.6 小结

XML与注解比较

  • XML可以适用任何场景 ,结构清晰,维护方便

  • 注解不是自己提供的类使用不了,开发简单方便

xml与注解整合开发

  • xml管理Bean

  • 注解完成属性注入

  • 使用过程中, 可以不用扫描,扫描是为了类上的注解

<context:annotation-config/>  

作用:

  • 进行注解驱动注册,从而使注解生效

  • 用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显示的向Spring注册

  • 如果不扫描包,就需要手动配置bean

6.7 基于java类进行配置

JavaConfig 原来是 Spring 的一个子项目,它通过 Java 类的方式提供 Bean 的定义信息

测试:

  • 编写实体类Dog

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

    @Configuration  //代表这是一个配置类
    public class MyConfig {
       @Bean 
        //通过方法注册一个bean,返回值是Bean的类型,方法名是bean的id
       public Dog dog(){
           return new Dog();
      }
    }
  • 测试

    @Test
    public void test2(){
       ApplicationContext applicationContext =
               new AnnotationConfigApplicationContext(MyConfig.class);
       Dog dog = (Dog) applicationContext.getBean("dog");
       System.out.println(dog.name);
    }

6.8 导入其他配置

编写配置类

@Configuration //代表是一个配置类
Public class MyConfig2{
}

在之前的配置类中选择导入这个配置类

@Configuration
@Import(MyConfig2.class)  //导入合并其他配置类,类似于配置文件中的 inculde 标签
public class MyConfig {
   @Bean
   public Dog dog(){
       return new Dog();
  }
}

7.代理

7.1 静态代理

7.1.1 角色分析
  • 抽象:使用接口或者抽象类实现

  • 真实角色:被代理的

  • 代理:代理真实角色

  • 客户:使用代理进行操作

7.1.2 代码实现

 

抽象:Rent.java

//抽象角色:租房
public interface Rent {
   public void rent();
}

真实角色:Host.java

//真实角色: 房东,房东要出租房子
public class Host implements Rent{
   public void rent() {
       System.out.println("房屋出租");
  }
}

代理:Proxy.java

//代理角色:中介
public class Proxy implements Rent {

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

   //租房
   public void rent(){
       seeHouse();
       host.rent();
       fare();
  }
   //看房
   public void seeHouse(){
       System.out.println("带房客看房");
  }
   //收中介费
   public void fare(){
       System.out.println("收中介费");
  }
}

客户:Client.java

//客户类,一般客户都会去找代理!
public class Client {
   public static void main(String[] args) {
       //房东要租房
       Host host = new Host();
       //中介帮助房东
       Proxy proxy = new Proxy(host);

       //你去找中介!
       proxy.rent();
  }
}

静态代理优势:

  • 公共业务由代理完成,实现业务分工

  • 公共业务发生拓展时变得更加集中

劣势:

  • 随着类的增加添加代理类,开发效率降低

7.2 动态代理

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

  • 动态代理的代理类是动态生成的 ,静态代理的代理类是提前写好的

  • 动态代理 : 基于接口 , 基于类

    • 基于接口的动态代理----JDK动态代理

      • InvocationHandler:调用处理程序

        参数: proxy - 调用该方法的代理实例 method -所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。 args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。

        Object invoke(Object proxy, 方法 method, Object[] args);
      • Proxy : 代理

        //生成代理类
        public Object getProxy(){
           return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                                         rent.getClass().getInterfaces(),this);
        }
    • 基于类的动态代理--cglib

代码实现:

//抽象:租房
public interface Rent {
   public void rent();
}
//真实角色: 房东,房东要出租房子
public class Host implements Rent{
   public void rent() {
       System.out.println("房屋出租");
  }
}
//代理:中介
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);
  }

   // proxy : 代理类 method : 代理类的调用处理程序的方法对象.
   // 处理代理实例上的方法调用并返回结果
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       seeHouse();
       //核心:本质利用反射实现!
       Object result = method.invoke(rent, args);
       fare();
       return result;
  }

   //看房
   public void seeHouse(){
       System.out.println("带房客看房");
  }
   //收中介费
   public void fare(){
       System.out.println("收中介费");
  }
}
//租客
public class Client {
   public static void main(String[] args) {
       //真实角色
       Host host = new Host();
       //代理实例的调用处理程序
       ProxyInvocationHandler pih = new ProxyInvocationHandler();
       pih.setRent(host); //将真实角色放置进去!
       Rent proxy = (Rent)pih.getProxy(); //动态生成对应的代理类!
       proxy.rent();
  }
}

优势:

  • 一个动态代理可以代理多个类,代理的是接口

8. AOP实现方式

8.1 简介

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

AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容

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

8.2 在Spring中的作用

提供声明式事务

允许用户自定义切面

  • 横切关注点:跨越应用程序多个模块的方法或功能。与业务逻辑无关但是需要关注的部分。

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

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

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

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

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

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

img

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

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

img

8.3 使用Spring实现Aop

 

使用AOP需要导入依赖包

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

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

业务接口和实现类

public interface UserService {
   public void add();
   public void delete();
   public void update();
   public void search();
}
public class UserServiceImpl implements UserService{
   @Override
   public void add() {
       System.out.println("增加用户");
  }
   @Override
   public void delete() {
       System.out.println("删除用户");
  }
   @Override
   public void update() {
       System.out.println("更新用户");
  }
   @Override
   public void search() {
       System.out.println("查询用户");
  }
}

增强类

  • 前置增强

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

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

spring文件中注册,实现aop切入实现

  • 导入约束

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:aop="http://www.springframework.org/schema/aop"
          xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop.xsd">
    ​
       <!--注册bean-->
       <bean id="userService" class="com.kuang.service.UserServiceImpl"/>
       <bean id="log" class="com.kuang.log.Log"/>
       <bean id="afterLog" class="com.kuang.log.AfterLog"/>
    ​
       <!--aop的配置-->
       <aop:config>
           <!--切入点 expression:表达式匹配要执行的方法-->
           <aop:pointcut id="pointcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
           <!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
           <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
           <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
       </aop:config>
    </beans>
  • 测试

    public class MyTest {
       @Test
       public void test(){
           ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
           UserService userService = (UserService) context.getBean("userService");
           userService.search();
      }
    }
8.3.2 自定义类

切入类

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

spring配置

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

测试

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

注解实现的增强类

package com.kuang.config;
​
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
​
@Aspect
public class AnnotationPointcut {
   @Before("execution(* com.kuang.service.UserServiceImpl.*(..))")
   public void before(){
       System.out.println("---------方法执行前---------");
  }
​
   @After("execution(* com.kuang.service.UserServiceImpl.*(..))")
   public void after(){
       System.out.println("---------方法执行后---------");
  }
​
   @Around("execution(* com.kuang.service.UserServiceImpl.*(..))")
   public void around(ProceedingJoinPoint jp) throws Throwable {
       System.out.println("环绕前");
       System.out.println("签名:"+jp.getSignature());
       //执行目标方法proceed
       Object proceed = jp.proceed();
       System.out.println("环绕后");
       System.out.println(proceed);
  }
}

spring注册Bean,增加支持注解的配置

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

aop:aspectj-autoproxy:说明:

  • 通过aop命名空间的<aop:aspectj-autoproxy />声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。
    ​
    <aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,当配为<aop:aspectj-autoproxy  poxy-target-class="true"/>时,表示使用CGLib动态代理技术织入增强。

 

9. Mybatis

 

9.1 步骤

导入jar包

  • junit

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

    <dependency>
       <groupId>org.mybatis</groupId>
       <artifactId>mybatis</artifactId>
       <version>3.5.2</version>
    </dependency>
  • mysql-connector-java

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

    <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-webmvc</artifactId>
       <version>5.1.10.RELEASE</version>
    </dependency>
    <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-jdbc</artifactId>
       <version>5.1.10.RELEASE</version>
    </dependency>
  • aspectJ AOP织入器

    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
    <dependency>
       <groupId>org.aspectj</groupId>
       <artifactId>aspectjweaver</artifactId>
       <version>1.9.4</version>
    </dependency>
  • mybatis-spring整合包

    <dependency>
       <groupId>org.mybatis</groupId>
       <artifactId>mybatis-spring</artifactId>
       <version>2.0.2</version>
    </dependency>
  • 配置maven静态资源过滤

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

编写配置文件&代码实现

9.2 Mybatis

pojo实体类

package com.kuang.pojo;
​
public class User {
   private int id;  //id
   private String name;   //姓名
   private String pwd;   //密码
}

mybatis配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
       PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
       "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
​
   <typeAliases>
       <package name="com.kuang.pojo"/>
   </typeAliases>
​
   <environments default="development">
       <environment id="development">
           <transactionManager type="JDBC"/>
           <dataSource type="POOLED">
               <property name="driver" value="com.mysql.jdbc.Driver"/>
               <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
               <property name="username" value="root"/>
               <property name="password" value="123456"/>
           </dataSource>
       </environment>
   </environments>
​
   <mappers>
       <package name="com.kuang.dao"/>
   </mappers>
</configuration>

UserDao.java

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

Mapper映射文件

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

测试类

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

9.3 Mybatis-Spring

mybatis-spring包中的重要类

将mybatis代码无缝整合到Spring中

Mybatis-Spring需要以下版本:

Mybatis-SpringMybatisSpring框架Spring BatchJava
2.03.5+5.0+4.0+java 8+
1.33.4+3.2.2+2.1+java 6+

使用maven作为构建工具:pom.xml

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

使用Spring和Mybatis,在Spring应用上下文定义SqlSessionFactory和一个数据映射器类。

使用SqlSessionFactoryBean创建SqlSessionFactory。

配置工厂Bean:

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

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
</bean>
  • 基础 MyBatis 通过 SqlSessionFactoryBuilder 创建 SqlSessionFactory 。在 MyBatis-Spring 中,使用 SqlSessionFactoryBean 创建。

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

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

  • 一个常用的属性是 configLocation指定 MyBatis 的 XML 配置文件路径。基础配置指的是 < settings> 或 < typeAliases>元素。

  • 任何环境配置(<environments>),数据源(<DataSource>)和 MyBatis 的事务管理器(<transactionManager>)都会被忽略。SqlSessionFactoryBean 会创建它自有的 MyBatis 环境配置(Environment),并按要求设置自定义环境的值。

  • SqlSessionTemplate 是 MyBatis-Spring 的核心。可以使用它无缝代替你代码中已经在使用的 SqlSession。

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

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

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

bean 已经注入 DAO bean 中,添加SqlSession 属性

public class UserDaoImpl implements UserDao {
​
 private SqlSession sqlSession;
​
 public void setSqlSession(SqlSession sqlSession) {
   this.sqlSession = sqlSession;
}
​
 public User getUser(String userId) {
   return sqlSession.getMapper...;
}
}

注入 SqlSessionTemplate

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

9.4 整合方式1

spring配置文件beans.xml

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

配置数据源替换mybatis数据源

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

配置SqlSessionFactory,关联MyBatis

<!--配置SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
   <property name="dataSource" ref="dataSource"/>
   <!--关联Mybatis-->
   <property name="configLocation" value="classpath:mybatis-config.xml"/>
   <property name="mapperLocations" value="classpath:com/kuang/dao/*.xml"/>
</bean>

注册sqlSessionTemplate,关联sqlSessionFactory;

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

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

public class UserDaoImpl implements UserMapper {
​
   //sqlSession不用我们自己创建了,Spring来管理
   private SqlSessionTemplate sqlSession;
​
   public void setSqlSession(SqlSessionTemplate sqlSession) {
       this.sqlSession = sqlSession;
  }
​
   public List<User> selectUser() {
       UserMapper mapper = sqlSession.getMapper(UserMapper.class);
       return mapper.selectUser();
  }
   
}

注册bean实现

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

测试

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

输出结果后查看Mybatis配置文件状态发现都可以被Spring整合

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
       PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
       "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
   <typeAliases>
       <package name="com.kuang.pojo"/>
   </typeAliases>
</configuration>

9.5 整合方式2

  • dao继承Support类 ,:直接利用 getSqlSession() 获得

  • 直接注入SqlSessionFactory

  • 不需要管理SqlSessionTemplate , 对事务的支持更加友好 . 可跟踪源码查看

测试:

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

修改bean设置

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

测试:

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

10. 事务

10.1 事务

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

事务的四个属性ACID

  • 原子性(atomicity):由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用

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

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

  • 持久性(durability):事务一旦完成,无论系统发生什么错误,结果都不会受到影响。

10.2 测试

在UserDao新增两个方法:删除和增加用户

//添加一个用户
int addUser(User user);
​
//根据id删除用户
int deleteUser(int id);

编写接口的实现类

public class UserDaoImpl extends SqlSessionDaoSupport implements UserMapper {
​
   //增加一些操作
   public List<User> selectUser() {
       User user = new User(4,"小明","123456");
       UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
       mapper.addUser(user);
       mapper.deleteUser(4);
       return mapper.selectUser();
  }
​
   //新增
   public int addUser(User user) {
       UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
       return mapper.addUser(user);
  }
   //删除
   public int deleteUser(int id) {
       UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
       return mapper.deleteUser(id);
  }
}

测试

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

10.3 Spring声明式事务

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

为什么需要配置事务?

  • 如果不配置,就需要我们手动提交控制事务;

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

编程式事务管理

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

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

声明式事务管理

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

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

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

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

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

事务管理器

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

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

JDBC事务

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

配置事务的通知

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

spring事务传播特性:

多个事务方法相互调用时,事务如何在这些方法间传播。

  • propagation_requierd:当前没有事务,新建一个事务,如果已存在一个事务中,加入到这个事务中.Spring 默认的事务传播行为.

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

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

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

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

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

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

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

配置AOP

导入aop的头文件

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

进行测试

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

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

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值