【急速入门】Spring 极速入门

高清思惟导图已同步Git:https://github.com/SoWhat1412/xmindfile,关注公众号sowhat1412获取海量资源html

在这里插入图片描述

一、Spring概述及IOC理论推导

Spring简介

Spring : 给软件行业带来了春天java

2002年,Rod Jahnson首次推出了Spring框架雏形interface21框架。2004年3月24日,Spring框架以interface21框架为基础,通过从新设计,发布了1.0正式版。很难想象Rod Johnson的学历 , 他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学。mysql

Spring理念 : 使现有技术更加实用 . 自己就是一个大杂烩 , 整合现有的框架技术git

官网 : http://spring.io/程序员

官方下载地址 : https://repo.spring.io/libs-release-local/org/springframework/spring/github

GitHub : https://github.com/spring-projectsweb

优势

  • 一、Spring是一个开源免费的框架 , 容器 .spring

  • 二、Spring是一个轻量级的框架 , 非侵入式的 .sql

  • 三、控制反转 IoC , 面向切面 Aop数据库

  • 四、对事物的支持 , 对框架的支持

  • 五、…

一句话归纳:Spring是一个轻量级、非***式的控制反转(IoC)和面向切面(AOP)的容器(框架)

组成

在这里插入图片描述
Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了建立、配置和管理 bean 的方式 。
在这里插入图片描述
组成 Spring 框架的每一个模块(或组件)均可以单独存在,或者与其余一个或多个模块联合实现。每一个模块的功能以下:

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

  • Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。

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

  • Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不一样数据库供应商抛出的错误消息。异常层次结构简化了错误处理,而且极大地下降了须要编写的异常代码数量(例如打开和关闭链接)。Spring DAO 的面向 JDBC 的异常听从通用的 DAO 异常层次结构。

  • Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。全部这些都听从 Spring 的通用事务和 DAO 异常层次结构。

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

  • Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。经过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。

拓展

Spring Boot与Spring Cloud

  • Spring Boot 是 Spring 的一套快速配置脚手架,能够基于Spring Boot 快速开发单个微服务;
  • Spring Boot专一于快速、方便集成的单个微服务个体,Spring Cloud关注全局的服务治理框架;
  • Spring Boot使用了约束优于配置的理念,不少集成方案已经帮你选择好了,能不配置就不配置 , Spring Cloud很大的一部分是基于Spring Boot来实现,Spring Boot能够离开Spring Cloud独立使用开发项目,可是Spring Cloud离不开Spring Boot,属于依赖的关系。
  • Spring Cloud是基于Spring Boot实现的;
  • SpringBoot在SpringClound中起到了承上启下的做用,若是你要学习SpringCloud必需要学习SpringBoot。

IOC理论推导

IOC基础:新建一个空白的maven项目

分析实现

咱们先用咱们原来的方式写一段代码 .
一、先写一个UserDao接口

public interface UserDao {
   public void getUser();
}

二、再去写Dao的实现类

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

三、而后去写UserService的接口

public interface UserService {
   public void getUser();
}

四、最后写Service的实现类

public class UserServiceImpl implements UserService {
   private UserDao userDao = new UserDaoImpl();
   @Override
   public void getUser() {
       userDao.getUser();
  }
}

五、测试一下

@Test
public void test(){
   UserService service = new UserServiceImpl();
   service.getUser();
}

这是咱们原来的方式 , 开始你们也都是这么去写的对吧 . 那咱们如今修改一下 ,把Userdao的实现类增长一个 .

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

紧接着咱们要去使用MySql的话 , 咱们就须要去service实现类里面修改对应的实现

public class UserServiceImpl implements UserService {
   private UserDao userDao = new UserDaoMySqlImpl(); // 此处被修改了
   @Override
   public void getUser() {
       userDao.getUser();
  }
}

在假设, 咱们再增长一个Userdao的实现类 .

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

那么咱们要使用Oracle , 又须要去service实现类里面修改对应的实现 . 假设咱们的这种需求很是大 , 这种方式就根本不适用了, 甚至反人类对吧 , 每次变更 , 都须要修改大量代码 . 这种设计的耦合性过高了, 牵一发而动全身 .彻底违背了 设计模式中的开闭原则。

那咱们如何去解决呢 ? 咱们能够在须要用到他的地方 , 不去实现它 , 而是留出一个接口 , 利用set , 咱们去代码里修改下 .

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

如今去咱们的测试类里 , 进行测试 ;

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

你们发现了区别没有 ? 可能不少人说没啥区别 . 可是它们已经发生了根本性的变化 , 不少地方都不同了 . 仔细去思考一下 , 之前全部东西都是由程序在Service层固定死去进行控制建立 , 而如今是由咱们自行控制建立对象 , 把主动权交给了调用者 . 程序不用去管怎么建立,怎么实现了 . 它只负责提供一个接口 .

这种思想 , 从本质上解决了问题 , 咱们程序员再也不去管理对象的建立了 , 更多的去关注业务的实现 . 耦合性大大下降 . 这也就是IOC的原型 !

在这里插入图片描述

IOC本质

