Spring进门

什么是Spring

简介

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

  • 是一个免费的开源容器
  • 支持事务的处理、对框架整合的支持

环境

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

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

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

</beans>

组成

在这里插入图片描述

IOC

控制反转IoC(Inversion of Control),是一种设计思想,是通过描述(XML或注解)并通过第三方去生产或获取特定对象的一种方式。

本质

在没有IoC的程序中 , 我们会使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,而控制反转后,我们会将对象的创建交给第三方实现,从而大大降低系统的耦合性!

通俗的讲,控制反转其实就是将获得依赖对象的方式反转了,由主动的编程变成被动的接收!

在这里插入图片描述

在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。

控制反转 :

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

依赖注入 : 就是利用set方法来进行注入

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

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

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

原型

UserDao.java

public interface UserDao {
    public void getUser();
}

UserDaoImpl.java

public class UserDaoImpl implements UserDao{

    public void getUser() {
        System.out.println("普通用户");
    }
}

UserMySqlDaoImpl.java

public class UserMySqlDaoImpl implements UserDao{

    public void getUser() {
        System.out.println("MySQL普通用户");
    }
}

UserService.java

public interface UserService {
    public void getUser();
}

UserServiceImpl.java

public class UserServiceImpl implements UserService{
    private UserDao userDao;
	
	//无论创建多少UserDao的实现类,都可以调用此set方法,使用需要的对象
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void getUser() {
        userDao.getUser();
    }
}

UserTest.java

public class UserTest {
    @Test
    public void getUserTest(){
        UserServiceImpl userService = new UserServiceImpl();
        userService.setUserDao(new UserMySqlDaoImpl());
        userService.getUser();
    }
}

使用set让我们不用再去管理对象的创建了,系统的耦合性大大降低。

案例

1、修改原型
beans.xml

    <bean id="mySqlDaoImpl" class="com.indi.dao.UserMySqlDaoImpl" />
    <bean id="userDaoImpl" class="com.indi.dao.UserDaoImpl"/>

    <bean id="userServiceImpl" class="com.indi.service.UserServiceImpl">
        <!--
	        property标签
	            给对象中的属性赋值的一个过程
	        属性:
	            value 一般使用基本数据类型
	            ref 引用Spring容器中创建好的对象
    	-->
        <property name="userDao" ref="userDaoImpl"></property>
    </bean>

UserTest.java

    @Test
    public void getUserTest(){
        //获取Spring的上下文对象,也就是Spring的容器
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        UserServiceImpl usi = (UserServiceImpl) context.getBean("userServiceImpl");

        usi.getUser();
    }

现在要实现不同的操作 , 不需要在程序中去改动了 , 只需要在xml配置文件中进行修改即可。
Spring中所谓的IoC就是对象由Spring 来创建 , 管理 , 装配 !

IOC创建对象

  • 默认使用无参构造创建对象
  • 使用有参构造创建
    • 下标赋值
    		<constructor-arg index="0" value="qwe"/>
    
    • 通过类型创建【不推荐】
    		<constructor-arg type="java.lang.String" value="qwe"/>
    
    • 通过参数名设置【推荐】
    		<constructor-arg name="name" value="qwe"/>
    

总结:在配置文件加载的时候,容器中未指定有参构造的对象都会通过默认的无参构造初始化。

Spring配置

别名

		<!--
			alias
				为已声明的对象起别名
			属性
				name		对象名
				alias		要使用的别名
		-->
		<alias name="user" alias="userNew"/>

Bean的配置

		<!--
	        bean标签
	            就是由Spring创建和管理的java对象
	        属性:
	            id  对象名
	            class   对象的全限定名:包名+类型
	            name	也是别名,并且可以起多个别名,使用常用的分隔符分开即可
		-->
		<bean id="user" class="com.indi.pojo.User" name="usern user1 user0">
		</bean>

import

一般用于团队使用,比如:在applicationContext.xml中整合多个bean.xml,便于管理

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

依赖注入

说白了就是为spring中对象的属性赋值的一个操作

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

构造器注入

见IOC创建对象

Set注入

