Spring学习
关于Spring
简介
介绍:Spring框架是一个开放源代码的J2EE应用程序框架,由[Rod Johnson](https://baike.baidu.com/item/Rod Johnson/1423612)发起,是针对bean的生命周期进行管理的轻量级容器(lightweight container)。
特点:
-
两个最大的特性:AOP(面向切面编程) 和 IOC(控制反转)—面试必问
-
Spring是一个轻量级的,非入侵式的框架(引入框架的时候不会对以前的代码产生影响)
-
支持事务处理,对框架整合的支持,相当于一个大杂烩容器
-
开源的,免费的
组成
- Spring Boot
- 一个快速开发的脚手架
- 基于SpringBoot可以快速的开发单个微服务
- 约定大于配置
- Spring Cloud
- SpringCloud是基于Stringboot开发的
IOC理论推导
推导
原先我们写MVC三层模式的时候是需要经历以下这些过程的:
-
编写UserDao接口
public interface UserDao { void getUser(); }
-
编写UserDaoImpl实现类
public class UserDaoImpl implements UserDao { public void getUser() { System.out.println("我是数据访问层!"); } }
-
编写UserService接口
public interface UserService { void getUser(); }
-
编写UserServiceImpl实现类
public class UserServiceImpl implements UserService { private UserDao userDao = new UserDaoImpl(); public void getUser() { userDao.getUser(); } }
-
最后才能够调用到数据访问层
public void Test01(){ UserServiceImpl userService = new UserServiceImpl(); userService.getUser(); }
这样做的好处就是不用管数据访问层是什么,我们直接再表示层调用就可以了,我们根本不与数据库层打交道。
但是,这个模式也有很大的缺点,那就是我们如果后期添加很多需求的话我们必须去内部修改源码,这样的代价十分昂贵!牵一发而动全身!
改良以上方法,在服务层加一个set方法,这样一来用户的需求就不会改变源代码,这种思想与上面的思想有着天大的区别!
public class UserServiceImpl implements UserService {
//本来是写死的,现在可以灵活调用了
private UserDao userDao;
//新加的set方法
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void getUser() {
userDao.getUser();
}
}
测试:
public void Test01(){
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDao(new UserDaoMysqlImpl());
userService.getUser();
}
- 之前,程序是主动创建对象的,因为在服务层里面是写死的,只能去new一个数据访问层,主动权在程序员的手里!而且耦合性很高,牵一发而动全身!
- 而现在我们用了一个set方法注入后,程序new对象不再是死的了,可以很灵活,传入什么对象就new什么对象,被动的接收对象!
- 这就是控制反转的思想!
这种思想从本质上解决了问题,程序猿不用再去管理对象的创建了。系统的耦合性大大地降低,可以更加专注的关注业务了。这是IOC的原型!
整体架构没有变,只是核心业务变了。
本质
如上图,一开始我们的程序之间都相互联系,耦合度很高,耦合度高的话不利于程序的拓展,这是一种强连接,于是乎演变出来了IOC容器,也就是第二个图,通过一个中间件来调用其他的模块,通过接口调用就不会是强连接了
控制反转是一种通过描述(XNL或注解)并通过第三方生产或获取特定对象的方式,在Spring中实现控制反转的是IOC容器,其实现方法是依赖注入(Dependency Injection,DI)(set…)。
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用spring之后对象是由spring来创建的。
反转:程序本身不创建对象,而变成被动的接收对象。
核心是set方法,在配置文件中是利用实体类中的set方法对属性进行赋值
IOC是一种编程思想,由主动的编程变成被动的接收。
第一个spring程序
首先我们有一个pojo类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Hello {
private String name;
}
然后就是至关重要的配置文件了,在spring中new一个对象就是通过配置文件来的
<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用spring来创建对象 在spring中 对象称之为bean-->
<!--
id相当于变量名
class相当于new的对象的目的地址
property就相当于赋值
变量名 id = new 变量名(class)
-->
<bean id="hello" class="pojo.Hello">
<property name="name" value="Spring!"/>
</bean>
</beans>
最后测试
public static void main(String[] args ){
//获取spring的上下文对象 固定写法
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//我们的对象现在都在spring中管理了 我们只需要取出来就可以了
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.toString());
}
只要在pojo类有这个标志,→
就代表已经连接成功了,已经被spring托管了。
到了现在,我们彻底不用再去程序里改动源码了,要实现不同的操作,只需要在xml文件中进行修改,所谓IOC总结为一句话就是:对象由spring来创建,管理,装配!
用spring改良mvc三层模式
由于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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="daoImpl.UserDaoImpl"/>
<bean id="userDaoMysql" class="daoImpl.UserDaoMysqlImpl" />
<bean id="userService" class="serviceImpl.UserServiceImpl">
<property name="userDao" ref="userDaoMysql"/>
</bean>
</beans>
测试:
@Test
public void test02(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserServiceImpl userService = (UserServiceImpl) context.getBean("userService");
userService.getUser();
}
这样一来我们没有用关键字new去新建任何一个对象,只是用配置文件去调用(spring去实现创建)然后我们取出来就可以了,我们需要更改需求的时候也不需要更改源码,在改良的整个过程当中我们没有动过任何源码!!
接下来我们对这个配置文件进行讲解:
- 每一个bean就是一个对象
- 以前是在service的实现类中传不同的对象,现在直接在配置文件中传不同的对象,在第三个bean中我们可以传前两个对象中的任意一个,这个过程就相当于我们在service实现类传参
- 在spring中是set方法很重要,在配置文件中我们也是通过调用set方法来设置它的值为某个对象的
IOC创建对象方式
-
使用无参构造方法,没有无参的构造函数就会报错(默认方法)
-
使用有参构造方法
-
下标
<bean id="user" class="pojo.User"> <!-- <property name="name" value="大姚"/>--> <constructor-arg index="0" value="火花"/> </bean>
-
参数的类型
通过这个我们可以得出一个结论,就是在配置文件中的bean都会被执行就像是new了多个对象,并不是你调用谁谁才被new,他已经new好了事先。 -
通过参数名来设置
<bean id="user2" class="pojo.User"> <constructor-arg name="name" value="hutu"/> </bean>
spring配置
-
- 别名 可以取多个别名 每个别名都可以用
<alias name="user2" alias="user3"/>
* - import
- 将多个bean合并成一个总的bean
- 场景:一个项目多个人开发,他们建的bean都不同,我们可以利用import将所有人的beans合并为一个总的,最后使用的时候直接用总的就可以了。
依赖注入
构造器注入,如上
set注入(重点)
- 依赖注入:set注入!核心
- 依赖 : bean对象的创建依赖于容器!
- 注入 : bean对象中的所有的属性,由容器来注入
环境搭建:
- 复杂类型
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
- 真实测试对象
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;
}
- beans.xml
<?xml version="1.0" encoding="GBK"?>
<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="student" class="pojo.Student">
<property name="name" value="滴滴"/>
</bean>
</beans>
- 测试类
public class MyTest {
public static void main(String[] args ){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = (Student) context.getBean("student");
System.out.println(student.getName());
}
}
基本di注入方式:
<?xml version="1.0" encoding="GBK"?>
<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="pojo.Address">
<property name="address" value="南京"/>
</bean>
<bean id="student" class="pojo.Student">
<!--第一种 普通值注入 value-->
<property name="name" value="滴滴"/>
<!--bean注入 ref-->
<property name="address" ref="address"/>
<!--数组-->
<property name="books">
<array>
<value>三国演义</value>
<value>红楼梦</value>
<value>水浒传</value>
<value>西游记</value>
</array>
</property>
<!--List-->
<property name="hobbys">
<list>
<value>听歌</value>
<value>看电影</value>
<value>啪啪啪</value>
</list>
</property>
<!--Map-->
<property name="card">
<map>
<entry key="身份证" value="11123125415252"></entry>
<entry key="银行卡" value="09876553565446"></entry>
</map>
</property>
<!--Set-->
<property name="games">
<set>
<value>LOL</value>
<value>CF</value>
</set>
</property>
<!--null-->
<property name="wife">
<null/>
</property>
<!--Properties-->
<property name="info">
<props>
<prop key="drive">mysql.root.null</prop>
<prop key="username">大疆</prop>
<prop key="password">1243</prop>
</props>
</property>
</bean>
</beans>
拓展方式注入
命名空间
官方使用
p and c
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
不能直接使用需要导入上面这两条约束
如果要用c命名的话必须要有无参和有参的构造函数
测试
<?xml version="1.0" encoding="GBK"?>
<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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="address" class="pojo.Address" p:address="南京"/>
<bean id="user" class="pojo.User" p:name="大海" p:age="13" p:address-ref="address"/>
<bean id="user2" class="pojo.User" c:name="王华" c:age="23" c:address-ref="address"/>
</beans>
注意点:在我们用上下文(context)调用beans.xml中的对象的时候可以用这种方式来规避每次的强转User user = context.getBean("user2", User.class);
bean作用域
-
单例模式(Spring默认机制),永远都是一个对象 单线程用
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
-
原型模式:每次从容器get的时候都会产生一个新的对象 多线程用
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
-
其余的在web中会用到
Bean的自动装配
- 自动装配是spring满足bean依赖的一种方式!
- spring会在上下文中自动寻找,并自动给bean装配属性!
装配的三种方式
在spring中有三种装配方式:
- 在xml中显示的配置
- 在java中显示的配置
- 隐式的自动装配bean <重要>
以前我们手动在beans.xml里面配置文件就是手动的。
使用xml自动装配
自动装配:
<bean id="cat" class="pojo.Cat"/>
<bean id="dog" class="pojo.Dog"/>
<bean id="person" class="pojo.Person" autowire="byName">
<property name="name" value="幂幂"/>
</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
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>
使用注解就可以不用set方法了
可以在属性上使用,也可以在set方法上使用,前提是你自动装配的属性在IOC(spring)容器中存在,且名字规范。
小结:
@Resource 和 @Autowired的区别:
- 都可以自动装配 都可以放在属性字段上
- @Autowired是通过ByType的方式实现的,而且这个对象必须存在
- @Resource 默认是通过ByName实现的,如果找不到名字,则自动通过类型实现,如果名称和类型都不标准,那就报错!
使用注解开发
-
bean
-
spring 4之后要使用注解AOP的包必须导入
-
需要现在配置文件中声明注解生效的包
-
<!--指定要扫描的包 这个包下的注解才会生效--> <context:component-scan base-package="pojo"/>
-
-
@Component
组件 直接在类上面用就可以 说明这个类被spring管理了 这就是上面的bean标签
-
-
属性如何注入
@Value("大糊涂")
直接在属性上面写就可以
@Component public class User { @Value("大糊涂") public String name; @Value("糊糊涂涂") //最后输出的是这个 public void setName(String name) { this.name = name; } }
-
衍生的功能
@Componet有几个衍生的注解 在web开发中 会按照mvc三层架构分层
- dao 【@Repository】
- service 【@Service】
- control 【@org.springframework.stereotype.Controller】
这四个注解的功能都是一样的,都是代表某个类注册到spring中,装配bean
-
自动装配
-
作用域
直接在类上面配置就可以
-
小结
xml 和 注解:
- xml更加全能 适用于各种场合 维护简单方便
- 注解维护相对麻烦
xml用来管理bean
注解只负责属性的注入
我们必须要注意的一个问题就是在xml文件中开启注解,路径到某个包下。
使用Java的方式配置spring
我们现在完全不需要使用xml了,完全交给java去做
JavaConfig是一个核心功能!
这种纯Java的配置方法在springBoot中随处可见!
代理模式
为什么要学习代理模式?
because这是spring AOP的底层
面试必问【springAOP 和 springMVC】
代理模式的分类:
静态代理
好处:
- 通过代理可以增加很多事情,使操作更加丰富,可以做很多真实对象做不了的事情
- 使真实对象(房东)的操作更加纯粹,不用去关注一些公共的事务,实现了业务的分工
- 业务扩展的时候方便集中管理
- 不改变原有的代码 来增加新的需求
缺点:
- 一个真实对象对应一个代理角色,这样的话会造成代码量翻倍,开发效率降低
实例
-
接口
-
public interface Rent { public void rent(); }
-
-
真实角色
-
public class Host implements Rent{ public void rent() { System.out.println("房东要出租房子!"); } }
-
-
代理角色
-
public class Proxy implements Rent{ public Host host; public Proxy(Host host) { this.host = host; } public Proxy() { } public void rent() { host.rent(); fee(); seeHouse(); } public void fee(){ System.out.println("收中介费"); } public void seeHouse(){ System.out.println("中介看房子"); } }
-
-
客户访问代理角色
-
public class Cilent { public static void main(String[] args ){ Proxy proxy = new Proxy(new Host()); proxy.rent(); } }
-
聊聊AOP
动态代理
- 动态代理和静态代理角色一样
- 动态代理的代理类是自动生成的,不是我们直接写好的
- 动态代理分为两大类:基于接口的动态代理 和 基于类的动态代理
- 基于接口—JDK 动态代理
- 基于类 cglib
- java字节码实现 : javasist
需要了解两个类 Proxy **代理 ** 和 InvocationHandler 调用处理程序
动态代理最大的好处就是:一个动态代理类可以代理多个类!!
实例:
接口
public interface Rent {
public void rent();
}
真实对象(房东)
public class Host implements Rent {
public void rent() {
System.out.println("房东要出租房子!");
}
}
代理对象(动态代理,不是写死的,可以代理很多类)
//自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
//处理代理实例上的方法调用并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质就是反射机制
System.out.println("执行了"+method.getName()+"方法");
seeHouse();
Object result = method.invoke(target, args);
return result;
}
public void seeHouse(){
System.out.println("中介看房子");
}
}
测试
public class Client {
public static void main(String[] args ){
Host host = new Host();
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setTarget(host);
Rent proxy = (Rent) pih.getProxy();
proxy.rent();
}
}
与静态代理最大的区别就是动态代理是基于反射实现的,而且在代理层面一个代理可以很灵活的代理多个类。
AOP
第一步导包:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
我们也是先建一个userservice接口和一个userviceimpl实现类,实现了增删改查的方法,之前我们是用动态代理来做的,现在我们用spring来做。
方法一:使用spring的API接口
复制以前写的UserService
类和UserServiceImpl
类
编写log类和afterlog类
public class Log implements MethodBeforeAdvice {
/**
* @param method 要执行的目标对象的方法
* @param args 参数
* @param target 目标对象
* @throws Throwable
*/
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
public class AfterLog implements AfterReturningAdvice {
/**
* @param returnValue 返回值
* @param method 调用的方法
* @param args 参数列表
* @param target 目标对象
* @throws Throwable
*/
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了"+method.getName()+"方法,返回结果为:"+returnValue);
}
}
创建applicationContext.xml
配置文件
<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册bean-->
<bean id="userservice" class="com.shi.serviceImpl.UserServiceImpl"/>
<bean id="log" class="com.shi.log.Log"/>
<bean id="afterlog" class="com.shi.log.AfterLog"/>
<!--使用原生的方式一-->
<!--配置aop 导入aop的约束-->
<aop:config>
<!--切入点 在那个地方去执行 expression 表达式 execution(要执行的位置! * * * *)-->
<aop:pointcut id="pointcut" expression="execution(* com.shi.serviceImpl..*(..))"/>
<!--执行环绕增加!-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterlog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
测试
public class MyTest {
public static void main(String[] args ){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userservice = context.getBean("userservice", UserService.class);
userservice.select();
}
}
结果
方法二:实现自定义类实现
主要是切面定义
<bean id="diy" class="com.shi.diy.DiyPointCut"/>
<!--方法二:自定义类-->
<aop:config>
<!--自定义切面 ref要引用的类-->
<aop:aspect ref="diy">
<!--切入点-->
<aop:pointcut id="point" expression="execution(* com.shi.serviceImpl.UserServiceImpl..*(..))"/>
<!--通知-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
自定义切面类:
public class DiyPointCut {
public void before(){
System.out.println("===========方法执行前============");
}
public void after(){
System.out.println("===========方法执行后============");
}
}
方法三:使用注解实现
@Aspect //标记这是一个切面
public class AnnotationPointCut {
@Before("execution(* com.shi.serviceImpl.UserServiceImpl..*(..))")
public void before(){
System.out.println("============方法执行前==============");
}
@After("execution(* com.shi.serviceImpl.UserServiceImpl..*(..))")
public void after(){
System.out.println("============方法执行后==============");
}
}
整合MyBatis
-
导入相关jar包
- junit
- mybatis
- mysql数据库
- spring相关的
- aop
- mybatis-spring(专门整合mybatis和spring的包)
-
编写配置文件
-
测试
回忆mybatis
- 编写实体类
- 核心配置文件
- 接口
- 实现类
- 测试
Mybatis-Spring
- 编写数据源配置
- sqlsessionfactory
- sqlsessionTemplate
- 需要给接口加实现类
- 想实现类注入到spring中
- 测试
代码示例:
配置文件:
spring-dao.xml配置文件
<?xml version="1.0" encoding="GBK"?>
<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">
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--绑定mybatis配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--这句话就相当于让原来的mybatis配置找到接口的mapper.xml-->
<property name="mapperLocations" value="classpath:com/shi/mapper/*.xml"/>
</bean>
<!--DataSource: 使用spring的数据源替换mybatis的配置 即替换用户名密码url等信息
我们这里使用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://127.0.1:3306/test?serverTimezone=UTC&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</bean>
<!--SqlSessionTemplate 就是我们使用的sqlsession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--只能使用构造器注入sqlsession 因为没有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
</beans>
mybatis-config.xml配置文件
(mybatis核心配置文件)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="com.shi.entity"/>
</typeAliases>
</configuration>
applicationContext.xml整合配置文件
<?xml version="1.0" encoding="GBK"?>
<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">
<import resource="spring-dao.xml"/>
<bean id="coursemapperimpl" class="com.shi.mapper.CourseMapperImpl">
<property name="sqlSessionTemplate" ref="sqlSession"/>
</bean>
</beans>
Course.xml配置文件
(相当于以前的接口的dao)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.shi.mapper.CourseMapper">
<select id="list" resultType="course">
select * from test.course
</select>
</mapper>
我们先在通过这三个配置文件就已经创建好了sqlsession和sqlsessionfactory了,在测试类中直接getBean就可以了。
因为是spring配置的所以要有一个接口的实现类
public class CourseMapperImpl implements CourseMapper {
//我们的所有的操作都是用上去了session执行 在原来
//现在我们都使用sqlsessionTemplate
private SqlSession sqlSessionTemplate;
public void setSqlSessionTemplate(SqlSession sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}
public List<Course> list() {
CourseMapper mapper = sqlSessionTemplate.getMapper(CourseMapper.class);
return mapper.list();
}
}
有了这个接口实现类我们就可以代理原接口,并且在这里面进行CourseMapper mapper = sqlSessionTemplate.getMapper(CourseMapper.class);
,有了这个对象我们就可以返回我们的结果了,这样的话在客户端就不需要写很多的代码了。
测试
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
CourseMapper cmi = context.getBean("coursemapperimpl", CourseMapper.class);
for (Course course : cmi.list()) {
System.out.println(course);
}
}
注意:mybatis-config.xml是可以删掉的!!
这个可以简化,主要是简化sqlsession的创建,在实现类中我们可以一条语句就返回
public class CourseMapperImpl extends SqlSessionDaoSupport implements CourseMapper {
//我们的所有的操作都是用上去了session执行 在原来
//现在我们都使用sqlsessionTemplate
/* private SqlSession sqlSessionTemplate;
public void setSqlSessionTemplate(SqlSession sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}*/
public List<Course> list() {
return getSqlSession().getMapper(CourseMapper.class).list();
}
}
相对应的上面这块内容也可以删掉。
声明式事务
回顾事务
- 要么都成功,要么都失败
- 确保完整性和一致性
- 事务在项目开发中很重要 涉及到数据一致性的问题 不能马虎
事务ACID原则:
- 原子性 : 确保要么都成功 要么都失败
- 一致性 : 要么都提交 要么都失败
- 隔离性 : 多个业务可能操作同一个资源,防止数据损坏
- 持久性 : 事务一旦提交,无论系统发生什么问题,结果都不会再被影响 被持久化写到存储器中
spring事务
- 声明式事务 : AOP
- 编程式事务 : 需要在代码中进行事务管理
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource" />
</bean>
思考:
为什么需要事务?
- 如果不配置事务 可能存在数据提交不一致的情况,例如当两个插入语句,一个有错误,一个没有错误,按照事务的ACID原则,肯定都不能插入,但是不配置事务的话,那一条对的插入语句就会插入成功!违背了ACID原则。
- 强调,事务很重要,关系到数据的完整性和一致性
<!--结合AOP实现事务的织入-->
<!--配置事务的类:-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--给那些方法配置事务-->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="list" read-only="true"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配置事务切入-->
<aop:config>
<aop:pointcut id="txPoint" expression="execution(* mapper.*..*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/>
</aop:config>