控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另外一种说法。没有IoC的程序中 , 咱们使用面向对象编程 , 对象的建立与对象间的依赖关系彻底硬编码在程序中,对象的建立由程序本身控制,控制反转后将对象的建立转移给第三方,我的认为所谓控制反转就是:得到依赖对象的方式反转了。

  • IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可使用XML配置,也可使用注解,新版本的Spring也能够零配置实现IoC。

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

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

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

  • 明白IOC的思想,是理解Spring的核心技巧

  • 经过反射跟XML解析手动实现IOC

二、快速上手Spring

HelloSpring

导入Jar包
注 : spring 须要导入commons-logging进行日志记录 . 咱们利用maven , 他会自动下载对应的依赖项 .

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

编写代码

一、编写一个Hello实体类

public class Hello {
   private String name;

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

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

二、编写咱们的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就是java对象 , 由Spring建立和管理-->
   <bean id="hello" class="com.sowhat.pojo.Hello">
       <property name="name" value="Spring"/>
   </bean>
</beans>

三、咱们能够去进行测试了 .

@Test
public void test(){
   //解析beans.xml文件 , 生成管理相应的Bean对象
   ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
   //getBean : 参数即为spring配置文件中bean的id .
   Hello hello = (Hello) context.getBean("hello");
   hello.show();
}

思考

  • Hello 对象是谁建立的 ? 由Spring建立的
  • Hello 对象的属性是怎么设置的 ? hello 对象的属性是由Spring容器设置的
  • 这个过程就叫控制反转 :
  • 控制 : 谁来控制对象的建立 , 传统应用程序的对象是由程序自己控制建立的 , 使用Spring后 , 对象是由Spring来建立的
  • 反转 : 程序自己不建立对象 , 而变成被动的接收对象 .
  • 依赖注入 : 就是利用set方法来进行注入的.
  • IOC是一种编程思想,由主动的编程变成被动的接收
    能够经过ClassPathXmlApplicationContext去浏览一下底层源码

在这里插入图片描述
在这里插入图片描述
一个XML文件的解析就能够上延8层,可见Spring容器为了实现IOC进行了全面性的考虑。

修改案例一

咱们在案例一中, 新增一个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.sowhat.dao.impl.UserDaoMySqlImpl"/>
    <bean id="OracleImpl" class="com.sowhat.dao.impl.UserDaoOracleImpl"/>

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

测试!

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

OK , 到了如今 , 咱们完全不用再程序中去改动了 , 要实现不一样的操做 , 只须要在xml配置文件中进行修改 , 所谓的IoC,一句话搞定 : 对象由Spring 来建立 , 管理 , 装配 !

IOC建立对象方式

经过无参构造方法来建立

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

    <!--bean就是java对象 , 由Spring建立和管理-->
    <bean id="hello" class="com.sowhat.pojo.Hello">
        <property name="name" value="Spring"/>
    </bean>

    <bean id="user" class = "com.sowhat.pojo.User">
        <property name="name" value="sowhat"/>
    </bean>
</beans>

三、测试类

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

结果能够发现,在调用show方法以前,User对象已经经过无参构造初始化了!
在这里插入图片描述

经过有参构造方法来建立

一、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="userT1" class="com.sowhat.pojo.UserT">
        <!-- index指构造方法 , 下标从0开始 -->
        <constructor-arg index="0" value="sowhat1"/>
    </bean>
    <!-- 第二种根据参数名字设置 -->
    <bean id="userT2" class="com.sowhat.pojo.UserT">
        <!-- name指参数名 -->
        <constructor-arg name="name" value="sowhat2"/>
    </bean>
    <!-- 第三种根据参数类型设置 -->
    <bean id="userT3" class="com.sowhat.pojo.UserT">
        <constructor-arg type="java.lang.String" value="sowhat3"/>
    </bean>

三、测试

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

结论:在配置文件加载的时候。其中管理的对象都已经初始化了!

Spring配置

别名:alias 设置别名 , 为bean设置别名 , 能够设置多个别名

<!--设置别名:在获取Bean的时候可使用别名获取-->
<alias name="userT" alias="userNew"/>

Bean的配置
bean就是java对象,由Spring建立和管理-

<!--
   id 是bean的标识符,要惟一,若是没有配置id,name就是默认标识符
   若是配置id,又配置了name,那么name是别名
   name能够设置多个别名,能够用逗号,分号,空格隔开
   若是不配置id和name,能够根据applicationContext.getBean(.class)获取对象;
   class是bean的全限定名=包名+类名
-->
<bean id="hello" name="hello2 h2,h3;h4" class="com.sowhat.pojo.Hello">
   <property name="name" value="Spring"/>
</bean>

import

团队的合做经过import来实现 .

<import resource="{path}/beans.xml"/>
三、依赖注入(DI)

Dependency Injection

概念

  • 依赖注入(Dependency Injection,DI)。

  • 依赖 : 指Bean对象的建立依赖于容器 . Bean对象的依赖资源 .

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

构造器注入

第二章已经讲解过了

Set 注入 (重点)

要求被注入的属性 , 必须有set方法 , set方法的方法名由set + 属性首字母大写 , 若是属性是boolean类型 , 没有set方法 , 是 is ,主要有常量、Bean注入、数组注入、List注入、Map注入、set注入、Null注入、Properties注入等。
测试pojo类 :
Address.java

public class Address {
 
     private String address;
 
     public String getAddress() {
         return address;
    }
 