要求被注入的属性 , 必须有set方法 , set方法的方法名由set + 属性首字母大写 , 如果属性是boolean类型 , 没有set方法 , 是 is .

		<!--基本数据类型-->
		<property name="name" value="qwe"/>

		<!--bean-->
		<property name="address" ref="address"/>

		<!--数组-->
		<property name="books">
			<array>
				<value>时间简史</value>
				<value>活着</value>
				<value>为了你</value>
			</array>
		</property>

		<!--List-->
		<property name="hobbys">
			<list>
				<value>1</value>
				<value>2</value>
				<value>3</value>
			</list>
		</property>

		<!--map-->
		<property name="card">
			<map>
				<entry key="昵称" value="qwe"/>
				<entry key="全称" value="qawsed"/>
			</map>
		</property>

		<!--set-->
		<property name="games">
			<set>
				<value>LOL</value>
				<value>AOA</value>
				<value>COC</value>
			</set>
		</property>

		<!--null-->
		<property name="zero">
			<null/>
		</property>
		
		<!--properties-->
		<property name="info">
			<props>
				<prop key="username">root</prop>
				<prop key="password">123456</prop>
			</props>
		</property>

其它注入

<!--首先往beans标签中导入p命名空间和c命名空间的头文件约束-->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:p="http://www.springframework.org/schema/p"
	   xmlns:c="http://www.springframework.org/schema/c"	   
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

	<!--
		p:可以直接注入属性的值-property
	-->
	<bean id="user" class="com.indi.pojo.User" p:age="10" p:name="qwe"/>

	<!--
		c:通过构造器注入,必须要有一个有参构造器,constructor-args
	-->
	<bean id="user2" class="com.indi.pojo.User" c:age="18" c:name="ewq"/>

</beans>

bean的作用域

Spring的默认作用域是singleton
在这里插入图片描述
singleton
Singleton是单例类型,就是在创建起容器时自动创建了一个bean的对象,不管你是否使用,它都存在,每次获取到的对象都是同一个对象

	<bean id="user" class="com.indi.pojo.User" scope="singleton"/>

prototype
Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不一样。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。

	<bean id="user" class="com.indi.pojo.User" scope="prototype"/>

自动装配

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

spring会在上下文中自动查找,并自动给bean装配属性

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

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

Spring的自动装配需要从两个角度来实现,或者说是两个操作:

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

推荐不使用自动装配xml配置 , 而使用注解 .

byName自动装配

public class Dog {
    public void shout(){
        System.out.println("dog");
    }
}
public class Cat {
    public void shout(){
        System.out.println("cat");
    }
}
@Getter
@Setter
public class Person {
    private Cat cat;
    private Dog dog;
    private String name;
}

测试类

public class AutowiredTest {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Person person = context.getBean("person", Person.class);
        person.getCat().shout();
        person.getDog().shout();
    }
}

beans.xml

	<bean id="cat" class="com.indi.pojo.Cat"/>
	<bean id="dog" class="com.indi.pojo.Dog"/>

	<!--
		byName	
			会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanid
	-->
	<bean id="person" class="com.indi.pojo.Person" autowire="byName">
		<property name="name" value="ewq"/>
		<!--可直接省去为类中已声明对象赋值的过程,这就是自动装配的作用-->
	</bean>

byType自动装配

	<!--byType可省略id-->
	<bean class="com.indi.pojo.Cat"/>
	<bean class="com.indi.pojo.Dog"/>

	<!--
		byType	
			会自动在容器上下文中查找,和自己对象属性类型相同的beanid,
			必须保证装配的类型全局唯一、并且bean需要和自动注入的属性类型一致
	-->
	<bean id="person" class="com.indi.pojo.Person" autowire="byType">
		<property name="name" value="ewq"/>
	</bean>

使用注解自动装配

修改beans.xml,引入context约束

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

	<!--开启属性注解支持-->
	<context:annotation-config/>
</beans>

@Autowired

  • 按类型自动装配、不支持id匹配

Person.java

/**
 * 使用@Autowired注解,省略set方法,只保留get方法
 */
@Getter
public class Person {

    //required = false 允许对象为null,默认为true
    @Autowired(required = false)    
    private Cat cat;
    
    @Autowired
    private Dog dog;

    private String name;
}

@Nullable

	/**
     * @Nullable 允许方法中的参数为空
     * @param cat
     */
    public void setCat(@Nullable Cat cat) {
        this.cat = cat;
    }

