我们一般的开发基本上有两种模式:C/S架构(客户端与服务器端)、B/S架构(浏览器端和服务器端),而java非常适合做B/S架构
建议收藏的网址:
spring的网址 :https://docs.spring.io/spring/docs/current/spring-framework-reference/#mvc-controller
maven工厂:https://mvnrepository.com/
目录
spring是什么
spring是全栈式轻量级框架,以IOC和AOP为核心,提供了表现层SpringMVC和和持久层 Spring JDBC以及业务层事物管理等众多的企业级应用技术,还能整合众多第三方框架和类库,逐渐成为使用最多的java EE企业应用开源框架。
Spring的优势
- 方便解耦,简化开发
解耦:降低程序间的依赖关系,增加程序的独立性
解耦思路:
使用反射来创建对象,而避免使用new
通过读取配置文件来获取要创建的对象全限定类名
通过工厂模式解耦
- AOP编程的支持
- 声明式事物的支持
- 方便程序的测试
- 方便集成各种优秀框架
- 降低JavaEE API的使用难度
Spring体系结构
所有的运行都需要核心容器IOC的支持
Bean和JavaBean
- Bean
可重用组件的含义 - JavaBean
JavaBean >实体类
用java语言编写的可重用组件
他就是创建我们的sevice和dao对象的
步骤:
第一步:需要一个配置文件配置我们的service和dao,配置内容:全限定类名
第二步:通过配置文件内容,反射创建对象
单例和多例
-
单例:只创建了一个实例对象,也只初始化一次,会出现线程安全问题
servlet就是单例的 -
多例:对象被创建多次,执行效率没有单例搞
工厂模式
- 通过工厂来与资源联系,降低依赖
IOC
-
把对象创建的权利交给框架框架。它包括依赖注入(DI)和依赖查找
-
作用:缩减程序的耦合度
-
ioc的底层是获取配置文件中的key存入,通过key获取全限定类名并存人map,工厂模式,遍历map,通过反射创建对象
-
ApplicatonContext的三个常用实现类: -
ClassPathXmlApplicatonContext:它可以加载类路径下的配置文件,要求配置文件必须在类路径下,不在的话就加载不了
-
FileSystemXmlApplicationContext:它可以加载自盘任意路径下的配置文件(必须有访问权限)
-
AnnotationConfigApplicationContext:它是用于读取注解创建容器的
核心容器的两个接口: -
ApplicationContext: 单例对象适用 实际开发中更多采用此接口
-
他在构建核心容器时,创建对象采取的策略是采用立即加载的方式,也就是说,只要一读取配置文件马上就创建配置文件中配置的对象
-
BeanFactory 多例对象适用
-
它在构建核心容器时,创建对象采用的策略是采用延迟加载的方式。也就是说,什么时候根据id创建对象,什么时候才真正的创建对象。
创建Bean的三种方式:
-
使用默认构造函数创建
在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时,采用的就是默认构造函数创建bean对象,此时如果勒种没有默认构造函数,则对象无法创建
-
使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)
-
使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)
Bean的作用范围(scope属性) -
singleton 单例的(默认)
-
prototype 多例的
-
request 作用于web应用的请求范围
-
session 作用于web应用的会话范围
-
global-session 作用于集群环境的会话范围 吧
Bean对象的生命周期
-
单例对象(立即创建)
出生:当容器创建时对象出生
或者:只要容器还在,对象一直活着
死亡:容器销毁,对象消亡
总结;单例对象的声明周期和容器相同 -
多例对象(延迟创建)
出生:当我们使用对象时,spring框架为我们创建
活着:对象在使用过程中一直活着
销毁:对象长时间不用,且没有其他对象引用时,java的垃圾回收机制进行回收
依赖注入(Dependency Injection)
- 有参构造函数注入(此方法基本不用,避无可避才用)
使用标签constructor-arg
标签出现的位置:bean标签的内部
标签中的属性:
type:用于指定注入数据的类型
index:给指定索引位置的参数赋值,索引的位置从0开始
name:给构造函数中指定名字的参数赋值(最常用,基本用这个)
以上三个参数用于指定给构造函数中哪个参数赋值*
value:用于提供基本类型和string的数据
ref:用于引用其他类型的bean类型数据,指的是sping的IOC核心容器中的出现的bean对象
特点:
在获取bean对象时,注入数据时必须的操作,否则对象无法创建成功 - set注入(更常用)
涉及的标签:property
出现的位置:bean标签的内部
标签的属性:
name:用于指定注入时所调用的set方法名称
value:用于提供基本类型和stirng类型的数值
ref:用于指定其他的bean类型数据,他指的是在spring的Ioc的核心容器中出现的bean对象
优势:创建对象时没有直接的限制,可以直接使用默认构造函数
弊端:如果某个成员必须有值,但又没进行赋值(比如没写set方法),也不会报错
复杂类型(集合类型)的数据注入
用于给List的结构集合进行注入标签的有
List、Set、Array
用于给map结构集合进行注入的标签的有
map、props
总结:结构一致,标签可以进行互换,总的来说只用记list和map这两个标签
- 注解注入
常用IOC注解分类
-
用于创建对象的(@Component,@Controller,@Service,@Repository)
@Controller 控制层
@Service 业务逻辑层
@Repository 持久层
以上三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰,但是都是可以用Component替代的,其实不影响。
他们的作用于xml中配置的文件中编写一个bean标签的作用是一样的
作用:用于把当前类对象存入spring容器中
属性:
value用于指定bean的id,当没写该属性时,默认value值为类名改小写
-
用于注入数据的(@Autowired,@Qualifier,@Resource)
他们的作用于xml中配置文件中的bean标签中写一个property标签中的作用是一样的
Autowired:
作用:自动按照类型注入,只要容器中有一个bean对象类型和要注入的变量类型匹配,就可以注入成功,如果两个相同类型的对象,则按对象名称进行匹配。
Qualifier:
作用:在按照类注入的基础上再按照名称注入,在给类成员注入时不能单独使用(要和autowired搭配使用 ),但是在给方法参数的注入可以单独使用
Resource
作用:直接按照bean的id注入,它可以独立使用
属性:
name:用于指定bean的id
以上三个注入都只能注入其他bean类型的数据,而基本类型和string类型无法使用上述注解注入,另外集合类型的注入只能通过xml来实行
Value:
作用:用于注入基本类型和stirng类型的数据
属性:
value用于指定数据的值,它可以使用spring和spEL(也就是spring的el表达式)
SpEL的写法:$(表达式) -
用于改变作用范围的
他们的作用和bean标签中使用scope属性实现的功能是一样的
Scope:
作用:用于指定bean的作用范围
属性:
value:指定范围的取值,常用取值:singleton prototype(单例,多例)
-
和生命周期有关的
他们的作用和在bean标签中使用init-method和destory-method的作用是一样的
PreDestroy:
作用:用于指定销毁方法
PreConstruct
作用:用于指定初始化方法
注意:使用注解要开启注解扫描
配置类Configuration
作用:指定当前类是一个配置类
细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,可以不写
-
ComponentScan
作用:用于通过注解指定spring在创建容器时要扫描的包
属性:
value=basePcakages :用于指定创建容器时要扫描的包,我们使用此注解就等同于在xml中配置的:
<context:component-scan base-package="com.hwh"></context:component-scan>
使用:
@ComponentScan("com.hwh")
-
Bean
作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器中
属性:
name:用于指定bean的id,默认值为当前方法的名称
细节:
当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找是否有可用的bean对象,查找方式与autowried相同。 -
import
作用:用于导入其他的配置类
属性:
value:用于指定其他配置类的字节码
当我们使用Import的注解之后,有Import注解的类就是父配置类,而导入的都为子配置类
-
PropertySource
作用:用于指定properties文件的位置,通过@PropertySource注解将properties配置文件中的值存储到Spring的 Environment中,Environment接口提供方法去读取配置文件中的值,参数是properties文件中定义的key值。属性:
value:指定文件的名称和路径
关键字:classpath:便是类路径
@Value(${jdbc.driver})
spring整合Junit
1、应用程序的入口 :main方法
2、junit单元测试中,没有main方法也能执行,junit集成了一个main方法,该方法会判断当前测试类中哪些方法有@Test注解,junit就让有Test的方法执行
3、junit不管我们否采用spring框架
在执行,所以也不会为我们读取配置文件/配置类创建spring核心容器
4、由以上三点可知
当测试方法执行时,没有ioc容器,就算写了@Aurowried注解,也无法实现注入
spring整合junit配置
当我们使用spring5.X版本的时候,要求junit的jar包必须是4.12及以上
1、导入spring-test jar包
2、使用junit提供的一个注解把原有的main方法替换了,替换成spring提供的@Runwith
@RunWith(SpringJUnit4ClassRunner.class)
@RunWith(locatons = "classpath:***.xml")
3、告知spring的运行器,spring的ioc容器时基于xml还是注解的,并且说明位置@ContextConfiguration
Location:指定xml文件的位置,加上classpath关键字,百世在类路径下
classes:指定注解类所在位置
@ContextConfiguration(classes = springConfiguration.class)
事物控制
Account source = accountDao.findAccountByName(name);
//2、根据名称查询转入账户
Account target = accountDao.findAccountByName(targetname);
//3、转出账户减钱
source.setMoney(source.getMoney()-money);
int i = 1/0;
//4、转入账户加钱
target.setMoney(target.getMoney()+money);
//5、更新转入账户
accountDao.updateaccount(source);
//6、更新转出账户
accountDao.updateaccount(target);
该代码会产生一定的问题,事物是在持久层,每一个语句建立了一个连接,导致中间发生异常后,不能进行回滚,而产生不一致,不能保证一致性。所以需要通过连接和线程进行绑定,使事物控制发生在业务层。
- ConnectionUtils
package com.hwh.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* 连接的工具类,它用于从数据源中获取一个连接,并实现对线程的绑定
*/
@Component
public class ConnectionsUtils {
private ThreadLocal<Connection> t1 = new ThreadLocal<Connection>();
@Autowired
private DataSource dataSource;
/**
* 获取当前线程上的连接
*/
public Connection getThreadConnection(){
try {
//1、先从threadLocal获取连接
Connection connection = t1.get();
if(connection==null){
//从数据库中获取一个连接,并且存入threadlocal中
connection = dataSource.getConnection();
t1.set(connection);
}
return connection;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
/**
* 把连接和线程进行解绑
*/
public void removeConnecion(){
t1.remove();
}
}
- TransactionManager(事物管理)
package com.hwh.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;
import java.sql.SQLException;
/**
* 和事物管理相关的工具类,包括开启事务,提交事物,回滚事物,断开连接
*/
@Component
public class TransactionManager {
@Autowired
ConnectionsUtils connectionsUtils;
public void setConnectionsUtils(ConnectionsUtils connectionsUtils) {
this.connectionsUtils = connectionsUtils;
}
/**
* 开启事物
*/
public void BeginTransaction(){
try {
connectionsUtils.getThreadConnection().setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 提交事物
*/
public void commint(){
try {
connectionsUtils.getThreadConnection().commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 回滚事物
*/
public void rollback(){
try {
connectionsUtils.getThreadConnection().rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 断开连接
*/
public void close(){
try {
//把连接返回连接池中
connectionsUtils.getThreadConnection().close();
connectionsUtils.removeConnecion();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
相关知识:
Synchronized是通过线程等待,牺牲时间来解决访问冲突
ThreadLocal是通过每个线程单独一份存储空间,牺牲空间来解决冲突,并且相比于Synchronized,ThreadLocal具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问到想要的值。
代理的分析
-
动态代理
特点:字节码随用随创建,随用随加载
作用:不修改源码的基础上对方法增强
分类:
基于接口的动态代理
基于子类的动态代理 -
基于接口的动态代理:
设计的类:Proxy
提供者:JDK官方
如何创建代理对象
使用Proxy类中的newProxyIntstance方法
创建代理对象的要求:
被代理类最少实现一个接口,如果没有则不能使用
newProxyIntance方法的参数
ClassLoader:类加载器
他是用于加载代理对象字节码的,和被代理对象使用相同的列加载器,固定写法
Class[]:字节码数组
它是用于代理对象和被代理对象有相同的方法,固定写法,代理谁写谁的
InvocationHandler:用于提供增强的代码
它是让我们写如何代理,我们一般是写一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的
案例: -
iProduct(接口)
package Proxy;
/**
* @Description 生产工厂
* @Author hwh
* @Date 2019/12/15 19:48
**/
public interface iProduct {
/**
* 销售
* @param money
*/
public void saleProduce(Float money);
/**
* 售后
* @param money
*/
public void afterService(Float money);
}
- Produce(工厂类)
package Proxy;
/**
* @Description 工厂接口类
* @Author hwh
* @Date 2019/12/15 19:51
**/
public class Produce implements iProduct{
/**
* 销售
* @param money
*/
public void saleProduce(Float money) {
System.out.println("销售产品,并拿到钱:"+money);
}
/**
* 售后
* @param money
*/
public void afterService(Float money) {
System.out.println("售后服务,并拿到钱:"+money);
}
}
- Client(客户找代理购买产品)
package Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @Description 客户
* @Author hwh
* @Date 2019/12/15 19:53
**/
public class Client {
public static void main(String[] args) {
final Produce produce = new Produce();
/**
* 创建一个代理对象
*/
iProduct proxyProducer = (iProduct)Proxy.newProxyInstance(produce.getClass().getClassLoader(), produce.getClass().getInterfaces(), new InvocationHandler() {
/**
*执行代理对象的任何方法都会经过该方法
* @param proxy 代理对象的引用
* @param method 当前执行的方法
* @param args 当前执行方法的参数
* @return 和被代理对象方法有相同的返回值
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//提供增强的代码
Object returnValue = null;
//1.获取方法执行的参数
Float money = (Float) args[0];
//2、判断打你那方法是不是销售
if("saleProduce".equals((method .getName()))){
returnValue = method.invoke(produce,money*0.8f);
}
return returnValue;
}
});
proxyProducer.saleProduce(10000f);
}
}
-
注意:匿名内部类使用外部对象必须为final
-
基于子类的动态代理:
设计的类:Enhancer
提供者:第三方cglib库
如何创建代理对象
使用Enhancer类中的create方法
创建代理对象的要求:
被代理类不能是最终类(也就是final修饰的类,因为不能被继承)
create方法的参数
Class:字节码
它是用于指定被代理对象的字节码
Class[]:字节码数组
它是用于代理对象和被代理对象有相同的方法,固定写法,代理谁写谁的
Callback:用于提供增强的代码
我们一般写的都是改接口的子接口实现类:MethodInterceptor
(执行代理对象的任何方法都会经过该方法)
案例: -
cglib代理
package Proxy;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @Description 客户
* @Author hwh
* @Date 2019/12/15 19:53
**/
public class Client {
public static void main(String[] args) {
final Produce produce = new Produce();
//
// /**
// * 创建一个代理对象
// */
Produce cglibproduce = (Produce)Enhancer.create(produce.getClass(), new MethodInterceptor() {
/**
* 执行类对象中的任何方法都会经过该方法
* @param o 代理对象的引用
* @param method 当前执行的方法
* @param objects 方法的参数
* @param methodProxy 当前执行方法的代理对象
* @return
* @throws Throwable
*/
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//提供增强的代码
Object returnValue = null;
//1.获取方法执行的参数
Float money = (Float) objects[0];
//2、判断打你那方法是不是销售
if("saleProduce".equals((method .getName()))){
returnValue = method.invoke(produce,money*0.8f);
}
return returnValue;
}
});
cglibproduce.saleProduce(10000f);
}
}
AOP(面向切面编程)
运用动态代理和预编译方式实现程序功能同一维护的一种技术。利用AOP可以降低耦合度,提高程序重用性和提高开发效率。
简单的说,他就是把我们程序中重复的代码抽取出来,在需要执行的时候,运用动态代理技术,在不修改源码的基础上,对我们已有的方法进行增强。
- AOP的作用和优势
作用:在程序运行期间,不修改源码对已有代码进行增强
优势:
1、减少重复代码
2、提高开发效率
3、维护方便
4、提高重用性 - AOP的实现方式
使用动态代理(包括JDK代理和cglib代理 )
Spring的AOP
-
代理的选择
在spring中框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式 -
AOP的术语
-
Joinpoint(连接点):
在spring中,这些点指的就是业务层中所有的方法 -
Pointcut(切入点):
指我们要对哪些Joinpoint进行拦截,对其进行增强的方法 -
Advice(通知/增强):
所谓通知是指拦截到joinpoint之后所要做的事情
通知的类型:前置通知、后置通知、异常通知、最终通知和环绕通知
-
Introduction(引介)
引介是一种特殊的通知在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一些方法或Field -
Target(目标对象)
被代理的对象 -
Weaving(织入)
是指把增强应用到目标对象来创建新的代理对象的过程
spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入 -
Proxy(代理)
一个类被AOP织入增强后,就产生一个代理对象 -
Aspect(切面)
切入点和通知(引介)的结合
spring中基于AOP的XML配置步骤
1、把通知的bean也交给spring来管理
2、使用aop:config标签表明开始AOP的配置
3、使用aop:aspect标签表明开始配置切面
id属性:是给切面提供一个唯一标记
ref属性:是指定通知类bean的id
4、在aop:aspect标签的内部使用对应标签来配置通知的类型
我们现在的示例是让printLog方法在切入点方法之前执行,所以是前置通知
aop:before:表示配置前置通知
method属性:用于指定Logger类中哪个方法时前置通知
pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中的哪些方法增强
- 切入点表达式的写法
关键字:execution(表达式)
表达式
访问修饰符 返回值 包名.包名… 类名.方法名(参数列表)
public void com.hwh.service.impl.AccountServiceImpl.saveAccount()
访问修饰符可以省略
返回值可以使用通配符,表示任意返回值
* com.hwh.service.impl.AccountServiceImpl.saveAccount()
包名可以使用通配符表示任意包,但是有几级包就要写几个*.
* *.*.*.*.AccountServiceImpl.saveAccount()
包名可以使用..表示当前包及其子包
* *..AccountServiceImpl.saveAccount()
类名和方法名可以用*来实现通配
参数列表:
可以直接写数据类型:
基于类型直接写名称 int
引用类型写包名.类名的方式 java.lang.String
可以使用通配符表示任意类型*,但是必须有参数
可以使用..表示有无参数均可,有参数可以使任意参数
全通配写法:
* *..*.*()
实际开发中切入点表达式的通常写法
切到业务层实现类下的所有方法
* com.hwh.service.imp.*.*(..)
基于xml的AOP配置代码:
<!-- spring中基于xml的AOP配置 -->
<aop:config>
<!-- 配置接入点表达式-->
<aop:pointcut id="pc1" expression="execution(* com.hwh.service.Imp.*.*(..))"/>
<!-- 配置切面-->
<aop:aspect id="logAdvice" ref="logger">
<!-- 配置通知的类型,并且建立通知方法和切入点方法的关联-->
<!-- <aop:before method="printLog" pointcut="execution(public void com.hwh.service.AccountService.saveAccount())"></aop:before>-->
<!-- 前置通知-->
<aop:before method="beforePrintLog" pointcut-ref="pc1"></aop:before>
<!-- 后置通知-->
<aop:after-returning method="afterPrintLog" pointcut-ref="pc1"></aop:after-returning>
<!--异常通知 -->
<aop:after-throwing method="throwPrintLog" pointcut-ref="pc1"></aop:after-throwing>
<!--最终通知 -->
<aop:after method="finallyPrintLog" pointcut-ref="pc1"></aop:after>
</aop:aspect>
</aop:config>
- 环绕通知
spring框架为我们提供了一个接口:ProceedingJoinPoint,该接口有一个方法proceed(),此方法相当于明确调用切入点方法,该接口可以作为环绕通知的方法参数,在程序执行之前,spring框架会为我们提供该接口的实现类供我们使用。
作用:它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式。
<aop:around method="aroundPrintLog" pointcut-ref="pc1"></aop:around>
/**
* 环绕通知
*/
public Object aroundPrintLog(ProceedingJoinPoint pjp){
Object result = null;
try {
Object[] args = pjp.getArgs();
System.out.println("环绕通知aroundPrintLog打印了日志");
result = pjp.proceed(args);
System.out.println("环绕通知aroundPrintLog打印了日志");
return result;
} catch (Throwable throwable) {
System.out.println("环绕通知aroundPrintLog打印了日志");
throw new RuntimeException(throwable);
}finally {
System.out.println("环绕通知aroundPrintLog打印了日志");
return result;
}
}
基于注解的AOP配置代码
注意:AOP的注解配置中后置通知和最终通知的顺序会发生错乱,所以建议使用环绕通知
注解AOP的执行顺序:前置通知—最终通知----后置通知(异常通知)
package com.hwh.utils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.concurrent.Executor;
/**
* @Description 用于记录日志的工具类,它里面提供了公共的代码
* @Author hwh
* @Date 2019/12/20 14:30
**/
@Component("logger")
@Aspect
public class Logger {
@Pointcut("execution(* com.hwh.service.Imp.*.*(..))")
public void pc1(){}
/**
* 前置通知
*/
@Before("pc1()")
public void beforePrintLog(){
System.out.println("前置通知beforePrintLog打印了日志");
}
/**
* 后置通知
*/
@AfterReturning("pc1()")
public void afterPrintLog(){
System.out.println("后置通知afterPrintLog打印了日志");
}
/**
* 异常通知
*/
@AfterThrowing("pc1()")
public void throwPrintLog(){
System.out.println("异常通知throwPrintLog打印了日志");
}
/**
* 最终通知
*/
@After("pc1()")
public void finallyPrintLog(){
System.out.println("最终通知finallyPrintLog打印了日志");
}
/**
* 环绕通知
*/
@Around("pc1()")
public Object aroundPrintLog(ProceedingJoinPoint pjp){
Object result = null;
try {
Object[] args = pjp.getArgs();
System.out.println("环绕通知aroundPrintLog打印了日志---前置");
result = pjp.proceed(args);
System.out.println("环绕通知aroundPrintLog打印了日志---后置");
return result;
} catch (Throwable throwable) {
System.out.println("环绕通知aroundPrintLog打印了日志---异常");
throw new RuntimeException(throwable);
}finally {
System.out.println("环绕通知aroundPrintLog打印了日志---最终");
return result;
}
}
}
xml中:
<aop:aspectj-autoproxy ></aop:aspectj-autoproxy>
Spring中的JDBCTemplate
- JdbcTemplate的作用
它就是用于和数据库交互的,实现对表的CRUD操作 - 注意:
示例代码:
Account实体类:
package com.hwh.entity;
import java.io.Serializable;
/**
* @Description 账户实体类
* @Author hwh
* @Date 2019/12/22 10:18
**/
public class Account implements Serializable {
private Integer id;
private String name;
private Float money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Float getMoney() {
return money;
}
public void setMoney(Float money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
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">
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/test"></property>
<property name="username" value="root"></property>
<property name="password" value=""></property>
</bean>
</beans>
基于Jdbctemplate的CURD操作
package com.hwh.jdbcTemplate;
import com.hwh.entity.Account;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
/**
* @Description JdbcTemplate的基本用法
* @Author hwh
* @Date 2019/12/22 10:20
**/
public class JdbcTemplateDemo1 {
public static void main(String[] args) {
//获取IOC容器
ApplicationContext ac = new ClassPathXmlApplicationContext("ApplicationContext.xml");
//获取JdbcTemplate对象
JdbcTemplate jdbcTemplate = (JdbcTemplate) ac.getBean("jdbcTemplate");
//插入
jdbcTemplate.update("insert into account(name,money) values(?,?)","jerry",1000f);
//更新
jdbcTemplate.update("update account set name = ? where id = ?","tom",3);
//删除
jdbcTemplate.update("delete from account where id = ?",1);
//查询
//new BeanPropertyRowMapper<Account>(Account.class)是对结果集的一个封装
List<Account> accounts = jdbcTemplate.query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),2);
System.out.println(accounts.isEmpty()?"没有结果":accounts.get(0));
//查询一个
Long count = jdbcTemplate.queryForObject("select count(*) from account",Long.class);
System.out.println(count);
}
}
- jdbcTemplate在Dao层中的应用
基于注解方式的实现:
package com.hwh.dao.impl;
import com.hwh.dao.AccountDao;
import com.hwh.entity.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @Description TODO
* @Author hwh
* @Date 2019/12/22 14:23
**/
@Repository
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 查询全部账户
* @return
*/
public List<Account> findAllAccount(){
List<Account> accounts = jdbcTemplate.query("select *from account",new BeanPropertyRowMapper<Account>(Account.class));
System.out.println(accounts.isEmpty()?"没有结果":accounts);
return accounts;
}
/**
* 根据名字查询账户
* @param name
* @return
*/
public Account findAcconntByName(String name){
List<Account> accounts = jdbcTemplate.query("select * from account where name = ?",new BeanPropertyRowMapper<Account>(Account.class),name);
if(accounts.isEmpty()){
return null;
}else if(accounts.size()>1){
throw new RuntimeException("结果集不唯一,请检查数据");
}else{
return accounts.get(0);
}
}
/**
* 根据id查询账户
* @param id
* @return
*/
public Account findAccountById(Integer id){
List<Account> accounts = jdbcTemplate.query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),id);
return accounts.isEmpty()?null:accounts.get(0);
}
/**
* 保存账户
* @param account
*/
public void insertAccount(Account account) {
jdbcTemplate.update("insert into account(name,money) values(?,?) ",account.getName(),account.getMoney());
}
/**
* 更新账户
* @param account
*/
public void updateAccount(Account account){
jdbcTemplate.update("update account set name = ?, money=?",account.getName(),account.getMoney());
}
/**
* 删除账户
* @param id
*/
public void deleteAccount(Integer id){
jdbcTemplate.update("delete from account where id = ?",id);
}
}
基于xml方式,继承JdbcDaoSupport代码实现
package com.hwh.dao.impl;
import com.hwh.dao.AccountDao;
import com.hwh.entity.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Repository;
import java.util.List;
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
/**
* 查询全部账户
* @return
*/
public List<Account> findAllAccount(){
List<Account> accounts = super.getJdbcTemplate().query("select * from account",new BeanPropertyRowMapper<Account>(Account.class));
return accounts;
}
/**
* 根据名字查询账户
* @param name
* @return
*/
public Account findAcconntByName(String name){
List<Account> accounts = super.getJdbcTemplate().query("select * from account where name = ?",new BeanPropertyRowMapper<Account>(Account.class),name);
if(accounts.isEmpty()){
return null;
}else if(accounts.size()>1){
throw new RuntimeException("结果集不唯一,请检查数据");
}else{
return accounts.get(0);
}
}
/**
* 根据id查询账户
* @param id
* @return
*/
public Account findAccountById(Integer id){
List<Account> accounts = super.getJdbcTemplate().query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),id);
return accounts.isEmpty()?null:accounts.get(0);
}
/**
* 保存账户
* @param account
*/
public void insertAccount(Account account) {
super.getJdbcTemplate().update("insert into account(name,money) values(?,?) ",account.getName(),account.getMoney());
}
/**
* 更新账户
* @param account
*/
public void updateAccount(Account account){
super.getJdbcTemplate().update("update account set name = ?, money=? where id = ?",account.getName(),account.getMoney(),account.getId());
}
/**
* 删除账户
* @param id
*/
public void deleteAccount(Integer id){
super.getJdbcTemplate().update("delete from account where id = ?",id);
}
}
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 = "accountDao" class="com.hwh.dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/test"></property>
<property name="username" value="root"></property>
<property name="password" value=""></property>
</bean>
</beans>
Spring中事物控制的API
spring框架为我提供了一组事务控制的接口,该接口在spring-tx 的jar包中
- 事物的传播行为
REQUIRED一般为增删改用
SUPPORTS一般为查询
Spring中基于xml的声明式事务控制
- 配置步骤
1、配置事务管理器
2、配置事务的通知
此时我们需要导入事务的约束,tx名称空间和约束,同时也需要aop的
使用tx:advice标签配置事务通知
属性:
id:给事务通知起一个唯一标识
transaction-manager:给事务通知提供一个事务管理器引用
3、配置AOP中的通用切入表达式
4、建立事务通知和切入点表达式的对应关系
5、配置事务的属性,在tx:advice标签的内部进行配置
isolation:用于指定事务的隔离级别,默认值是DEFAULT,表示使用数据库的默认隔离级别
no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时事物回滚,没有默认值,任何异常都回滚。
propagation 用于指定事务的传播行为,默认值是REQUIRED,表示一定会有事物,若无事务创建一个事物,增删改的选择,查询方法可以选择SUPPORTS
read-only:用于指定事务是否只读,还有查询方法才会设为true,默认值为false,表示读写
rollback-for:用于指定一个异常,当产生该异常,事务回滚,产生其他异常时,事务不会滚,没有默认值,表示任何异常都回滚
timeout:用于指定事务的超时时间,默认值为-1,表示永不超时,如果指定了初值,以秒为单位
示例代码:
<!-- 配置事物管理器-->
<bean name="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事物通知-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"></tx:method>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="pc1" expression="execution(* com.hwh.service.impl.*.*(..))"/>
<!-- 建立事物通知和切入点表达式的对应关系-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc1"></aop:advisor>
</aop:config>
Spring中基于注解的声明式事务控制
- 配置步骤
1、配置事物管理器
<!-- 配置事物管理器-->
<bean name="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
2、开启spring对注解事务的支持
<tx:annotation-driven transaction-manager="txManager"></tx:annotation-driven>
3、在需要事务支持的地方使用@Transactional注解
@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
- 注意:当多个方法需要配置不同的事务属性时,建议使用xml方式更加方便
编程式事务控制
编程式事务使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。
使用事务模板类实现。可以对代码块进行事务控制,细粒度上优于声明式事务控制。
- 还是更加推荐使用声明式事务控制。
Spring5新特性
spring5是基于JDK8开发的,所以使用的jdk需要在1.8以上,tomcat版本要求在8.5以上