     public void setAddress(String address) {
         this.address = address;
    }
 }

Student.java

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;
     // get  set  toString
    }
 }

一、常量注入

<bean id="student" class="com.sowhat.pojo.Student">
     <property name="name" value="小明"/>
 </bean>

测试:

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

二、Bean注入
注意点:这里的值是一个引用,ref

<bean id="addr" class="com.sowhat.pojo.Address">
     <property name="address" value="重庆"/>
 </bean>
 
 <bean id="student" class="com.sowhat.pojo.Student">
     <property name="name" value="小明"/>
     <property name="address" ref="addr"/>
 </bean>

三、数组注入

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

四、List注入

<property name="hobbys">
     <list>
         <value>听歌</value>
         <value>看电影</value>
         <value>登山</value>
     </list>
 </property>

五、Map注入

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

六、set注入

<property name="games">
     <set>
         <value>LOL</value>
         <value>BOB</value>
         <value>COC</value>
     </set>
 </property>

七、Null注入

<property name="wife"><null/></property>

八、Properties注入

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

p命名和c命名注入

public class User2 {
     private String name;
     private int age;
 
     public void setName(String name) {
         this.name = name;
    }
 
     public void setAge(int age) {
         this.age = age;
    }
 
     @Override
     public String toString() {
         return "User2{" +
                 "name='" + name + '\'' +
                 ", age=" + age +
                 '}';
    }
 }
一、P命名空间注入 : 须要在头文件中加入约束文件

导入约束 :

xmlns:p="http://www.springframework.org/schema/p
 
 <!--P(属性: properties)命名空间 , 属性依然要设置set方法,调用系统自带无参构造函数而后调用set方法-->
 <bean id="user2" class="com.sowhat.pojo.User2" p:name="sowhat" p:age="18"/>

测试:

@org.junit.Test
	public void test2(){
		ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
		//在执行getBean的时候, user已经建立好了 , 经过无参构造
		User2 user2 = (User2) context.getBean("user2");
		//调用对象的方法 .
		System.out.println(user2);
	}
二、c 命名空间注入 : 须要在头文件中加入约束文件

导入约束 :

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

c 就是所谓的构造器注入!调用有参构造函数。

Bean的做用域

在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为bean。简单地讲,bean就是由IoC容器初始化、装配及管理的对象 。

类别 说明
singleton 在Spring IOC容器中仅存在一个Bean实例,能够认为是单例模式,是spring默认模式
prototype 每次从容器中调用Bean时都会返回一个新的实例,每次getBean()都至关于执行了new xxxBean()
request 每次HTTP请求都会建立一个新的Bean,该做用域仅适用于WebApplicationContext环境
session 同一个HTTP Session共享一个Bean,不一样Session使用不一样Bean,仅使用于WebApplicationContext环境

几种做用域中,request、session做用域仅在基于web的应用中使用(没必要关心你所采用的是什么web应用框架),只能用在基于web的Spring ApplicationContext环境。

Singleton

当一个bean的做用域为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">

测试:

@Test
 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);
 }
Prototype

当一个bean的做用域为Prototype,表示一个bean定义对应多个对象实例。Prototype做用域的bean会致使在每次对该bean请求(将其注入到另外一个bean中,或者以程序的方式调用容器的getBean()方法)时都会建立一个新的bean实例。Prototype是原型类型,它在咱们建立容器的时候并无实例化,而是当咱们获取bean的时候才会去建立一个对象,并且咱们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype做用域,而对无状态的bean则应该使用singleton做用域。在XML中将bean定义成prototype,能够这样配置:

<bean id="account" class="com.foo.DefaultAccount" scope="prototype"/>  
  或者
 <bean id="account" class="com.foo.DefaultAccount" singleton="false"/>
Request

当一个bean的做用域为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实例将被销毁。

Session

当一个bean的做用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例。该做用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:

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

针对某个HTTP Session,Spring容器会根据userPreferences bean定义建立一个全新的userPreferences bean实例,且该userPreferences bean仅在当前HTTP Session内有效。与request做用域同样,能够根据须要放心的更改所建立实例的内部状态,而别的HTTP Session中根据userPreferences建立的实例,将不会看到这些特定于某个HTTP Session的状态变化。当HTTP Session最终被废弃的时候,在该HTTP Session做用域内的bean也会被废弃掉。

四、自动装配

自动装配说明

  • 自动装配是使用spring知足bean依赖的一种方法
  • spring会在应用上下文中为某个bean寻找其依赖的bean。

Spring中bean有三种装配机制,分别是:

  • 在xml中显式配置;
  • 在java中显式配置;
  • 隐式的bean发现机制和自动装配。

这里咱们主要讲第三种:自动化的装配bean,Spring的自动装配须要从两个角度来实现,或者说是两个操做:

  • 组件扫描(component scanning):spring会自动发现应用上下文中所建立的bean;
  • 自动装配(autowiring):spring自动知足bean之间的依赖,也就是咱们说的IoC/DI

组件扫描和自动装配组合发挥巨大威力,使得显示的配置下降到最少。推荐不使用自动装配xml配置 , 而使用注解

环境搭建

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

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

二、新建一个用户类 User

public class User {
   private Cat cat;
   private Dog dog;
   private String str;
   // get set 方法
}