beans.xml

	<bean id="cat" class="com.indi.pojo.Cat"/>
	<bean id="dog" class="com.indi.pojo.Dog"/>
	<bean id="person" class="com.indi.pojo.Person"/>

@Qualifier

  • @Autowired是根据类型自动装配的,配合@Qualifier则可以根据byName的方式自动装配
  • @Qualifier无法单独使用

beans.xml

	<bean id="cat" class="com.indi.pojo.Cat"/>
	<bean id="cat1" class="com.indi.pojo.Cat"/>

	<bean id="dog" class="com.indi.pojo.Dog"/>
	<bean id="dog2" class="com.indi.pojo.Dog"/>

Person.java

public class Person {

    @Autowired()
    @Qualifier("cat1")
    private Cat cat;
    
    @Autowired
    @Qualifier("dog")
    private Dog dog;
}

@Resource

beans.xml

	<!--写一个时,按照byName装配不上,会按照byType装配-->
	<bean id="cat123" class="com.indi.pojo.Cat"/>

	<!--写两个时,只会按照byName装配-->
	<bean id="cat" class="com.indi.pojo.Cat"/>
	<bean id="cat123" class="com.indi.pojo.Cat"/>

	<!--byName与byType都无法装配时会报错-->
	<bean id="cat123" class="com.indi.pojo.Cat"/> 
	<bean id="cat12" class="com.indi.pojo.Cat"/>

Person.java

public class Person {

	//可以手动指定name属性,先按该属性进行byName装配,优先级最高	
    @Resource(name="cat123")
    private Cat cat;

}

@Resource和@Autowired的区别:

  • 都是用来自动装配的,都可以放在属性字段上
  • @Autowired默认通过byType方式装配,要求该对象必须存在!【常用】
  • @Resource默认通过byName装配,有一个时,如果byName找不到,则通过byType装配,有两个时,直接按照byName装配,如果byName与byType都找不到,则报错!【常用】

使用注解开发

在spring4之后,想要使用注解,必须要引入aop的包
在这里插入图片描述

beans.xml

修改beans.xml,引入context约束

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

	<!--开启属性注解支持-->
	<context:annotation-config/>
</beans>

applicationContext.xml

	<!--指定要扫描的包,这个包下的注解就会生效-->
	<context:component-scan base-package="com.indi.pojo"/>

User.java

@Component//与xml中的bean作用相同,相当于new了这个对象
public class User {

}

属性注入

public class User {
    @Value("qwe")//无set方法,直接注入属性
    public String name;
}
public class User {
	private String name;

    @Value("qwe")//有set方法,注入方法
    public void setName(String name) {
        this.name = name;
    }
}

衍生注解

@Component三个衍生注解
为了更好的进行分层,MVC的其它层可以使用其它的三个注解,功能一样。

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

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

作用域

@Scope("prototyp")
pubic class User{
	
}

JavaConfig

它通过 Java 类的方式提供 Bean 的定义信息,完全取代applicationContext.xml

IndiConfig.java

@Configuration//表示自己是配置类,等于之前的beans.xml
@ComponentScan("com.indi.config")//扫描包
@Import(IndiConfig2.class)//导入另一个bean配置类
public class IndiConfig {

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

User.java

@Component
public class User {
    private String name;


    public String getName() {
        return name;
    }

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

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

MyTest.java

public class MyTest {
    public static void main(String[] args) {
        //如果完全使用了配置类方式去做,我们就得通过AnnotationConfig上下文来获取容器,通过配置类的class对象加载
        ApplicationContext context = new AnnotationConfigApplicationContext(IndiConfig.class);
        User user = (User) context.getBean("getUser");
        System.out.println(user.getName());
    }
}

AOP

简介

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

Aop在Spring中的作用

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

Aop中的名词:

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

在这里插入图片描述
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:

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

在这里插入图片描述

方式一:使用Spring的API接口实现AOP

1、首先导入AOP织入的依赖包

<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.9.4</version>
</dependency>

2、创建接口、接口实现类

public interface UserDao(){
	...
}
public class UserDaoImpl implements UserDao(){
	...
}

3、编写增强类,就是原来代码以外的新功能

public class Log implements MethodBeforeAdvice {

