Spring总结
1. 关于Spring
官网:https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html
Spring是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架。目的是为了解决企业开发的复杂性。
1.1 Spring环境搭建
maven中导入依赖,maven仓库地址:https://mvnrepository.com/。这里我们直接导入spring-webmvc的jar,它会自动将一些其他依赖的jar包导入,比如core,context,AOP,beans,expression,等。如果IDEA没有自动导包,则右击项目执行一下maven的Reload Project。
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
</dependencies>
2. IOC
在没有IOC的程序中,对象的创建与对象间的依赖关系完全在硬编码中,对象的创建由程序自己控制,控制反转之后将对象的创建转移给第三方Spring容器,在程序需要某个对象的地方,这个SpringIOC容器将根据我们的需求自动注入所依赖的对象,程序获取依赖对象的方式反转了。
IOC是Spring框架的核心内容,在实际开发中,我们可以通过XML配置,也可以通过注解,甚至可以零配置实现IOC。我们写完POJO,接口等代码之后,通过XML配置,或者注解,然后SpringIOC容器去读取配置文件或者解析注解,从而将这些类的对象组织存入到IOC容器中,程序要用的时候,IOC容器根据需求自动注入所需的对象。
IOC其实是一种思想的体现,将Bean之间进行解耦。举个例子,我们现在有一个UserDao的接口,然后有好几个实现类,我们在Service层调用Dao的实现类,如果不采用注入的方式,则需要在UserService的实现类里面直接写死调用哪个UserDao的实现类比如
private UserDao UserDaoImpl = new UserDaoImpl3();
每次需求更改的时候,都需要修改代码,违反了开闭原则(对修改关闭,对扩展开放),现在我们换一种方式,部在UserService里面去写死这个实现类,修改代码
private UserDao UserDaoImpl;
public void setUserDaoImpl(UserDao UserDaoImpl){
this.UserDaoImpl = UserDaoImpl;
}
采用set注入的方式,在需要调用UserDaoImpl的时候,直接通过set方法,注入一个我们需要的UserDao的实现类对象即可。这样,代码耦合性,维护成本将大大降低。
简单点说说一下IOC,就好比我们要炒菜请客,已经买好了食材(类),然后请一个第三方餐厅(IOC)来帮我们做,把我们的食材跟列出来的菜单交给这个餐厅就好了(进行配置)并且把菜做好了(单例模式Singleton),等待客人过来吃饭。客人来了之后,要吃什么菜,直接根据菜单去点菜就行(使用IOC的对象)
3. 依赖注入DI
Dependency Injection。这是SpingIOC容器(Bean的容器,装配了我们所配置的Bean,在我们需要的时候交给我们。)的实现方式。依赖注入的方式主要分为:
3.1 构造器注入
<constructor-arg index="0" ref="sqlSessionFactory"/>
<!-- index:下标注入,name:名字注入(常用), type:类型注入 -->
3.2 set注入【重点掌握】
set注入的前提是配置的Bean当中,每个属性都应该有对应的set方法。下面是每种类型的set注入方式演示
<?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="address" class="com.pojo.Address">
<property name="address" value="address"/>
</bean>
<!-- 各种setter注入演示 -->
<bean id="student" class="com.pojo.Student">
<!-- 基本类型注入-->
<property name="name" value="Clay"/>
<!-- 引用类型注入,引用其它已经配置好了的bean ref-->
<property name="address" ref="address"/>
<!-- 数组 array -->
<property name="strings">
<array>
<value>stringArray1</value>
<value>stringArray2</value>
<value>stringArray3</value>
</array>
</property>
<!-- List集合 list -->
<property name="stringList">
<list>
<value>list1</value>
<value>list2</value>
<value>list3</value>
</list>
</property>
<!-- Set集合 set -->
<property name="stringSet">
<set>
<value>set1</value>
<value>set2</value>
<value>set3</value>
</set>
</property>
<!-- 空指针 null -->
<property name="nullPoint">
<null></null>
</property>
<!-- Properties -->
<property name="properties">
<null></null>
</property>
<!-- Map map -->
<property name="map">
<map>
<entry key="mapKey1" value="mapValue1"></entry>
<entry key="mapKey2" value="mapValue2"></entry>
<entry key="mapKey3" value="mapValue3"></entry>
</map>
</property>
</bean>
</beans>
4. Bean的作用域
Scope | Description |
---|---|
singleton | (默认)为每个Spring IoC容器将单个bean定义的作用域限定为单个对象实例。IOC容器的默认作用域,说白了就是,只创建一个实例,整个程序都用这个实例。不建议在多线程中使用。而且在程序解析Spring的配置文件的时候,会为每一个单例作用域Bean都准备好一个对象,即使你没有在程序中调用它 |
prototype | 将单个bean定义的作用域限定为任意数量的对象实例。即我们每次在需要这个对象的时候,IOC容器会为我们创建一个新的实例,多线程情况下比单例安全 |
request | Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext . |
session | Scopes a single bean definition to the lifecycle of an HTTP Session . Only valid in the context of a web-aware Spring ApplicationContext . |
application | Scopes a single bean definition to the lifecycle of a ServletContext . Only valid in the context of a web-aware Spring ApplicationContext . |
websocket | Scopes a single bean definition to the lifecycle of a WebSocket . Only valid in the context of a web-aware Spring ApplicationContext . |
5. 自动装配
spring提供了满足bean依赖则自动注入的一种方式,它会在上下文中自动寻找,并给Bean装配属性。
5.1 XML显示配置
在bean标签中有一个属性 autowired,该属性可以实现xml的自动装配
- byName:寻找上下文跟set方法后面的bean相匹配的值。声明的bean id必须全局唯一且跟自动注入属性中的名字匹配,即id唯一且匹配!
- byType:查找IOC容器里面查找和自己对象属性类型相同的bean。每个类只能声明一个bean,即class唯一!
用法:
<bean id = "people" class="com.pojo.People" autowire="byName">
除此之外,还有构造器自动装配,但我们重点需要掌握的还是下面的自动装配方法,它也是开发中常用的方式。
5.2 @Autowired
使用注解之前需要导入约束,context,配置注解支持
https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-autowired-annotation
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
一般在配置文件中注册了的类
,在其他类的属性需要注入的时候
,(@Autowired引用类型的注入)都可以用这个注解,所以这种注解一般是引用类型的注入。如果显示定义了@Autowired的required 属性为false(默认为true),说明这个对象可以为null,通过类型名字 自动装配,当情况复杂的时候,可以用@Qualifier(value = “beanid”)指定唯一一个bean注入。在Spring5之后,多了一个注解@Nullable。某个属性被标记了这个注解,说明这个字段可以被注册为空,类似的。
类似用法:
@Autowired(required = false)
Phone phone;//自动注入phone,用在属性上面
Computer computer;
@Autowired//自动注入Computer,用在set方法上面
public void setComputer(Computer computer) {
this.computer = computer;
}
@Autowired
@Qualifier(value = "computer1")//显示的指定我们要取IOC容器里面的bean
Computer computer1;//
@Nullable
private String name;
6. 使用注解开发Spring
在Spring4之后,要使用注解开发,必须要保证AOP的包导入了。使用注解开发需要在配置文件里面导入context约束,增加注解支持,并开启注解。关于约束导入以及注解开启参考上面小节的内容
xml比较万能,维护简单方便,使用于很多复杂的场景。
注解(annotation),减少繁琐的xml配置,有一定的局限性,维护相对复杂。
6.1 @Component
使用该注解之前,需要在配置文件中开启组件扫描。
<!--开启组件扫描。扫描指定包下面的所有类,只要有被@Component以及其衍生注解标注的类,都会被注册到IOC容器里面-->
<context:component-scan base-package="com"/>
被该注解标注的类,将自动装配到SpringIOC容器当中,配合@Autowired,将减少大量的XML配置,实际开发中也大都是这种形式。
实际开发中基本上都是采用MVC模式,所以@Component也多出了几个衍生注解。
- dao 【@Repository】
- service【@Service】
- controller【@Controller】
类似的
/**
* 这个注解用在Controller层上面。类似与@Component,不过在Controller习惯用这个注解
*/
@Controller
public class UserController {
}
6.2 @Value(“值”)
这个注解用在类的属性或者set方法上,表示该属性被自动注入某个值,作用优点类似@Autowired,只不过@Autowired是引用类型的注入,@Value是基本类型的注入。类似:
@Value("Clay")
private String name;
6.3 @Scope
用在类上面。作用域注解,里面的值可以参考官方文档,常用值为单例singleton,原型prototype,类似用法
@scope("prototype")//原型
public void User{
...
}
7. 使用java配置Spring
在Spring4之后,推出了一种不用配置文件,而是使用java的方式来配置Spring。类似用法:
package com.utils;
import com.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Configuration:声明该类为Spring的配置文件,而且这个类也被Component注解标注了,会被注册到IOC容器里面
* Import:标签,可以引用别的配置类,类似import标签,将这写配置类弄成一个
*/
@Configuration
public class AppConfig {
/**
* 类似<bean id = "getUser" class = "com.pojo.User">
* <property name = "age" value = "16"/>
* </bean>
*/
@Bean
public User getUser() {
User user = new User();
user.setAge("16");
return user;
}
}
8. 代理模式
SpringAOP的底层就是代理模式,代理模式是必须要精通的。
代理模式分为:
- 静态代理
- 动态代理
- 基于接口====jdk动态代理
- 基于抽象类====cglib动态代理
关于代理模式,我们可以举一个简单的例子。在我们现实生活中,去租房子,一般是无法找到房东的,而房东也很难联系到需要租房子的人,所以出现了这么一个角色,房屋中介(代理)。房东将房子交给中介,我们直接从中介手里租房子,在我们眼里,中介就等同于房东了,我们的需求直接通过中介就可以实现。而中介跟房东都有一个共同的行为,就是出租房子(可以用一个接口约束)。我们来进行一下角色分析
- 抽象角色:一般会使用接口或者抽象类来解决,主要用于约束一些行为
- 真是角色:被代理的角色(房东)
- 代理角色:代理真是角色(中介),代理真是角色后,我们一般会进行一些附属操作。***【不进行附属操作,那么代理模式将毫无意义,说白了,代理模式就是,我们想要给真实角色增加一些功能业务,但是又不好去直接修改原有代码,在公司开发中,改错了,又改不回来,那么gg了。代理模式,就是想办法,去获取真实角色的所有功能,然后增加我们想要的功能,一般在java中获取一个类的所有允许访问的功能(业务方法),常用方式就是继承,以及声明一个公开的接口,让这个接口去约束真实角色与代理角色之间的功能业务】***
- 客户:我,访问代理角色的人。
8.1 静态代理
优点:
- 使真实角色的操作更加纯粹,不去关注一些公共业务
- 公共业务交给代理角色,实现业务的分工
- 公共业务发生扩展的时候,方便管理
缺点:
- 一个真实角色便会产生一个代理角色,代码量翻倍,开发效率变低。
8.2 动态代理
动态代理说白了,就是为了解决静态代理的缺点:一个真实角色便会产生一个代理角色,代码量翻倍,开发效率变低。而弄出来的一种东西,我们写好一个生成代理对象的模板程序,想要什么真实角色对应的代理角色,直接将真实角色丢给这个模板(模板中可以添加相应的业务代码),然后这么模板自动丢给我们一个代理角色。代理模式的底层是反射。
关于JDK动态代理,则不得不了解下面两个东西,这两个东西都是java反射包之下的。
8.2.1 Proxy
这个类是提供了一个获取代理角色的方法newProxyInstance,这个方法会动态生成一个代理类$Proxy,这个动态生成的类实现了真实角色的所有接口,并继承了Proxy。
public static Object newProxyInstance(
ClassLoader loader,//真实角色的类加载器 来定义这个返回的代理类的类型,。
类<?>[] interfaces,//真实对象实现的了的接口
InvocationHandler h//实现了InvocationHandler接口的类,
)
8.2.2 InvocationHandler
invoke(
Object proxy, //动态生成的代理角色
Method method, //真实角色所执行的方法
Object[] args //执行方法的参数
) throws Throwable
这是一个接口,而且这个接口只有一个方法,当调用newProxyInstance获取到动态生成的代理对象之后($Proxy)之后,通过这个这个代理对象执行真实对象的方法,会自动将这个方法分配到invoke方法上执行。要了解这个类是怎么将执行的方法分配到invoke方法上执行的,我们除了需要了解反射机制之外,还需要了解一下这个动态生成的类是什么样子,这里就不赘述反射的机制了,直接来谈谈&Proxy。
****``$Proxy继承自Proxy,而且这类只有一个构造函数,就是给newProxyInstance的第三个参数赋值(实现了InvocationHandler接口的实例,而且是调用了它的父类,Proxy中的构造方法)赋值,由于这个这类自动实现了真实对象的所有接口,所以它自然而然就实现了这些接口的所有方法,而且它的属性形式一般为*****
*``
private static Method m1;//对应equals方法
private static Method m2//对应toString()
private static Method m3//对应真实对象的接口中的某一个方法
//....一大堆的方法所对应的属性
private static Method m0;//对应m0
****可以看出来,它的属性都是一些Method的对象,一般真实对象的接口中有几个方法,它就会有几个这种属性,除此之外还多了三个Method对象(hashCode(),toString(),equals())。假设在真实对象的接口中有一个方法为getAllUser();。那么这个动态生成的代理角色,则会如此实现这个方法:*****
*``
public final String getAllUser(){
try{
/**这里我们不知道getAllUser()方法对应的Method属性是m几,我们假设是m3。这里就是自动将我们所执行的方法
分配到invoke上执行的关键,我们直接将这个方法对象丢给了invoke去执行,除了执行真实对象的方法之外,还可以执行我们在invoke方法中添加的业务。在实现invoke的地方(我们继承InvocationHandler接口并实现这个方法的地方)则可以实现*/
return (String)this.h.invoke(this, m3, null);
}
catch (Error|RuntimeException localError){
throw localError;
}
catch (Throwable localThrowable){
throw new UndeclaredThrowableException(localThrowable);
}
}
这里演示一下InvocationHandler接口实现的思路:
public class ProxyInvocationHandler implements InvocationHandler {
/**
* 处理真实对象,并返回结果
* @param proxy 动态生成的代理对象
* @param method 这是一个反射对象,主要是去执行method.invoke()
* @param args 参数集,这些参数丢入invoke里面去执行
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* 还可以做一些其他业务比如日志
*/
log(method.getName());
Object invoke = method.invoke(object, args);
return invoke;
}
public void log(String msg) {
System.out.println("执行了" + msg + "方法");
}
}
9. AOP
AOP(Aspect Oriented Programing):面向切面编程。
9.1 关于AOP
在我们的开发过程中,基本上都是采用的上面的纵向开发的模式,从dao一直到前端。假如,由于业务逐渐加大,程序出了bug,我们一般都是不会直接去修改原有的业务代码的,风险比较好,万一改错了,还改不会回来就gg了,比如现在bug出现在了service,我们需要用日志去打印排错,直接代理这个service就行了,**横切
**进去,即可达到查看日志排错的目的。这就是AOP的基本概念。
说白了AOP还是动态代理的一种运用,AOP是OOP的延续,而不是代替。
即获取原有业务类的功能,添加我们所需要的功能,减少修改的原有业务类的风险。
9.2 关于SpringAOP的一些名字概念
国外人就是喜欢搞这么一些绕口的概念。
- 横切关注点:即与业务逻辑无关的,但是我们需要关注的部分,比如我们要给原有的业务类增加了一个日志功能,这个日志功能就是横切关注点,还有一些,比如事务,缓存,安全等等。
- 切面(Aspect):横切关注点被模板化的一个对象。即,一个类,比如我们将所关注的日志功能,抽取出来成一个类,这个log类就是切面。
- 通知(Advice):切面要完成的工作。即,切面类中的方法,比如,抽取出来的Log类中的log方法
- 目标(Target):被通知的对象。即业务类
- 代理(Proxy):向目标对象应用通知之后创建的对象,即生成的动态代理角色。
- 切入点(PointCut):切面通知执行的地方。即你在业务类中某个方法加上日志功能,这个方法就是切入点
- 连接点(JointPoint):与切入点匹配的执行点。即你要业务类这个方法的哪里执行,前面,后面,还是前后。
9.3 Spring中5种通知类型
- 前置通知:方法前。
- 后置通知:方法后。
- 环绕通知:方法前后。
- 异常抛出通知:方法抛出异常
- 引介通知:类中增加新的方法属性
9.4 实现AOP所需要导入的依赖
除了上面已经导入的AOP,等依赖之外,还需要导入一个新的依赖
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
9.5 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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启注解-->
<context:annotation-config/>
<!--开启组件扫描。扫描指定包下面的所有类,只要有被@Component以及其衍生注解标注的类,都会被注册到IOC容器里面
注解注册的bean都是小写开头。
-->
<context:component-scan base-package="com"/>
<!--配置AOP,需要导入AOP的约束-->
<!-- 方式一 -->
<aop:config>
<!--切入点 expression:表达式。execution:需要执行的位置!
第一个*:返回类型,com.service.impl.UserServiceImpl.。 *(..):方法跟参数-->
<aop:pointcut id="pointCut" expression="execution(* com.service.impl.UserServiceImpl.*(..))"/>
<!--执行增强-->
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointCut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointCut"/>
</aop:config>
<!-- 方式二 -->
<aop:config>
<!-- 自定义的切面。ref 要引用的类-->
<aop:aspect ref="diyPointCut">
<!-- 切入点-->
<aop:pointcut id="point" expression="execution(* com.service.impl.UserServiceImpl.*(..))"/>
<!-- 通知 method:切面类中的方法 pointcut-ref:切入点 -->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
<!-- 方式三:开启AOP的注解支持 。默认采用JDK动态代理。proxy-target-class="true":采用cglib -->
<aop:aspectj-autoproxy />
</beans>
9.5 AOP实现方式一,使用Spring的AOP接口
- 后置通知:实现AfterReturningAdvice,配置方式参考9.4 advice-ref="beforeLog"这个是实现AfterReturningAdvice的类名,这个类需要注入到SpringIOC容器里面
- 前置通知:实现MethodBeforeAdvice。
- 其他通知方法类似
9.6 自定义一个切面类。
实现方式参考9.4
9.7 注解实现AOP
注意注解实现需要开启aop的注解支持
<!-- 方式三:开启AOP的注解支持 。默认采用JDK动态代理。proxy-target-class="true":采用cglib -->
<aop:aspectj-autoproxy />
类似用法:
package com.div;
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;
import org.springframework.stereotype.Component;
//方式三:使用注解的方式来实现AOP功能
@Component
@Aspect//标注这个类是一个切面
public class AnnoPointCut {
@Before(value= "execution(* com.service.impl.UserServiceImpl.*(..))")
public void before() {
System.out.println("方法执行前");
}
@After(value= "execution(* com.service.impl.UserServiceImpl.*(..))")
public void after() {
System.out.println("方法执行后");
}
/**
* 在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点
*/
@Around(value= "execution(* com.service.impl.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕前");
//执行方法
Object proceed = proceedingJoinPoint.proceed();
System.out.println("环绕后");
}
}
10. Spring整合Mybatis
10.1 需要导入的依赖
除了Spring跟Mybatis相关jar包之外,还需要导入
<!-- spring整合mybatis的包 -->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.1</version>
</dependency>
<!-- spring连接jdbc的包 -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
10.2 整合方式一
<?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">
<!-- 类似这种配置基本是死的,要用直接拷贝,然后修改一下数据源,注册的mapper等即可-->
<!--DataSource:数据源,使用Spring数据源替换Mybaits的配置。
我们这里使用Spring的JDBC
这个bean就相当于Mybaits配置文件中的environments中的配置-->
<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?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--配置SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 因为获取sqlSessionFactory对象,需要sqlSessionFactoryBuilder获取。注入数据源-->
<property name="dataSource" ref="dataSource"/>
<!--绑定mybatis配置文件,-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!--配置sqlSession 这里即是我们使用的myabtis-spring包中的SqlSessionTemplate
方式二中,这里可以省略,直接通过继承一个SqlSessionDaoSupport类
通过这个类的getSession()方法就能获得一个SqlSessionTemplate实例(SqlSession),
但是SqlSessionDaoSupport需要注入一个sqlSessionFactory,直接注入到它的子类即可-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--只能使用构造器注入,因为它没有set方法,
因为获得SqlSession需要通过sqlSessionFactory.openSqlSession(),
所以我们在这里注入一个sqlSessionFactory对象-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
</beans>
在上面的配中,
我们首先配置了一个数据源的Bean。org.springframework.jdbc.datasource.DriverManagerDataSource。
这里面取代了Mybatis的配置文件中的environments中的设置,即配置了数据库连接地址,密码,驱动等。然后将这个Bean注入到了一个org.mybatis.spring.SqlSessionFactoryBean的Bean中,这个Bean中我们还通过classpath:的方式绑定了Mybatis的原生配置文件(这个配置文件将省去environments的配置,除此之外,想省去的配置的都可以在这个Bean中配置,比如映射器的绑定,别名等等,但一般可以不用这么做,然后Mybatis的配置文件看起来更像一个配置文件),然后将org.mybatis.spring.SqlSessionFactoryBean注入到一个org.mybatis.spring.SqlSessionTemplate,这个东西就相当于Mybatis中的sqlSession,同过这个东西可以获取相应的映射器,执行我们的sql。
注意,由于是Spring整合Mybatis,为了然后IOC容器更加方便管理我们的DAO接口,一般需要为DAO接口添加一个实现类,在这个实现类中注入org.mybatis.spring.SqlSessionTemplate,通过这个获取映射器执行sql。
10.3 整合方式二
在DAO接口的实现类上,我们可以继承一个SqlSessionDaoSupport,这个类提供了一个getSqlSession()方法,可以获取sqlSession。因为,这个SqlSessionDaoSupport要获取SqlSession,所以我们需要为这个类注入一个SqlSessionFactory或者SqlSession实例(不然它从鬼地方去获取sqlSession),常常采用的方式,是直接注入到这个Dao接口的实现类中。类似用法:
package com.dao;
import com.pojo.User;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* 将这个类配置到SpringIOC,
*/
public class UserDaoImpl2 extends SqlSessionDaoSupport implements UserDao {
public List<User> getAllUser() {
SqlSession sqlSession = getSqlSession();
return sqlSession.getMapper(UserDao.class).getAllUser();
}
}
11. Spring声明式事务
11.1 什么是事务
- 把一组业务当成一个业务来执行,要么都成功,要么都失败,如果执行到一半遇到失败,那么事务将会回滚
- 事务及其重要,不容马虎,设计到数据一致性问题。
- 确保数据的完整性跟一致性
- 事务的的ACID原则
- 原子性
- 一致性
- 隔离性:多个业务可能操作同一个资源,防止数据损坏。
- 持久性:事务一旦提交,无论系统发生什么问题,结果都不会被影响,被持久化的写到存储器中。
11.2 类似配置方法
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd">
<!--
这
里
整
合
Mybatis
-->
<!-- 配置声明式事务,然后注入到sqlSessionFactory-->
<bean id="transaction" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 结合AOP,实现事务的织入 需要导入tx 和aop的约束-->
<!--配置事务的类-->
<tx:advice id="txAdvice" transaction-manager="transaction">
<!--给那些方法配置事务
配置事务的传播特性 propagation,默认REQUIRED ,也是最常见的选择
支持当前事务,如果当前没有,则新建一个,-->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="update"/>
<tx:method name="delete"/>
<tx:method name="query" read-only="true"/>
<!-- 所有方法 -->
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 配置事务切入-->
<aop:config>
<!-- 切入点为Dao下面的所有类的所有方法, 即dao包下面的所有方法都会被织入上面的事务 -->
<aop:pointcut id="txPointCut" expression="execution(* com.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
</beans>
声明式事务利用了SpringAOP,将事务横切到我们所需要添加的地方,需要导入tx和aop的约束
- 先采用官方的方式,声明一个org.springframework.jdbc.datasource.DataSourceTransactionManager的Bean,
- 然后在这里Bean里面注入一个dataSource
- 然后使用tx:advice去配置事务的传播方式, transaction-manager=“transaction”。事务的管理方式就是上面声明好的Bean
- 然后将配置好的事务横切到需要添加事务的地方。比如Dao下面的所有方法。默认事务是REQUIRED