三、编写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.sowhat.pojo.Dog"/>
    <bean id="cat" class="com.sowhat.pojo.Cat"/>

    <bean id="user" class="com.sowhat.pojo.User">
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>
        <property name="str" value="sowhat"/>
    </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();
  }
}

结果正常输出,环境OK

byName

autowire byName (按名称自动装配),因为在手动配置xml过程当中,经常发生字母缺漏和大小写等错误,而没法对其进行检查,使得开发效率下降。采用自动装配将避免这些错误,而且使配置简单化。

测试:
一、修改bean配置,增长一个属性 autowire=“byName”

<bean id="user" class="com.sowhat.pojo.User" autowire="byName">
   <property name="str" value="sowhat"/>
   <!-- 注意此处没有 ref cat dog 自动查找配置 -->
</bean>

二、再次测试,结果依旧成功输出!

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

三、咱们将 cat 的bean id修改成 catXXX

四、再次测试, 执行时报空指针java.lang.NullPointerException。由于按byName规则找不对应set方法,真正的setCat就没执行,对象就没有初始化,因此调用时就会报空指针错误。

小结:

当一个bean节点带有 autowire=byName的属性时。

  • 将查找其类中全部的set方法名,例如setCat,得到将set去掉而且首字母小写的字符串,即cat。

  • 去spring容器中寻找是否有此字符串名称id的对象。

  • 若是有,就取出注入;若是没有,就报空指针异常。

byType

autowire byType (按类型自动装配),使用autowire byType首先须要保证:同一类型的对象,在spring容器中惟一。若是不惟一,会报不惟一的异常。NoUniqueBeanDefinitionException

测试:

一、将user的bean配置修改一下 : autowire=“byType”

<bean id="user" class="com.sowhat.pojo.User" autowire="byType">
        <property name="str" value="sowhat"/>
    </bean>

二、测试,正常输出

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

三、再注册一个cat 的bean对象!

<bean id="dog1" class="com.sowhat.pojo.Dog"/>
    <bean id="cat" class="com.sowhat.pojo.Cat"/>
    <bean id="cat2" class="com.sowhat.pojo.Cat"/>
    <bean id="user" class="com.sowhat.pojo.User" autowire="byType">
        <property name="str" value="sowhat"/>
    </bean>

在这里插入图片描述
五、删掉cat2,将cat的bean名称改掉!测试!由于是按类型装配,因此并不会报异常,也不影响最后的结果。甚至将id属性去掉,也不影响结果。

<bean id="dog1" class="com.sowhat.pojo.Dog"/>
    <bean class="com.sowhat.pojo.Cat"/>
    <bean id="user" class="com.sowhat.pojo.User" autowire="byType">
        <property name="str" value="sowhat"/>
    </bean>

这就是按照类型自动装配!

使用注解

jdk1.5开始支持注解,spring2.5开始全面支持注解。简单的一些自动装备建议使用注解更加便捷。

准备工做:利用注解的方式注入属性。

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

@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;
  }
}

二、此时配置文件内容

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

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

</beans>

三、测试,成功输出结果!

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

PS:@Autowired(required=false) 说明:false,对象能够为null;true,对象必须存对象,不能为null。默认为true。

@Qualifier
  • @Autowired是根据类型自动装配的,加上@Qualifier则能够根据byName的方式自动装配
  • @Qualifier不能单独使用。

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

<bean id="dog1" class="com.sowhat.pojo.Dog"/>
<bean id="dog2" class="com.sowhat.pojo.Dog"/>
<bean id="cat1" class="com.sowhat.pojo.Cat"/>
<bean id="cat2" class="com.sowhat.pojo.Cat"/>

二、没有加Qualifier测试,直接报错
三、在属性上添加Qualifier注解

@Autowired
@Qualifier(value = "cat2")
private Cat cat;
@Autowired
@Qualifier(value = "dog2")
private Dog dog;

测试,成功输出!

@Resource

@Resource若有指定的name属性,先按该属性进行byName方式查找装配;

  • 其次再进行默认的byName方式进行装配;
  • 若是以上都不成功,则按byType的方式自动装配。
  • 都不成功,则报异常。
  • 前面的几个都是spring中的注解,Resource是javax.annotation.Resource
  • 用的很少,仍是 @Autowired 多。

实体类:

public class User {
   //若是容许对象为null,设置required = false,默认为true
   @Resource(name = "cat2")
   private Cat cat;
   @Resource
   private Dog dog;
   @Value("123321") // 等价于 xml中的property 设置
   private String str;
}

beans.xml

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

测试:结果OK

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

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

实体类上只保留注解

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

结果:OK

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

小结

@Autowired与@Resource 异同

  • 一、@Autowired与@Resource均可以用来装配bean。均可以写在字段上,或写在setter方法上。
  • 二、@Autowired默认按类型装配(属于spring规范),默认状况下必需要求依赖对象必须存在,若是要容许null 值,能够设置它的required属性为false,如:@Autowired(required=false) ,若是咱们想使用名称装配能够结合@Qualifier注解进行使用
  • 三、@Resource(属于J2EE范围),默认按照名称进行装配,名称能够经过name属性进行指定。若是没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,若是注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。可是须要注意的是,若是name属性一旦指定,就只会按照名称进行装配。

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

五、使用注解开发

在spring4以后,想要使用注解形式,必须得要引入aop的包