    /**
     * 前置通知
     * @param method 要执行的目标对象的方法
     * @param objects 被调用的方法的参数
     * @param o 目标对象
     * @throws Throwable
     */
    @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 {
    /**
     * 后置通知
     * @param returnValue 返回值
     * @param method   被调用的方法
     * @param args  被调用方法的对象的参数
     * @param target    被调用的目标对象
     * @throws Throwable
     */
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了" + target.getClass().getName() + "的" + method.getName() + "方法" + "返回值" + returnValue);
    }
}

4、去applicationContext.xml中注册,并实现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
       https://www.springframework.org/schema/aop/spring-aop.xsd">

	<!--注册bean-->
	<bean id="userDao" class="com.indi.service.UserDaoImpl"/>
	<bean id="log" class="com.indi.log.Log"/>
	<bean id="afterLog" class="com.indi.log.AfterLog"/>


	<!--aop配置,需要导入头部约束-->
	<aop:config>
		<!--
			切入点
				id:名称
				expression:
					表达式匹配要执行的方法
					格式:execution(要执行的位置 * * * * *)  
		-->
		<aop:pointcut id="pointcut" expression="execution(* com.indi.service.UserDaoImpl.*(..))"/>
		<!--
			执行环绕
				advice-ref:执行方法
				point-cut:切入点
		-->
		<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
		<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
	</aop:config>
</beans>

5、测试

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //动态代理的是接口
        UserDao userDao = context.getBean("userDao",UserDao.class);
        userDao.add();
    }
}

方式二:自定义类实现AOP[推荐]

修改applicationContext.xml

	<!--保留方式一的注册bean-->
	<!--方式二:自定义类-->
	<bean id="diy" class="com.indi.diy.DiyPointCut"/>

	<aop:config>
		<!--
			自定义切面
				ref:要引用的类
		-->
		<aop:aspect ref="diy">
			<!--切入点-->
			<aop:pointcut id="point" expression="execution(* com.indi.service.UserDaoImpl.*(..))"/>
			<!--
				通知
					pointcut-ref 在哪个切入点执行
			-->
			<aop:before method="before" pointcut-ref="point"/>
			<aop:after method="after" pointcut-ref="point"/>
		</aop:aspect>
	</aop:config>

DiyPointCut.java

package com.indi.diy;

public class DiyPointCut {
    public void before(){
        System.out.println("执行前");
    }
    public void after(){
        System.out.println("执行后");
    }
}

测试方法不变

方式三:使用注解实现AOP

AnnotationPointCut.java

//标注这个类是一个切面
@Aspect
public class AnnotationPointCut {
    @Before("execution(* com.indi.service.UserDaoImpl.*(..))")
    public void before(){
        System.out.println("执行前");
    }

    @After("execution(* com.indi.service.UserDaoImpl.*(..))")
    public void after(){
        System.out.println("执行后");
    }

    /**
     * 在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点
     * 环绕执行前>before>执行方法>环绕执行后>执行后
     */
    @Around("execution(* com.indi.service.UserDaoImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("环绕前");
        //获得签名
        Signature signature = jp.getSignature();
        System.out.println("signature"+signature);
        //执行方法
        Object proceed = jp.proceed();
        System.out.println("环绕后");
    }
}

applicationContext.xml

	<!--保留方式一的注册bean-->
	<!--方式三-->
	<bean id="annotationPointCut" class="com.indi.diy.AnnotationPointCut"/>
	<!--开启注解支持-->
	<!--
		proxy-target-class	默认为false表示使用jdk动态代理,true表示使用cglib动态代理
	-->	
	<aop:aspectj-autoproxy/>

整合Mybatis

MyBatis-Spring

MyBatis-Spring可以将 MyBatis 代码无缝地整合到 Spring 中。

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

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

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

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

SqlSessionTemplate

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

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

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

