文章目录
一. 总体介绍
1.1 概念介绍
Spring 被称为 J2EE 的春天,是一个是分层的 Java SE/EE full-stack 开源的轻量级的 Java 开发框架, 是
最受欢迎的企业级 Java 应用程序开发框架,数以百万的来自世界各地的开发人员使用 Spring 框架来创
建性能好、易于测试、可重用的代码。
1.2 两大核心
控制反转(IOC)、面向切面(AOP)
1.3 优势
- 方便解耦,简化开发(对应IOC)
- Spring 就是一个大工厂,可以将所有对象的创建和依赖关系的维护交给 Spring 管理。
- 方便集成各种优秀框架
- Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架的直接支持。
- 降低 Java EE API的使用难度
- Spring 对 Java EE 开发中非常难用的一些 API(JDBC、JavaMail、远程调用等)都提供了封装, 使这些 API 应用的难度大大降低。
- 方便程序的测试
- Spring 支持 JUnit4,可以通过注解方便地测试 Spring 程序
- AOP 编程的支持
- Spring 提供面向切面编程,可以方便地实现对程序进行权限拦截和运行监控等功能。
- 声明式 事务的支持
- 只需要通过配置就可以完成对事务的管理,无须手动编程。
1.4 体系结构
Spring 为我们提供了一站式解决方案,但Spring 是模块化的,允许我们挑选和选择适用于项目的模块,
不需要把剩余部分也引入。
Spring 框架提供约 20 个模块,可以根据应用程序的要求来选择。
二.IOC-控制反转
2.1 概念介绍
IOC:Inversion of Control,并非什么技术,而是一种设计思想。IOC是指在程序开发中,实例的创建不再有调用者管理,而是有Spring容器创建。简而言之,以前创建对象是通过构造器形参多态化来创建不同的对象,现在通过Spring配置文件就可实现该操作。 因此,控制权由程序代码转移到了Spring容器中,控制权发生了反转,这就是IOC思想。
2.2 入门案例
2.2.1 创建maven项目
2.2.2 pom.xml文件添加依赖和插件
2.2.3 创建一个实体类
public class Team {
private Integer id;
private String name;
private String location;
public Team() {
System.out.println("id="+id+" name="+name+" location="+location);
}
}
2.2.4 创建配置文件
2.2.5 使用Spring容器创建对象
<?xml version="1.0" encoding="UTF-8"?>
<!--spring的配置文件
1、beans: 根标签,spring中java的对象成为bean
2、spring-beans.xsd 是约束文件(约束XML文件中能编写哪些标签)-->
<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,通知spring要创建哪个类的对象
一个bean标签声明一个对象:
id="自定义的对象名称" ,要求唯一
class="类的完全限定名" 包名+类名,spring底层是反射机制创建对象,所以必须使用类名
相当于 Team team1=new Team();
创建好的对象放入一个集合Map中
例如:springMap.put("team1",new Team()); -->
<bean id="team1" class="com.kkb.bean.Team" ></bean>
</beans>
2.2.6 获取Spring容器
Spring提供了两种 IoC 容器,分别为 BeanFactory 和 ApplicationContext.
2.2.6.1 BeanFactory
BeanFactory 是基础类型的IoC 容器,最常见的实现类是XmlBeanFactory
2.2.6.2 ApplicationContext
ApplicationContext 是 BeanFactory 的子接口,也被称为应用上下文。它不仅提供了 BeanFactory 的
所有功能,还添加了对 i18n(国际化)、资源访问、事件传播等方面的良好支持。最常见的实现类是:ClassPathXmlApplicationContext、FileSystemXmlApplicationContext
2.2.7 获取容器中的对象
@Test
public void test1() {
//1. Spring容器创建对象的方式
String path = "application.xml";
//2. 获取spring容器
//法一:Beanfactory直接实现类-XmlBeanFactory
//获取容器(未创建对象)
BeanFactory beanFactory = new XmlBeanFactory(
new FileSystemResource("xxx/Spring01/src/main/resources/application.xml"));
//创建对象
beanFactory.getBean("team1");
//法二:(推荐!!!)
//Beanfactory子接口ApplicationContext实现类-ClassPathXmlApplicationContext
//获取容器(已创建对象)
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(path);
//获取对象
applicationContext.getBean("team1");
//法三:Beanfactory子接口ApplicationContext实现类-fileSystemXMLApplicationContext
获取容器(已创建对象)
ApplicationContext applicationContext1 = new FileSystemXmlApplicationContext("
xxx/Spring01/src/main/resources/application.xml");
//容器中的其他API
//获取当前容器对象个数
int count = applicationContext.getBeanDefinitionCount();
System.out.println("容器对象为:" + count);
//获取当前容器对象名字
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println("\t" + beanDefinitionName);
}
由上图可知,第二种方式获取容器及创建对象最为简便,不需要文件的全路径,且获取容器时已创建全部对象
2.2.8 创建非自定义对象
<bean id="simple1" class="java.text.SimpleDateFormat"></bean>
<bean id="date1" class="java.util.Date"></bean>
//获取非自定义对象
SimpleDateFormat simple1 = (SimpleDateFormat) applicationContext.getBean("simple1");
Date date1 = (Date) applicationContext.getBean("date1");
String format = simple1.format(date1);
System.out.println("当前日期为:" + format);
2.2.9 bean标签属性
2.2.9.1 scope
- sinleton:默认值,单例:在容器启动的时候就已经创建了对象,而且整个容器只有唯一的一个对象(类似java关键字的static)
- prototype:多例,在使用对象的时候才创建对象,每次使用都创建新的对象
- 下面通过不同的属性进行演示
<bean id="team1" class="com.kkb.bean.Team" scope="singleton" ></bean>
<bean id="team2" class="com.kkb.bean.Team" scope="prototype"></bean>
@Test
public void Test2() {
String path = "application.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(path);
Team team1 = (Team) applicationContext.getBean("team1");
Team team11 = (Team) applicationContext.getBean("team1");
Team team2 = (Team) applicationContext.getBean("team2");
Team team22 = (Team) applicationContext.getBean("team2");
System.out.println(team1);
System.out.println(team11);
System.out.println(team2);
System.out.println(team22);
}
2.2.9.2 lazy-init
主要用在单例对象中,因为多例对象需要时才创建
true:真懒,等价于多例对象,需要时才创建
false:默认值,假懒,加载容器时即创建好对象
2.2.9.3 init-method,destory-method
init-method:创建对象后执行的方法
destory-method:对象销毁方法,调用容器destory方法时候执行,调用此方法时,容器的创建不能通过父类引用指向子类对象,应由子类直接创建
public class Team {
private Integer id;
private String name;
private String location;
public Team() {
System.out.println("id="+id+" name="+name+" location="+location);
}
//名字任意
public void init() {
System.out.println("-----init------");
}
//名字任意
public void destory() {
System.out.println("-----destory-----");
}
}
init-method,destory-method后面的名字和实体类的方法对应即可
<bean id="team1" class="com.kkb.bean.Team" scope="singleton"
lazy-init="false" init-method="init" destroy-method="destory"></bean>
@Test
public void Test2() {
String path = "application.xml";
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(path);
Team team1 = (Team) applicationContext.getBean("team1");
System.out.println(team1);
applicationContext.close();
}
2.3 Spring容器创建对象的方式
2.3.1 默认的构造方法
2.3.2 带参数的构造方法
//实体类
public class Team {
private Integer id;
private String name;
private String location;
public Team(Integer id,String name,String location) {
this.id=id;
this.name=name;
this.location=location;
System.out.println("id="+id+",name="+name+",location="+location);
}
}
<!--name:表示参数的名称-->
<bean id="team2" class="com.kkb.bean.Team">
<constructor-arg name="id" value="1001"/>
<constructor-arg name="name" value="laker"/>
<constructor-arg name="location" value="洛杉矶"/>
</bean>
<!--index:表示参数的下标索引-->
<bean id="team3" class="com.kkb.bean.Team">
<constructor-arg index="0" value="1002"/>
<constructor-arg index="1" value="ox"/>
<constructor-arg index="2" value="芝加哥"/>
</bean>
2.3.3 使用工厂类
public class Factory {
//静态方法
public static Team staticFun() {
System.out.println("---------staticFun-----------");
return new Team(1003, "76人", "费城");
}
//实例方法
public Team instanceFun() {
System.out.println("---------instanceFun-----------");
return new Team(1004, "spur", "荷马城");
}
}
<!--工厂模式静态方法创建对象-->
<bean id="team4" class="com.kkb.bean.Factory" factory-method="staticFun"></bean>
<!--工厂模式实例方法创建对象-->
<!--先创建工厂对象-->
<bean id="factory" class="com.kkb.bean.Factory"></bean>
<!--再创建bean对象-->
<bean id="team5" factory-bean="factory" factory-method="instanceFun"></bean>
2.4 基于XML的DI
2.4.1 概念介绍
DI—Dependency Injection,即“依赖注入”:是组件之间依赖关系由容器在运行期决定,形象的说,即
由容器动态的将某个依赖关系注入到组件之中。
例如:有A、B、C、D四个类,A,B,C都有D作为成员变量,但D初值为空,却又调用了和D有关的方法,此时就需要通过依赖注入了。
//实体类
public class MyDao {
public void add() {
System.out.println("---myDao----add---");
}
}
public class MyService {
private MyDao myDao;
public void add() {
myDao.add();//由于myDao未实例化,此方法会报空指针异常
}
}
Ioc和DI是同一个概念的不同角度描述。IoC是一种思想,概念,DI是实现它的手段。Spring框架使用依
赖注入实现IoC.
2.4.2 注入前提
- set方法注入:需注入依赖的组件应提供依赖的get,set方法
- 其他方法注入:需注入依赖的组件应提供依赖的get,set方法及自身的构造方法(无参,有参都要有)
2.4.3 注入分类
-
set方法 (常用)
-
构造方法
-
自动注入
-
按名字自动注入
-
按类型自动注入
-
测试代码
<bean id="myDao" class="com.kkb.dao.MyDao"></bean>
<!--set方法注入-->
<bean id="myService" class="com.kkb.service.MyService" >
<property name="myDao" ref="myDao"></property>
</bean>
<!--构造方法注入-->
<bean id="myService2" class="com.kkb.service.MyService">
<constructor-arg name="myDao" ref="myDao"></constructor-arg>
</bean>
<!--按名称自动注入-->
<bean id="myService3" class="com.kkb.service.MyService" autowire="byName"></bean>
<!--按类型自动注入-->
<bean id="myService4" class="com.kkb.service.MyService" autowire="byType"></bean>
@Test
public void test02() {
ApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
MyService myService = (MyService) ac.getBean("myService");
MyService myService2 = (MyService) ac.getBean("myService2");
MyService myService3 = (MyService) ac.getBean("myService3");
MyService myService4 = (MyService) ac.getBean("myService4");
myService.add();
myService2.add();
myService3.add();
myService4.add();
}
- 运行结果
2.5 基于注解实现IOC(重要)
2.5.1 概念介绍
对于 DI 使用注解,将不再需要在 Spring 配置文件中声明 bean 实例。Spring 中使用注解,需要在原有
Spring 运行环境基础上再做一些改变。
2.5.2 注解类型
- @component
- 在类上添加注解@Component表示该类创建对象的权限交给Spring容器。注解的value属性用于指定
bean的id值,value可以省略。 - @Component 不指定 value 属性,bean 的 id 是类名的首字母小写。
- @Repository:用于dao实现类的注解
- @Service:用于Service实现类的注解
- @Controller:用于controller实现类的注解
Component相当于万能注解,后面三个注解还有其他的含义。不同的实现类用不同的注解
2.5.3 包扫描
2.5.3.1 概念介绍
需要在 Spring 配置文件中配置组件扫描器,用于在指定的基本包中扫描注解。如果没有包扫描,添加的创建对象的注解不生效。
2.5.3.2 扫描前提
修改xml文件,添加三项内容
2.5.3.3 扫描方式
<!--法一:使用多个component-scan声明-->
<context:component-scan base-package="com.kkb.controller"></context:component-scan>
<context:component-scan base-package="com.kkb.service"></context:component-scan>
<context:component-scan base-package="com.kkb.dao"></context:component-scan>
<!--法二:使用逗号/分号/空格(不推荐)隔开-->
<context:component-scan base-package="com.kkb.controller,com.kkb.service,com.kkb.dao">
</context:component-scan>
<!--法三:指定到父包名-->
<context:component-scan base-package="com.kkb"></context:component-scan>
2.5.4 注入
2.5.4.1 基础类型注入@Value
该注解的 value 属性用于指定要注入的值。使用该注解完成属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加到 setter 上。即value可用于属性上或对应属性的setter方法上,下图仅演示在属性上。
2.5.4.2 引用类型注入
- byType自动注入@Autowired
该注解默认使用按类型自动装配 Bean 的方式。使用该注解完成属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加到 setter 上。
2. byName自动注入@Autowired和@Qualifer
需要在引用属性上联合使用注解@Autowired 与@Qualifier。@Qualifier 的 value 属性用于指定要匹配
的 Bean 的 id 值。类中无需 set 方法,也可加到 set 方法上。
@Autowired 还有一个属性 required,默认值为 true,表示当匹配失败后,会终止程序运行。若将其值设置为 false,则匹配失败,将被忽略,未匹配的属性值为 null。
简言之,ture:要求自动注入,找不到该类时将报错,false:先自动去找对应的类,找到则自动注入,找不到则返回空值,并将这个空值自动注入,所以会报空指针异常。
3. 自动注入@Resource
@Resource由JDK提供。
@Resource 注解既可以按名称匹配Bean,也可以按类型匹配 Bean。默认是按名称注入。
使用该注解,要求 JDK 必须是 6 及以上版本。@Resource 可在属性上,也可在 set 方法上。
3.1. byName
3.2. byType
三.AOP-面向切面编程
3.1 概念介绍
AOP:Aspect Oriented Programming. 是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
以往service层代码还要加入日志,事务等东西,而且多个service要加入大量重复性代码,不利于项目的维护和扩展,现在通过将将日志,事务这些服务性代码抽离出来,形成一个切面,最后当程序启动后,可以和service进行动态编译执行。
以前的代码多数如demo1这样,通过切面编程后将进化到demo2,这样代码的可读性也能极大地提高。
3.2 AOP的实现机制-代理
3.2.1 概念介绍
代理:自己不做,找比人做。
即找一个代理类帮我们将事务,日志等东西融合,最后和service一起运行中。
3.2.2 代理分类
静态代理和动态代理
3.2.2.1 静态代理
- 原始方式-基于类(extends)
- 优化方式-基于接口(interface)
思路:
- service设计成接口,有多个实现类
- 日志,事务等设计成接口,且含有多个抽象方法,因为这些服务性代码出现的时机会有不同,可能在service前,后,异常,最后等部分,同时该有多个实现类。
- 设立代理类,整合service,log,transaction
- 总结
优点:可以做到在不修改目标对象的功能前提下,对目标对象功能扩展。
缺点:
- 如果service接口变化,实现类就要发生变化
- 因为代理对象要和目标对象实现一样的接口,所以会有很多代理类。
3.2.2.2 动态代理
- 介绍
程序运行的时候,根据要被代理的对象动态生成代理类
- 类型
- 基于JDK
虽然上图结构完整,但可读性不高,因此通过结构化设计可以把生成代理部分抽离出来封装成独立的方法。
- 基于CGLIB
Cglib代理,也叫做子类代理。在内存中构建一个子类对象从而实现对目标对象功能的扩展。
JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有
实现接口的类,就可以使用CGLIB实现。
- 结构化设计
3.3 SpringAOP
3.3.1 概念介绍
Spring的AOP实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分
进行代码编写,并通过配置的方式完成指定目标的方法增强。
3.3.2 相关术语
- Target(目标对象)
要被增强的对象,一般是业务逻辑类的对象- Proxy(代理)
一个类被AOP织入增强后,就产生一个结果代理类- Aspect(切面)
表示增强的功能,一般是服务性对象- Joinpoint(连接点)
指被拦截的点,在Spring中,这些点指的是方法(一般是业务方法)。- Pointcut(切入点)
连接点的集合,被标记为final的方法不能作为连接点与切入点,因为最终不能被修改。- Advice(通知/增强)
当拦截到joinpoint后要做的事就是通知了,通知定义了增强代码切入到目标代码的时间点,是方法前还是方法后,- Weaving(织入)
把增强应用到目标对象中,创建新的代理对象的过程。即把业务性代码和服务性代码共同整合。
3.3.3 Aspectj对Aop的实现
3.3.3.1 背景介绍
对于 AOP 这种编程思想,很多框架都进行了实现。Spring 就是其中之一,可以完成面向切面编程。
AspectJ 也实现了 AOP 的功能,且其实现方式更为简捷而且还支持注解式开发。所以,Spring 又将
AspectJ 的对于 AOP 的实现也引入到了自己的框架中。
3.3.3.2 通知类型
前置、后置、异常、返回、环绕
3.3.3.3 Aspectj切入点表达式
语法:execution(访问权限 方法返回值 方法声明(参数) 异常类型)
示例:
> 现在难理解没关系,通过后面的案例就可以慢慢熟悉了。不过要记得包后面有两个点能代表子包。
3.3.3.4 AOP实现方式
- 注解方式
(1)创建项目引入依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!--编译插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
(2) 创建Spring文件引入约束
<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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.kkb.service,com.kkb.aop"/>
</beans>
(3)创建核心业务类
(4)创建切面类
(5)添加注解以便进行包扫描
(6)通知概述
- 前置通知(@Before):出现在方法调用前
- 后置通知(@After):出现在方法调用后,无论是否发生异常,此时未能取到方法的返回值
- 异常通知(@AfterThrowing):在方法执行期间,出现在后置通知后
- 返回通知(@AfterReturning):在方法执行结束后,出现异常则不返回
- 环绕通知(@Around):出现在方法执行前后,可用来代替前置后置通知,方法必须有返回值,即目标方法返回值,否则返回通知将获取空值。
(7)测试运行 - 无异常
- 有异常
(8)切入点表达式优化
每个方法都写入大量冗杂的注解相当麻烦,可以通过引入 @Pointcut 来解决
2. XML方式
(1)创建切面类(注释掉原来的Aspect)
(2)配置xml文件
<aop:config>
<aop:pointcut id="p1" expression="execution(* com.kkb.service..*.*(..))"/>
<aop:aspect ref="myAop">
<aop:before method="before" pointcut-ref="p1"/>
<aop:after method="myFinally" pointcut-ref="p1"/>
<aop:after-returning method="afterReturn" returning="result" pointcut-ref="p1"/>
<aop:after-throwing method="exception" throwing="ex" pointcut-ref="p1"/>
<aop:around method="around" pointcut-ref="p1"/>
</aop:aspect>
</aop:config>
(3)测试运行
- 无异常
- 有异常
3.3.3.5 after,after-returning,after-throwing注解方式和XML方式的区别
- 注解方式:
- 无异常:
after->after-returning
,无after-throwing
- 有异常:
after->after-throwing
,无after-returning
该方式after靠前
- XML方式:
- 无异常:
after-returning->after
,无after-throwing
- 有异常:
after-throwing->after
,无after-returning
该方式after靠后
四.Spring整合JDBC
4.1 背景介绍
- 学习原因:以往连接数据库是较为繁琐的一步,流程基本是:创建连接、预编译Sql、Sql赋值、执行Sql、获取结果,就算结合工具类开发也是较为繁琐的,毕竟我们的核心只有写语句和取结果这两步。因此这块如果交给Spring来操作,将是极为简便的。
- 主要内容:学习使用JdbcTemplate API和 如何使用Spring管理 JdbcTemplate
4.1 操作流程
- 编写数据库数据
- 导入项目依赖
注意:导入Spring相关的依赖时版本必须一致,否则有些jar包版本会冲突
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!--编译插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
-
创建实体类Team(tid,name,location)
-
创建application.xml约束文件
-
创建dao
通过继承JdbcDaoSupport 获得私有方法getJdbcTemplate来进行操作,主要是两大类
- 增删改:update
- 查询:
- 一个对象:queryForObject
- 一组对象:query
- 一个数值:queryForObject
- 一组数值:queryForMap
查询对象时每次都要将查询到的数据进行封装,因此考虑将其抽离出来形成一个独立的方法handleResult
public class TeamDao extends JdbcDaoSupport {
//封装查询方法
public Team handleResult(ResultSet resultSet) throws SQLException {
Team team = new Team();
team.setTid(resultSet.getInt("tid"));
team.setName(resultSet.getString("name"));
team.setLocation(resultSet.getString("location"));
return team;
}
//1. 增加数据
public int insert(Team team) {
String sql = "insert into team(name,location) value(?,?)";
return this.getJdbcTemplate().update(sql, team.getName(), team.getLocation());
}
//2. 删除数据
public int del(int id) {
String sql = "delete from team where tid=?";
return this.getJdbcTemplate().update(sql, id);
}
//3. 修改数据
public int update(Team team) {
String sql = "update team set name=? where tid=?";
return this.getJdbcTemplate().update(sql, team.getName(), team.getTid());
}
//4. 查询数据(返回一个对象)
public Team findById(int id) {
String sql = "select * from team where tid=?";
//第二个参数是一个数组
return this.getJdbcTemplate().queryForObject(sql, new Object[]{id},
new RowMapper<Team>() {
@Override
public Team mapRow(ResultSet resultSet, int i) throws SQLException {
return handleResult(resultSet);
}
});
}
//5. 查询数据(返回一组对象)
public List<Team> findAll() {
String sql = "select * from team";
return this.getJdbcTemplate().query(sql, new RowMapper<Team>() {
@Override
public Team mapRow(ResultSet resultSet, int i) throws SQLException {
return handleResult(resultSet);
}
});
}
//6. 查询数据(返回一个数值)
public int getCount() {
String sql = "select count(tid) from team";
return this.getJdbcTemplate().queryForObject(sql, Integer.class);
}
//7. 查询数据(返回一组数值)
public Map<String,Object> getMany() {
String sql = "select max(tid),min(tid) from team";
return this.getJdbcTemplate().queryForMap(sql);
}
}
- 编写测试方法进行调试即可
五.Spring事务管理
事务原本是数据库中的概念,在 Dao 层。但在实际开发中,一般将事务提升到业务层,即 Service 层。
这样做是为了能够使用事务的特性来管理具体的业务。
5.1 事务管理器接口
事务管理器是 PlatformTransactionManager 接口对象。其主要用于完成事务的提交、回滚,及获取事
务的状态信息。常用的实现类:DataSourceTransactionManager
Spring 事务的默认回滚方式是:发生运行时异常和 error 时回滚,发生受查(编译)异常时提交。
5.2 事务传播行为常量
5.2.1 概念介绍
所谓事务传播行为是指,处于不同事务中的方法在相互调用时,执行期间事务的维护情况。如,A 事务
中的方法 doSome()调用 B 事务中的方法 doOther(),在调用执行期间事务的维护情况,就称为事务传
播行为。事务传播行为是加在方法上的。
事务传播行为常量都是以 propagation_ 开头。
5.2.2 类别
常用的有两类
- 增删改:required
- 查询:supports
5.3 声明式事务控制
Spring提供的对事务的管理,就叫做声明式事务管理。
如果用户需要使用spring的声明式事务管理,在配置文件中配置即可:不想使用的时候直接移除配置。
这种方式实现了对事务控制的最大程度的解耦。
声明式事务管理,核心实现就是基于AOP。
- 事务的粗细粒度
细粒度:对方法中的某几行代码进行开启提交回滚;
粗粒度:对整个方法进行开启提交回滚;
5.4 事务的实现方式
5.4.1 基于注解
- 创建service类,并为方法配上注解
同时插入两条数据进行模拟
@Service
public class TeamService {
@Autowired
private TeamDao teamDao;
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
public int insert(Team team) {
int i1 = teamDao.insert(team);
System.out.println("i1插入成功");
int i2 = teamDao.insert(team);
System.out.println("i2插入成功");
return i1 + i2;
}
}
- 修改约束文件(基于jdbc的xml上)
<beans>
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd"
<context:component-scan base-package="com.kkb.service"/>
<!--方式一-->
<tx:annotation-driven transaction-manager="transcationManager"/>
<beans/>
- 测试运行
@Test
public void test01() {
TeamService teamService = (TeamService) ac.getBean("teamService");
Team team=new Team();
team.setName("马刺");
team.setLocation("荷马城");
int insert = teamService.insert(team);
System.out.println("insert--------"+insert);
}
- 加入异常情况,查看能否执行回滚
- 查看数据库发现,第一条语句并未插入成功,因此事务已执行回滚操作。
5.4.2 基于XML
该方式可结合基于XML实现aop
- 注释掉原来的注解 @Transactional
- 修改xml文件,添加如下内容
<baens>
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd"
<!--方式二-->
<!--id值可以自定义,事务管理器的值不能更改-->
<tx:advice id="txAdvice" transaction-manager="transcationManager">
<tx:attributes>
<!--根据不同方法进行添加,比注解方式简便-->
<tx:method name="insert" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="pt" expression="execution(* com.kkb.service..*.*(..))"/>
<aop:advisor pointcut-ref="pt" advice-ref="txAdvice"/>
</aop:config>
<beans/>
- 修改pom.xml文件,添加aop配置
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
- 测试运行,可自定义异常进行检测
六.总结
Spring主要的核心功能就是IOC和AOP,此外还加入了jdbcTemplate和事务管理
- IOC:推荐通过XML方式进行包扫描,然后配置注解@Autowired,避免创建多个bean,一般都是加在dao类上,所以可以不考虑其有成员变量,因此也就不考虑xml方式创建带参的dao对象。
- AOP:推荐使用SpringAOP的注解方式,通过提炼出单独的@Pointcut,对每个方法进行配置,这样可以更直观地感受每个服务性代码织入的时间点。
- jdbcTemplate:通过在约束文件中引入相关的源,再从dao类中继承JdbcDaoSupport,并调用其内置的getJdbcTemplate方法,自己就可以只关注sql语句的编写和结果集的返回了。
- 事务管理:推荐使用基于XML方式,虽然引入的文件较多,但可以避免在service类上写多次注解,而是在xml约束文件上加上新的方法即可。
对比AOP的注解方式,当提炼出@Pointcut后,两者基本都是在其他方法上添加大量重复性的内容,但@Pointcut书写起来较为简便,所以虽然流程相同,却更推荐事务管理使用XML配置文件的方式。