在这里插入图片描述

注解开发配置

一、咱们以前都是使用 bean 的标签进行bean注入(在xml文件中将Bean注册好),可是实际开发中,咱们通常都会使用注解!具体配置以下:

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

    <!--指定注解扫描包-->
    <context:component-scan base-package="com.sowhat.pojo"/>
</beans>

二、在指定包下编写类,增长注解

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

	@Override
	public String toString()
	{
		return "Cat{}";
	}
}
@Component()
public class Dog {
   public void shout() {
       System.out.println("wang~");
  }

	@Override
	public String toString()
	{
		return "Dog{}";
	}
}
@Component()
public class User
{
	@Autowired
	private Cat cat;
	@Autowired
	private Dog dog;
	@Value("123321")
	private String str;

	public Cat getCat()
	{
		return cat;
	}

	public Dog getDog()
	{
		return dog;
	}

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

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

三、测试

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

结果:
在这里插入图片描述

属性注入

使用注解注入属性

一、能够不用提供set方法,直接在直接名上添加@value(“值”)

@Component("user")
// 至关于配置文件中 <bean id="user" class="当前注解的类"/>
public class User {
   @Value("sowhat")
   // 至关于配置文件中 <property name="name" value="sowhat"/>
   public String name;
}

二、若是提供了set方法,在set方法上添加@value(“值”);

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

衍生注解

咱们这些注解,就是替代了在配置文件当中配置步骤而已!更加的方便快捷!

@Component三个衍生注解

为了更好的进行分层,Spring可使用其它三个注解,功能同样,目前使用哪个功能都同样。

  • @Controller:web层
  • @Service:service层
  • @Repository:dao层

写上这些注解,就至关于将这个类交给Spring管理装配了!

自动装配注解

在Bean的自动装配已经讲过了 主要是byType or byName,能够回顾!

做用域

@scope

singleton:默认的,Spring会采用单例模式建立这个对象。关闭工厂 ,全部的对象都会销毁。
prototype:多例模式。关闭工厂 ,全部的对象不会销毁。内部的垃圾回收机制会回收

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

小结

XML与注解比较

  • XML能够适用任何场景 ,结构清晰,维护方便。
  • 注解不是本身提供的类使用不了,开发简单方便

xml与注解整合开发 :推荐最佳实践

  • xml管理Bean
  • 注解完成属性注入
  • 使用过程当中, 能够不用扫描,扫描是为了类上的注解

自动装载配置:

<context:annotation-config/>

做用:

  • 进行注解驱动注册,从而使注解生效
  • 用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显示的向Spring注册
  • 若是不扫描包,就须要手动配置bean
  • 若是不加注解驱动,则注入的值为null!

基于Java类进行配置

JavaConfig 原来是 Spring 的一个子项目,它经过 Java 类的方式提供 Bean 的定义信息,在 Spring4 的版本, JavaConfig 已正式成为 Spring4 的核心功能 。彻底不用xml配置,全权交给Java来作。

测试:

一、编写一个实体类,Dog

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);
}

四、成功输出结果!

导入其余配置如何作呢?

一、咱们再编写一个配置类!

@Configuration  //表明这是一个配置类
public class MyConfig2 {
}

二、在以前的配置类中咱们来选择导入这个配置类

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

关于这种Java类的配置方式,咱们在以后的SpringBootSpringCloud中还会大量看到,咱们须要知道这些注解的做用便可!底层来来回回也是常见到若干注解。

@Configuation总结

  • @Configuation等价于<Beans> </Beans>
  • @Bean等价于<Bean> </Bean>
  • @ComponentScan等价于<context:component-scan base-package="com.sowhat.pojo"/>
六、静态/动态代理模式

为何要学习代理模式,由于AOP的底层机制就是动态代理!

代理模式:

  • 静态代理
  • 动态代理

学习aop以前 , 咱们要先了解一下代理模式!

静态代理

静态代理角色分析

  • 抽象角色 : 通常使用接口或者抽象类来实现
  • 真实角色 : 被代理的角色
  • 代理角色 : 代理真实角色 ; 代理真实角色后 , 通常会作一些附属的操做 .
  • 客户 : 使用代理角色来进行一些操做 .

代码实现

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

分析:在这个过程当中,你直接接触的就是中介,就如同现实生活中的样子,你看不到房东,可是你依旧租到了房东的房子经过代理,这就是所谓的代理模式,程序源自于生活。

静态代理的好处:

  • 可使得咱们的真实角色更加纯粹 . 再也不去关注一些公共的事情 .
  • 公共的业务由代理来完成 . 实现了业务的分工 ,
  • 公共业务发生扩展时变得更加集中和方便 .

缺点 :

  • 类多了 , 多了代理类 , 工做量变大了 . 开发效率下降 .

咱们想要静态代理的好处,又不想要静态代理的缺点,因此 , 就有了动态代理 !

静态代理再理解

同窗们练习完毕后,咱们再来举一个例子,巩固你们的学习!

练习步骤:

一、建立一个抽象角色,好比咋们平时作的用户业务,抽象起来就是增删改查!

//抽象角色:增删改查业务
public interface UserService {
   void add();
   void delete();
   void update();
   void query();
}

二、咱们须要一个真实对象来完成这些增删改查操做