方式一

  1. 向pom.xml导入依赖
	<!--解决资源过滤问题,防止资源导出失败-->

	<dependencies>
		<!--mysql-->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.44</version>
		</dependency>

		<!--mybatis-->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.4.6</version>
		</dependency>

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

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

		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.9.4</version>
		</dependency>

		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-spring</artifactId>
			<version>1.3.2</version>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.10</version>
			<scope>provided</scope>
		</dependency>


	</dependencies>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

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

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

	<!--
		1.DataSource:使用Spring的数据源替换Mybatis的配置
		这里使用Spring提供的JDBC
	-->
	<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=UTF-8"/>
		<property name="username" value="root"/>
		<property name="password" value="123456"/>
 	</bean>

	<!--2.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/indi/mapper/*.xml"/>
	</bean>

	<!--3.注册sqlSessionTemplate,关联sqlSessionFactory-->
	<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
		<!--只能使用构造器注入sqlSessionFactory,因为它没有set方法-->
		<constructor-arg index="0" ref="sqlSessionFactory"/>
	</bean>

	<!--4.注入UserMapperImpl中的sqlSession-->
	<bean id="userMapper" class="com.indi.mapper.UserMapperImpl">
		<property name="sqlSession" ref="sqlSession"/>
	</bean>
</beans>
  1. 增加Dao接口的实现类;私有化sqlSessionTemplate
@Setter
public class UserMapperImpl implements UserMapper {
    //sqlSession不用我们自己创建了,Spring来管理
    private SqlSessionTemplate sqlSession;

    @Override
    public List<User> getList() {
        UserMapper mapper =sqlSession.getMapper(UserMapper.class);
        return mapper.getList();
    }
}
  1. 整合到applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
       
	<import resource="spring-dao.xml"/>
	<!--实例化UserMapperImpl中的sqlSession-->
	<bean id="userMapper" class="com.indi.mapper.UserMapperImpl">
		<property name="sqlSession" ref="sqlSession"/>
	</bean>
	
</beans>

  1. 测试
public class UserTest {
    @Test
    public void getList() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserMapper mapper = context.getBean("userMapper", UserMapper.class);
        mapper.getList().forEach(System.out::println);
    }
}

方式二

SqlSessionDaoSupport

dao继承Support类 , 直接利用 getSqlSession() 获得 , 然后直接注入SqlSessionFactory . 比起方式1 , 不需要管理SqlSessionTemplate , 而且对事务的支持更加友好 . 可跟踪源码查看
在这里插入图片描述

  1. 修改UserMapperImpl
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
    @Override
    public List<User> getList() {
        SqlSession sqlSession = getSqlSession();
        return sqlSession.getMapper(UserMapper.class).getList();
    }
}
  1. 在applicationContext.xml中添加bean
	<bean id="userMapper2" class="com.indi.mapper.UserMapperImpl2">
		<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
	</bean>
  1. 测试
    将方式一中的userMapper改成userMapper2即可

Spring事务

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

编程式事务管理

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

声明式事务管理

  • 一般情况下比编程式事务好用。
  • 将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
  • 将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理。

事务管理器

  • 就是 Spring的核心事务管理抽象,管理封装了一组独立于技术的方法。
  • 无论使用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 的事务传播机制都会工作在同一个事务中。

就好比,我们下面的几个方法存在调用,所以会被放在一组事务当中!

声明式事务案例

  1. spring-dao.xml导入头文件约束
<beans xmlns:tx="http://www.springframework.org/schema/tx"
	   xsi:schemaLocation="http://www.springframework.org/schema/tx
       https://www.springframework.org/schema/tx/spring-tx.xsd">
  1. spring-dao.xml配置事务管理器以及事务的通知
	<!--配置声明式事务-->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<constructor-arg ref="dataSource"/>
	</bean>

	<!--结合AOP实现事务的织入-->
	<!--配置事务的类-->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<!--给哪些方法配置事务-->
		<!--
			配置事务的传播特性:
			propagation="REQUIRED"为spring的默认事务
		-->
		<tx:attributes>
			<!--建议查询设置只读-->
		    <tx:method name="get" read-only="true"/>
			<tx:method name="*" propagation="REQUIRED"/>
		</tx:attributes>
	</tx:advice>
  1. 在UserMapperImpl中创建事务
    @Override
    public List<User> getList() {
        UserMapper mapper =getSqlSession().getMapper(UserMapper.class);
        mapper.addUser(new User(4,"lili","123456"));
        mapper.deleteUser(5);
        return mapper.getList();
    }
  1. 测试
    调用该方法时,若其中的任意一个方法执行失败,都会导致所有的方法执行失败,除非全部成功

为什么需要配置事务?

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

纯注解配置事务

  1. 在pom.xml中添加spring-test依赖
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>4.3.7.RELEASE</version>
		</dependency>
  1. pojo
@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {
    private int id;
    private String name;
    private String pwd;
}
  1. dao
@Repository
public interface UserMapper {

    @Insert("insert into mybatis.user (id, name, pwd) values (#{id}, #{name}, #{pwd})")
    public int addUser(User user);

    @Delete("delete from mybatis.user where id = #{id}")
    public int deleteUser(int id);

}
  1. service
@Service
public interface UserService {
    void testUser();
}
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper mapper;

    @Transactional
    @Override
    public void testUser() {
        mapper.addUser(new User(5, "555", "555"));
        //测试异常
//        int i = 5 / 0;
        mapper.deleteUser(5);
    }
}
  1. config
/**
 * 和数据库相关的配置类
 */