//真实对象,完成增删改查操做的人
public class UserServiceImpl implements UserService {

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

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

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

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

三、需求来了,如今咱们须要增长一个日志功能,怎么实现!

思路1 :在实现类上增长代码,违背开闭原则。。

思路2:使用代理来作,可以不改变原来的业务状况下,实现此功能就是最好的了!

四、设置一个代理类来处理日志!代理角色

//代理角色,在这里面增长日志的实现
public class UserServiceProxy implements UserService {
   private UserServiceImpl userService;

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

   public void add() {
       log("add");
       userService.add();
  }

   public void delete() {
       log("delete");
       userService.delete();
  }

   public void update() {
       log("update");
       userService.update();
  }
   public void query() {
       log("query");
       userService.query();
  }
   public void log(String msg){
       System.out.println("执行了"+msg+"方法");
  }
}

五、测试访问类:

public class Client {
   public static void main(String[] args) {
       //真实业务
       UserServiceImpl userService = new UserServiceImpl();
       //代理类
       UserServiceProxy proxy = new UserServiceProxy();
       //使用代理类实现日志功能!
       proxy.setUserService(userService);
       proxy.add();
  }
}

OK,到了如今代理模式你们应该都没有什么问题了,重点你们须要理解其中的思想;

咱们在不改变原来的代码的状况下,实现了对原有功能的加强,这是AOP中最核心的思想

动态代理

  • 动态代理的角色和静态代理的同样 .

  • 动态代理的代理类是动态生成的, 静态代理的代理类是咱们提早写好的。

  • 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理

    • 基于接口的动态代理 ===> JDK动态代理

    • 基于类的动态代理 ===> cglib

    • 如今用的比较多的是 javasist 来生成动态代理 . 百度一下javasist

    • 咱们这里使用JDK的原生代码来实现,其他的道理都是同样的!、

*.java 文件最终要编译成*.class文件而后通过JVM加载后执行,问题是*.class文件来源能够有多个,好比常规的*.java文件,网络传输*.class文件,内存中生成的*.class文件。动态代理用的就是内存中生成的class文件。细节部分可参考 AOP手动实现

动态代理必备两类

核心 : InvocationHandler和Proxy,百度便可看到各类教程,跟八股文同样的固定格式与套路。

接口 InvocationHandler,需实现invoke函数。

Object invoke(Object proxy, 方法 method, Object[] args);
//参数
//proxy: 调用该方法的代理实例
//method: -所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它能够是代理类继承该方法的代理接口的超级接口。
//args: -包含的方法调用传递代理实例的参数值的对象的阵列,或null若是接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。

Proxy代理类

//生成代理类
public Object getProxy(){
   return Proxy.newProxyInstance(loader,interfaces,h);
}
loader:类加载器来定义代理类
interfaces:代理类实现的接口列表
h:调度方法调用的处理函数

动态代理实战

代码实现 抽象角色和真实角色和以前的同样!

Rent . java 即抽象角色

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

Host . java 即真实角色

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

ProxyInvocationHandler. java 即代理角色

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("收中介费");
  }
}

Client . java

//租客
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();
  }
}

核心一个动态代理 , 通常代理某一类业务 , 一个动态代理能够代理多个类,代理的是接口

动态代理深化理解

咱们来使用动态代理实现代理咱们后面写的UserService!咱们也能够编写一个通用的动态代理实现的类!全部的代理对象设置为Object便可!

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);
  }

   // proxy : 代理类
   // method : 代理类的调用处理程序的方法对象.
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       log(method.getName());
       Object result = method.invoke(target, args);
       return result;
  }

   public void log(String methodName){
       System.out.println("执行了"+methodName+"方法");
  }
}

测试!

public class Test {
   public static void main(String[] args) {
       //真实对象
       UserServiceImpl userService = new UserServiceImpl();
       //代理对象的调用处理程序
       ProxyInvocationHandler pih = new ProxyInvocationHandler();
       pih.setTarget(userService); //设置要代理的对象
       UserService proxy = (UserService)pih.getProxy(); //动态生成代理类!
       proxy.delete();
  }
}

测试,增删改查,查看结果!

动态代理的好处

静态代理有的它都有,静态代理没有的,它也有!

  • 可使得咱们的真实角色更加纯粹 . 再也不去关注一些公共的事情 。
  • 公共的业务由代理来完成 . 实现了业务的分工 。
  • 公共业务发生扩展时变得更加集中和方便 。
  • 一个动态代理 , 通常代理某一类业务。
  • 一个动态代理能够代理多个类,代理的是接口!
七、AOP就这么简单

什么是AOP

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

在这里插入图片描述

Aop在Spring中的做用

提供声明式事务;容许用户自定义切面,SpringAOP中,经过Advice定义横切逻辑,Spring中支持5种类型的Advice,五种通知

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

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->·
<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.9.4</version>
</dependency>
第一种方式 实现指定接口实现AOP

经过 Spring API 实现,首先编写咱们的业务接口和实现类

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

    <!--aop的配置-->
    <aop:config>
        <!--切入点 expression:表达式匹配要执行的方法-->
        <aop:pointcut id="pointcut" expression="execution(* com.sowhat.demo4.UserServiceImpl.*(..))"/>
        <!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>

</beans>

测试

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

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

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

第二种方式 自定义类来实现Aop