public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    @Value("${jdbc.configLocation}")
    private String configLocation;

    @Value("${jdbc.mapperLocations}")
    private String mapperLocations;

    /**
     * 创建数据源对象
     *
     * @return
     */
    @Bean
    public DataSource createDataSource() {
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);
        return ds;
    }

    /**
     * 获取sqlSessionFactory
     * @return
     * @throws Exception
     */
    @Bean
    public SqlSessionFactoryBean createSqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        factoryBean.setConfigLocation(resolver.getResource(configLocation));
        factoryBean.setMapperLocations(resolver.getResources(mapperLocations));
        return factoryBean;
    }
}

/**
 * spring的配置类,相当于applicationContext.xml
 */
@Configuration//指定这是一个核心类
@MapperScan("com.indi.mapper")//扫描dao层,实现动态代理
@ComponentScan("com.indi")//扫描该路径下的所有注解
@Import({JdbcConfig.class, TransactionConfig.class})
@PropertySource("jdbcConfig.properties")
@EnableTransactionManagement//开启事务管理器
@EnableAspectJAutoProxy
public class SpringConfiguration {

}

/**
 * 和事务相关的配置类
 */
public class TransactionConfig {

    /**
     * 创建事务管理器对象
     *
     * @param dataSource
     * @return
     */
    @Bean
    public DataSourceTransactionManager createTransactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}
/**
 *	横切
 */
@Aspect
@Component
public class UserAdvisor {
    @Pointcut("execution(* com.indi.mapper.*.*(..))")
    public void pointCut(){}

    @Before("pointCut()")
    public void before(JoinPoint jp){}
}

  1. jdbc-config.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8
jdbc.username=root
jdbc.password=123456
jdbc.configLocation=classpath:mybatis-config.xml
jdbc.mapperLocations=classpath:com/indi/mapper/*.xml
  1. mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
		PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
		"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

	<!--别名管理-->
	<typeAliases>
		<package name="com.indi.pojo" />
	</typeAliases>

</configuration>

注解说明

注解作用
@Autowired@Autowired是通过类型自动装配的,配合@Qualifier(value="…")可以通过名称自动装配
@Nullable允许字段为空
@Resource(name="")@Resource默认通过名称自动装配
注解作用
@Component表示创建了一个bean(就是创建了一个对象),通常标记在类的上面,表示将这个类注册到Spring中
@Repository一般用于dao层,用法与作用与@Component一致
@Service一般用于service层,用法与作用与@Component一致
@Controller一般用于controller层,用法与作用与@Component一致
@Value("")相当于为bean中的property赋值(也就是为对象的属性赋值)
@Scope("")表示bean的作用域,()内可填写prototype、singleton、request、sesion
注解作用
@Configuration表示这是一个配置类,等于applicationContext.xml
@ComponentScan("")扫描包
@Bean注册一个bean,通常在方法上使用,等于Bean标签
@Import(类名.class)导入其它类
注解作用
@MapperScan(“com.indi.mapper”)扫描Mapper接口,为dao层生成动态代理
@PropertySource(“jdbcConfig.properties”)/导入properties文件
@EnableTransactionManagement开启事务管理器
@Transactional表示开启事务
@EnableAspectJAutoProxy开启aop的注解
@Aspect作用在类上,标识这是一个切面
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值