目标业务类不变依旧是userServiceImpl

第一步 : 写咱们本身的一个切入类

public class DiyPointcut {

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

去spring中配置

<?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="diy" class="com.sowhat.demo4.DiyPointcut"/>

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

</beans>

测试:

public class MyTest {
   @Test
   public void test(){
       ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
       UserService userService = (UserService) context.getBean("userService");
       userService.add();
  }
}
第三种方式 使用注解实现

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

package com.sowhat.demo4;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

@Aspect
public class AnnotationPointcut {

	@Pointcut("execution(* com.sowhat.demo4.UserServiceImpl.*(..))")
	public void pointCut() {}


   @Before("pointCut()")
   public void before(){
       System.out.println("---------方法执行前---------");
  }

   @After("pointCut()")
   public void after(){
       System.out.println("---------方法执行后---------");
  }

   @Around("pointCut()")
   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);
  }
}

xml配置

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

    <!--第三种方式:注解实现-->
    <bean id="userService" class="com.sowhat.demo4.UserServiceImpl"/>
    <bean id="annotationPointcut" class="com.sowhat.demo4.AnnotationPointcut"/>
    <aop:aspectj-autoproxy proxy-target-class="false"/>

</beans>

aop:aspectj-autoproxy:说明

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

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

End

公共部分:

  1. xml中导入切面依赖。
  2. 引入切面模糊表达式依赖pom。

1、继承接口实现AOP

  1. 前置依赖后置依赖的继承接口实现类。
  2. xml中配置切面依赖。

2、自定义类实现AOP

  1. 自定义切面类跟函数
  2. xml中配置切面依赖

3、基于注解实现AOP

  1. 自定义注解切面类
  2. xml中配置切面自动autoproxy

五种通知的执行顺序

在这里插入图片描述

若是存在多个切面,多切面执行时,采用了责任链设计模式。切面的配置顺序决定了切面的执行顺序,多个切面执行的过程,相似于方法调用的过程,在环绕通知的proceed()执行时,去执行下一个切面或若是没有下一个切面执行目标方法,从而达成了以下的执行过程:
在这里插入图片描述
若是目标方法抛出异常:在这里插入图片描述
不一样通知功能:

通知类型 功能
环绕通知 控制事务 权限控制
后置通知 记录日志(方法已经成功调用)
异常通知 异常处理 控制事务
最终通知 记录日志(方法已经调用,但不必定成功)
8、Spring整合MyBatis

什么是 MyBatis-Spring?

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

知识基础

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

MyBatis-Spring 须要如下版本相互兼容:

MyBatis-Spring MyBatis Spring 框架 Spring Batch Java
2.0 3.5+ 5.0+ 4.0+ Java 8+
1.3 3.4+ 3.2.2+ 2.1+ Java 6+

若是使用 Maven 做为构建工具,仅须要在 pom.xml 中加入如下代码便可:

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

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

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

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

注意:SqlSessionFactory须要一个 DataSource(数据源)。这能够是任意的 DataSource,只须要和配置其它 Spring 数据库链接同样配置它就能够了。

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

在 MyBatis 中,你可使用 SqlSessionFactory 来建立 SqlSession。一旦你得到一个 session 以后,你可使用它来执行映射了的语句,提交或回滚链接,最后,当再也不须要它的时候,你能够关闭 session。

SqlSessionFactory有一个惟一的必要属性:用于 JDBC 的 DataSource。这能够是任意的 DataSource 对象,它的配置方法和其它 Spring 数据库链接是同样的。

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

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

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

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

可使用 SqlSessionFactory 做为构造方法的参数来建立 SqlSessionTemplate 对象。

一、applicationContext.xml配置

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

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

    <bean id="userMapper" class="com.sowhat.mapper.UserMapperImpl">
        <property name="sqlSessionTemplate" ref="sqlSession"/>
    </bean>

    <bean id="userMapper2" class="com.sowhat.mapper.UserMapperImpl2">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

</beans>

二、spring-dao.xml配置,全权接管mybatis配置几乎

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


    <!-- DataSource 使用Spring的数据源替换Mybatis c3po dbcp druid spring 来管理数据库链接  -->
    <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
    </bean>

    <!--sqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 绑定MyBatis 配置文件 -->
        <property name="dataSource" ref="datasource"/>
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/sowhat/mapper/*.xml"/>
         <!-- <property name="typeAliases" value="com.sowhat.pojo.User"/>-->
    </bean>

    <!-- SqlSessionTemplate 就是咱们使用的sqlSession-->
    <!-- 若是用了方式二 SqlSessionDaoSupport 就不须要sqlSession了 -->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!-- 只能经过构造器注入 没有set方法 -->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>
    <!-- 至此 Spring 接管了 MyBatis的建立工做 通常状况下此处就不用动了-->
</beans>

三、mybatis-config.xml配置

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

    <properties resource="db.properties">
        <property name="password" value="123"/>
        <!--  此处的优先级小于外面 db.properties 设定参数的优先级   -->
    </properties>
    <typeAliases>
        <package name="com.sowhat.pojo"/>
    </typeAliases>
</configuration>

四、db.properties

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

五、pom.xml

<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>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>

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

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.2</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.10</version>
        </dependency>
    </dependencies>

    <!-- 解决文件过滤问题 -->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

六、pojo.xml

import lombok.Data;
@Data
public class User {
    private int id;
    private String name;
    private  String pwd;
}

七、接口跟Mapper

public interface UserMapper {
    public List<User> selectUsers();
}
<?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.sowhat.mapper.UserMapper">

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

八、自动实现类

public class UserMapperImpl implements UserMapper {
    // 咱们是因此操做都使用SqlSessionTemplate了
    private SqlSessionTemplate sqlSessionTemplate;

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

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

九、测试:

@Test
    public void test(){
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserMapper userMapper = classPathXmlApplicationContext.getBean("userMapper", UserMapper.class);
        for (User selectUser : userMapper.selectUsers()) {
            System.out.println(selectUser);
        }
    }

PS :SqlSessionDaoSupport
mybatis-spring1.2.3版以上的才有这个 .dao继承Support类 , 直接利用 getSqlSession() 得到 , 而后直接注入SqlSessionFactory . 比起方式1 , 不须要管理SqlSessionTemplate , 并且对事务的支持更加友好 . 可跟踪源码查看。
具体代码查看地址:spring整合mybatis

总结

Spring整合MyBatis的核心其实就是xml配置地狱。

  1. applicationContext.xml是全局配置文件,里面能够注册若干Bean。import若干配置文件
  2. spring-dao.xml就是spring整合mybatis的配置文件,通常状况下不多动。
  3. mybatis-config.xml 是mybatis的配置文件,spring-dao.xml能够引入该配置。
  4. db.properties是 mybatis-config.xml 配置须要的信息。
  5. 总体感受就是配置 配置 再配置。
  6. MyBatis整合到spring之后能够彻底不要mybatis的配置文件,除了这些方式能够实现整合以外,咱们还可使用注解来实现,这个等SpringBoot教程时候还会测试整合!
九、声明式事务

回顾事务

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

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

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

事务四个属性ACID

  1. 原子性(atomicity)
    事务是原子性操做,由一系列动做组成,事务的原子性确保动做要么所有完成,要么彻底不起做用

  2. 一致性(consistency)
    一旦全部事务动做完成,事务就要被提交。数据和资源处于一种知足业务规则的一致性状态中

  3. 隔离性(isolation)
    可能多个事务会同时处理相同的数据,所以每一个事务都应该与其余事务隔离开来,防止数据损坏

  4. 持久性(durability)
    事务一旦完成,不管系统发生什么错误,结果都不会受到影响。一般状况下,事务的结果被写到持久化存储器中

Spring中的事务管理

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

  • 编程式事务管理

    • 将事务管理代码嵌到业务方法中来控制事务的提交和回滚
    • 缺点:必须在每一个事务操做业务逻辑中包含额外的事务管理代码
  • 声明式事务管理

    • 通常状况下比编程式事务好用。
    • 将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
    • 将事务管理做为横切关注点,经过aop方法模块化。Spring中经过Spring AOP框架支持声明式事务管理。
      给出一个生命式事务的demo
<?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:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <!-- DataSource 使用Spring的数据源替换Mybatis c3po dbcp druid spring 来管理数据库链接  -->
    <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
        <property name="url"
                  value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
    </bean>

    <!--sqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 绑定MyBatis 配置文件 -->
        <property name="dataSource" ref="datasource"/>
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/sowhat/mapper/*.xml"/>
        <!-- <property name="typeAliases" value="com.sowhat.pojo.User"/>-->
    </bean>

    <!-- SqlSessionTemplate 就是咱们使用的sqlSession-->
    <!-- 若是用了方式二 SqlSessionDaoSupport 就不须要sqlSession了 -->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!-- 只能经过构造器注入 没有set方法 -->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>

    <!-- 配置声明式事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg ref="datasource"/>
    </bean>


    <!-- 结合AOP 实现事务 -->
    <!--配置事务通知 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--给那些方法配置事务,配置事务的传播特性  propagation -->
            <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.sowhat.mapper.UserMapperImpl.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txpointCut"/>
    </aop:config>
    <!-- 至此 Spring 接管了 MyBatis的建立工做 通常状况下此处就不用动了-->
</beans>

spring事务传播特性:

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

  • propagation_requierd:若是当前没有事务,就新建一个事务,若是已存在一个事务中,加入到这个事务中,这是最多见的选择。
  • propagation_supports:支持当前事务,若是没有当前事务,就以非事务方法执行。
  • propagation_mandatory:使用当前事务,若是没有当前事务,就抛出异常。
  • propagation_required_new:新建事务,若是当前存在事务,把当前事务挂起。
  • propagation_not_supported:以非事务方式执行操做,若是当前存在事务,就把当前事务挂起。
  • propagation_never:以非事务方式执行操做,若是当前事务存在则抛出异常。
  • propagation_nested:若是当前存在事务,则在嵌套事务内执行。若是当前没有事务,则执行与propagation_required相似的操做

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

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

就比如,咱们刚才的几个方法存在调用,因此会被放在一组事务当中!若是Spring中不配置,就须要咱们手动提交控制事务;事务在项目开发过程很是重要,涉及到数据的一致性的问题,不容马虎!

十、循环依赖

什么是循环依赖,如何解决循环依赖,三层缓存做用

参考

spring整合mybatis
帅丙
依赖通俗说

相关文章
相关标签/搜索