1. Spring5 框架概述 Spring 是轻量级的开源的 JavaEE 框架,也称 Spring Framework ;可以解决企业应用开发的复杂性。
Spring 两个核心部分:IOC 和 AOP
Spring 特点:(1)方便解耦,简化开发 (2)Aop 编程支持 (3)方便程序测试 (4)方便和其他框架进行整合 (5)方便进行事务操作 (6)降低 API 开发难度
Spring5 下载地址:https://repo.spring.io/ui/native/release/org/springframework/spring/
内容介绍:① IOC容器 ② AOP ③ JdbcTemplate ④ 事务管理 ⑤ Spring5 新特性
导入Spring5相关jar包:commons-logging-1.1.1.jar ;spring-beans-5.3.23.jar ;spring-context-5.3.23.jar ;spring-core-5.3.23.jar ;spring-expression-5.3.23.jar;
2. IOC 容器 控制反转,把对象创建和对象之间的调用过程,交给 Spring 进行管理 。
使用 IOC 目的:为了耦合度降低
底层原理:xml解析、工厂模式、反射
xml 配置文件中配置创建的对象,创建相关类的工厂类,在工厂类中解析xml文件,最后通过反射创建对象
IOC 容器实现 IOC 思想基于 IOC 容器完成,IOC 容器底层就是对象工厂;
Spring 提供 IOC 容器实现的两种方式(两个接口):
BeanFactory:IOC 容器基本实现,是 Spring 内部的使用接口,不提供开发人员进行使用
加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象
ApplicationContext:BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人员进行使用
加载配置文件时候就会把在配置文件对象进行创建。
对于服务器而言,应该在服务器启动时就将对象创建
ApplicationContext 的实现类:
其中 FileSystemXmlApplicationContext 使用系统路径(带盘符) ClassPathXmlApplicationContext 使用工程路径,默认是当前Module的 src 目录下
IOC操作 Bean 管理 Bean 管理指的是两个操作:Spring 创建对象;Spring 注入属性。
Bean 管理操作有两种方式:1. 基于 xml 配置文件方式实现;2. 基于注解方式实现
基于 xml 操作 创建对象 1 2 <bean id ="person" class ="Test.Person" > </bean >
在 spring 配置文件中,使用 bean 标签,标签里面添加对应属性,就可以实现对象创建
在 bean 标签有很多属性,介绍常用的属性
id 属性:唯一标识
class 属性:类全路径(包类路径)
创建对象时候,默认也是执行无参数构造方法完成对象创建
注入属性 DI:依赖注入,就是注入属性
方式一:使用对象的 set 方法注入
1 2 3 4 5 6 7 8 <bean id ="person" class ="Test.Person" > <property name ="name" value ="john" /> <property name ="id" value ="1" > </property > </bean >
方式二:使用带参构造器进行注入
1 2 3 4 <bean id ="person" class ="Test.Person" > <constructor-arg name ="name" value ="john" /> <constructor-arg name ="id" value ="1" > </constructor-arg > </bean >
p 名称空间注入(了解)
使用 p 名称空间注入,可以简化基于 xml 配置方式
在配置文件中添加 p 名称空间
1 2 3 4 <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" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" >
1 2 <bean name ="person" class ="Test.Person" p:name ="john" p:id ="1" />
注入特殊属性
字面量
1 2 3 4 5 6 7 8 9 10 <property name ="name" > <null /> </property > <property name ="address" > <value > <![CDATA[<<南京>>]]></value > </property >
注入属性 - 外部Bean
(1)创建两个类 service 类和 dao 类 (2)在 service 调用 dao 里面的方法
1 2 3 4 5 6 7 public class UserService { private UserDao userDao; public void setUserDao (UserDao userDao) { this .userDao = userDao; } public void add () { userDao.add(); } }
1 2 3 4 5 6 7 8 9 <bean id ="userService" class ="Test.UserService" > <property name ="userDao" ref ="userDaoImpl" /> </bean > <bean id ="userDaoImpl" class ="Test.UserDaoImpl" />
注入属性 - 内部Bean
1 2 3 4 5 6 7 8 9 10 11 <bean name ="person" class ="Test.Person" > <property name ="name" value ="john" /> <property name ="id" value ="1" /> <property name ="pet" > <bean class ="Test.Animals" p:name ="小黄" > <property name ="species" value ="Dog" /> </bean > </property > </bean >
注入属性 - 级联赋值
写法一(其实就是外部 Bean):
1 2 3 4 5 6 7 8 9 <bean id ="person" class ="Test.Person" > <property name ="name" value ="john" /> <property name ="id" value ="1" /> <property name ="pet" ref ="animals" /> </bean > <bean id ="animals" class ="Test.Animals" p:name ="小黄" > <property name ="species" value ="Dog" /> </bean >
写法二:
1 2 3 4 5 6 7 8 9 10 11 <bean id ="person" class ="Test.Person" > <property name ="name" value ="john" /> <property name ="id" value ="1" /> <property name ="pet" ref ="animals" /> <property name ="pet.name" value ="小白" /> <property name ="pet.species" value ="Dog" /> </bean > <bean id ="animals" class ="Test.Animals" p:name ="小黄" > <property name ="species" value ="Dog" /> </bean >
这种方式必须在 Person 类中存在 getPet() 方法才能使用,级联赋值会覆盖外部 Bean 注入的属性
注入集合属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class Stu { private String[] courses; private List<String> list; private Map<String,String> maps; private Set<String> sets; public void setSets (Set<String> sets) { this .sets = sets; } public void setCourses (String[] courses) { this .courses = courses; } public void setList (List<String> list) { this .list = list; } public void setMaps (Map<String, String> maps) { this .maps = maps; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <bean id ="stu" class ="Test.Stu" > <property name ="courses" > <array > <value > javaSE</value > <value > 数据结构</value > </array > </property > <property name ="list" > <list > <value > 张三</value > <value > 李四</value > </list > </property > <property name ="maps" > <map > <entry key ="name" value ="java" /> <entry key ="price" value ="15" /> </map > </property > <property name ="sets" > <set > <value > JDBC</value > <value > MySQL</value > </set > </property > </bean >
在集合里面设置对象类型值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <bean id ="course1" class ="com.atguigu.spring5.collectiontype.Course" > <property name ="cname" value ="Spring5 框架" > </property > </bean > <bean id ="course2" class ="com.atguigu.spring5.collectiontype.Course" > <property name ="cname" value ="MyBatis 框架" > </property > </bean > <property name ="courseList" > <list > <ref bean ="course1" > </ref > <ref bean ="course2" > </ref > </list > </property >
提取集合注入部分:
在 Spring 配置文件中引入名称空间 util
1 2 3 4 5 6 7 <?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:util ="http://www.springframework.org/schema/util" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd" >
使用 util 标签完成 list 集合注入提取
1 2 3 4 5 6 7 <util:list id ="books" > <value > Java从入门到精通</value > <value > 数据结构</value > </util:list > <bean id ="book" class ="Test.Book" > <property name ="list" ref ="books" /> </bean >
IOC容器实现:
1 2 3 4 ApplicationContext ac=new ClassPathXmlApplicationContext ("bean.xml" ); Book book = ac.getBean("book" , Book.class);System.out.println(book);
Bean 的作用域 在 Spring 中,设置创建的 Bean 默认情况是单实例对象
1 2 3 Book book1 = ac.getBean("book" , Book.class);Book book2 = ac.getBean("book" , Book.class);System.out.println(book1 == book2);
在默认情况下,book1 和 book2 地址相同,即指向同一个实例,
设置单实例或多实例:
在 spring 配置文件的 bean 标签中,属性(scope)用于设置单实例还是多实例
singleton,默认值,表示单实例对象
prototype,表示多实例对象
设置 scope 值是 singleton ,加载 spring 配置文件时就会创建单实例对象;设置 scope 值是 prototype ,在调用 getBean 方法时才创建多实例对象
1 2 3 4 <bean id ="book" class ="Test.Book" scope ="prototype" > <property name ="list" ref ="books" /> </bean >
Bean 的生命周期 生命周期:从对象创建到对象销毁的过程
通过构造器创建 bean 实例(无参数构造)
为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
调用 bean 的初始化的方法(需要进行配置初始化的方法)
使用 bean(对象获取到了)
当容器关闭时,调用 bean 的销毁的方法(需要进行配置销毁的方法)
1 2 3 4 <bean id ="book" class ="Test.Book" init-method ="initMethod" destroy-method ="destroyMethod" > <property name ="oname" value ="手机" > </property > </bean >
添加 Bean 的后置处理器后,Bean 的生命周期有七步:
通过构造器创建 bean 实例(无参数构造)
为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization
调用 bean 的初始化的方法(需要进行配置初始化的方法)
把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
使用 bean(对象获取到了)
当容器关闭时,调用 bean 的销毁的方法(需要进行配置销毁的方法)
添加后置处理器
创建类,实现接口 BeanPostProcessor,创建后置处理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class MyBeanPost implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization (Object bean, String beanName) throws BeansException { System.out.println("在初始化之前执行的方法" ); return bean; } @Override public Object postProcessAfterInitialization (Object bean, String beanName) throws BeansException { System.out.println("在初始化之后执行的方法" ); return bean; } }
配置后置处理器
1 2 <bean id ="myBeanPost" class ="Test.MyBeanPost" > </bean >
xml自动装配 根据指定装配规则(属性名称或者属性类型),Spring 自动将匹配的属性值进行注入
bean 标签属性 autowire,配置自动装配
1 2 3 4 5 6 <bean id ="person" class ="Test.Person" autowire ="byName" /> <bean id ="work" class ="Test.Work" /> <bean id ="person" class ="Test.Person" autowire ="byType" /> <bean id ="animal" class ="Test.Animals" />
byName :根据属性名称注入 ,注入值 bean 的 id 值必须与类的属性名称相同
外部属性文件导入 直接配置数据库信息:
1 2 3 4 5 6 7 <bean id ="dataSource" class ="com.alibaba.druid.pool.DruidDataSource" > <property name ="driverClassName" value ="com.mysql.cj.jdbc.Driver" > </property > <property name ="url" value ="jdbc:mysql:///test" > </property > <property name ="username" value ="root" > </property > <property name ="password" value ="123456" > </property > </bean >
引入外部属性文件配置数据库连接池:
需要引入 context 名称空间
1 2 3 4 5 6 7 8 9 <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" >
在 spring 配置文件使用标签引入外部属性文件:
1 2 3 4 5 6 7 8 9 <context:property-placeholder location ="classpath:jdbc.properties" /> <bean id ="dataSource" class ="com.alibaba.druid.pool.DruidDataSource" > <property name ="driverClassName" value ="${driverClass}" > </property > <property name ="url" value ="${url}" > </property > <property name ="username" value ="${userName}" > </property > <property name ="password" value ="${password}" > </property > </bean >
基于注解操作 注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值..);使用注解操作可以简化xml配置。
Spring 针对 Bean 管理中创建对象提供了四种注解:
@Component
@Service :一般用在业务层(Service层)
@Controller :一般用在表现层(Servlet层)
@Repository :一般用在持久层(DAO层)
作用:把当前类对象存入Spring容器中 属性:value 用于指定bean的id,不写默认为首字母小写的当前类名 每种注解都可以随意使用,但spring框架为我们提供明确的三层使用的注解,能使我们的三层对象更加清晰
创建对象
引入依赖 spring-aop-5.3.23.jar
开启组件扫描
1 2 3 4 5 <context:component-scan base-package ="Test" > </context:component-scan >
创建类,在类上面添加创建对象注解
1 2 @Component public class UserDaoImpl implements UserDao {}
开启组件扫描的细节配置:
1 2 3 4 <context:component-scan base-package ="com.atguigu" use-default-filters ="false" > <context:include-filter type ="annotation" expression ="org.springframework.stereotype.Controller" /> </context:component-scan >
其中 use-default-filters=”false” 表示现在不使用默认 filter,自己配置 filter;context:include-filter ,表示设置扫描哪些内容
1 2 3 4 5 <context:component-scan base-package ="com.atguigu" > <context:exclude-filter type ="annotation" expression ="org.springframework.stereotype.Controller" /> </context:component-scan >
默认filter扫描四种注解,context:exclude-filter: 设置哪些内容不进行扫描
注入属性
@Autowired:先根据属性类型进行自动装配,没有再按名字寻找
1 2 3 4 5 @Service public class UserService { @Autowired private UserDao userDao; }
使用注解方式注入属性不需要 set 方法,注解内部都封装好了
@Qualifier:根据名称进行注入
@Qualifier 需要和 @Autowired 一起使用,应对一个接口存在多个实现类的情况
1 2 3 4 5 6 @Service public class UserService { @Autowired @Qualifier(value="userDaoImpl1") private UserDao userDao; }
@Resource:先按照名字寻找,没有再按类型查找
1 2 3 4 5 6 @Service public class UserService { @Resource(name = "userDaoImpl1") private UserDao userDao; }
官方不建议用@Resource,因为它是 javax (Java拓展包)的内容
@Value:注入普通类型属性
1 2 @Value(value = "abc") private String name;
完全注解开发 创建配置类,替代 xml 配置文件
1 2 3 4 5 6 7 8 @Configuration @ComponentScan(basePackages = {"Test"}) public class SpringConfig { @Bean public UserService getUserService () { return new UserServiceImpl (); } }
Spring 通过调用标记了@Bean 注解的方法将对象放入 IOC 容器,不会重复调用方法。原因是 Spring 想要获取 bean 对应的实例对象时会查看 IOC 容器中是否已经有了这个对象,如果有则不会执行这个方法,从而保证这个 bean 是单实例的。
可以配合@Scope 注解修改为多实例。
1 2 3 4 5 6 7 @Test public void testService2 () { ApplicationContext ac = new AnnotationConfigApplicationContext (SpringConfig.class); UserService us = ac.getBean("userService" , UserService.class); us.add(); }
3. AOP 面向切面(方面)编程,利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
描述:不修改源代码的情况下,在主干功能里面添加新功能
底层原理:使用动态代理,分为两种情况
被代理类有接口,使用JDK动态代理 (JavaSE.反射机制)
创建接口实现类的代理对象,增强类的方法,具体见JavaSE中介绍
被代理类没有接口,使用CGLIB动态代理
CGLIB 通过动态生成一个需要被代理类的子类(即被代理类作为父类),该子类重写被代理类的所有不是 final 修饰的方法,并在子类中采用方法拦截的技术拦截父类所有的方法调用,进而织入横切逻辑。此外,因为 CGLIB 采用整型变量建立了方法索引,这比使用 JDK 动态代理更快。
区别:
JDK 动态代理只能对接口进行代理,不能对普通的类进行代理,这是因为 JDK 动态代理生成的代理类,其父类是 Proxy
,且 Java 不支持类的多继承。
CGLIB 能够代理接口和普通的类,但是被代理的类不能被 final
修饰,且接口中的方法不能使用 final
修饰。
JDK 动态代理使用 Java 反射技术进行操作,在生成类上更高效。
CGLIB 使用 ASM 框架直接对字节码进行修改,使用了 FastClass
的特性。在某些情况下,类的方法执行会比较高效。
AOP术语:
连接点:类中可以被增强的方法都称为连接点
切入点:真正被增强的方法称为切入点
通知(增强):实际增强的逻辑部分称为通知(增强)
通知有多种类型:前置通知、后置通知、环绕通知、异常通知、最终通知
切面:把通知应用到切入点的动作称为切面
AOP操作 Spring 框架一般都是基于 AspectJ 实现AOP操作;AspectJ 不是Spring组成部分,是独立的AOP框架,一般把 AspectJ 和 Spring 框架一起使用。
引入AOP相关依赖:
切入点表达式:
作用:指明对哪个类里面的哪个方法进行增强
语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )
示例:
1 2 3 4 5 6 举例 1:对 dao.BookDao 类里面的 add 进行增强 execution(* dao.BookDao.add(..)) 举例 2:对 dao.BookDao 类里面的所有的方法进行增强 execution(* dao.BookDao.* (..)) 举例 3:对 dao 包里面所有类,类里面所有方法进行增强 execution(* dao.*.* (..))
其中开头的 * 表示任意权限修饰符;返回类型可以省略,以空格代替
AspectJ 配置文件 注入名称空间 aop
1 2 3 4 5 6 7 8 9 <?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 http://www.springframework.org/schema/aop/spring-aop.xsd" >
在Spring配置文件中创建增强类和被增强类的对象,并在配置文件中配置切入点:
1 2 3 4 5 6 7 8 9 10 11 12 <bean id ="book" class ="Test.Book" > </bean > <bean id ="bookProxy" class ="Test.BookProxy" > </bean > <aop:config > <aop:pointcut id ="p" expression ="execution(* Test.Book.buy(..))" /> <aop:aspect ref ="bookProxy" > <aop:before method ="beforeBuy" pointcut-ref ="p" /> </aop:aspect > </aop:config >
AspectJ 注解
在Spring配置文件中开启注解扫描(需要注入context 和 aop两个名称空间)
1 2 3 4 5 6 7 8 9 10 11 12 13 <?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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd" > <context:component-scan base-package ="Test" > </context:component-scan >
使用注解创建增强类和被增强类的对象
增强类添加 @Aspect 注解
1 2 3 4 5 @Component public class Book {@Component @Aspect public class BookProxy {
在 spring 配置文件中开启生成代理对象
1 <aop:aspectj-autoproxy > </aop:aspectj-autoproxy >
在增强类中作为通知方法的上面使用切入点表达式配置通知类型注解
1 2 3 4 @Before(value="execution(* Test.Book.buy(..))") public void beforeBuy () { System.out.println("beforeBuy..." ); }
@Before :表示作为前置通知
@AfterReturning :表示后置通知(返回通知)
@After :表示最终通知,无论是否出现异常都会被执行
@AfterThrowing :表示异常通知,当程序发生异常才会执行
@Around :表示环绕通知
1 2 3 4 5 6 7 8 @Around(value = "execution(* Test.Book.buy(..))") public void aroundBuy (ProceedingJoinPoint pj) throws Throwable { System.out.println("环绕之前........." ); pj.proceed(); System.out.println("环绕之后........." ); }
环绕通知的执行包含前置通知和后置通知,即分别在前置通知之前和后置通知之后执行。
1 2 3 4 5 6 @Test public void test1 () { ApplicationContext ac= new ClassPathXmlApplicationContext ("bean1.xml" ); Book book = ac.getBean("book" , Book.class); book.buy(); }
相同切入点抽取:
1 2 3 4 5 6 7 8 @Pointcut(value = "execution(* Test.Book.buy(..))") public void demo () {} @Before(value = "demo()") public void beforeBuy () { System.out.println("beforeBuy..." ); }
设置增强类的优先级
多个增强类对同一个方法进行增强时可以设置执行顺序。
在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高(最小为0)
1 2 3 4 @Component @Aspect @Order(1) public class BookProxy {}
完全注解开发
创建配置类,不需要创建 xml 配置文件
1 2 3 4 5 @Configuration @ComponentScan(basePackages = {"Test"}) @EnableAspectJAutoProxy(proxyTargetClass = true) public class ConfigAop {}
其中 proxyTargetClass 用于切换jdk代理和cglib代理;默认为false,有接口实现的是jdk的动态代理,没有接口的是cglib代理,设置为true生成的都是cglib的代理,
1 2 3 4 5 6 7 @Test public void test2 () { ApplicationContext context = new AnnotationConfigApplicationContext (ConfigAop.class); Book book = ac.getBean("book" , Book.class); book.buy(); }
4. JdbcTemplate Spring 框架对 JDBC 进行封装,使用 JdbcTemplate 方便实现对数据库操作
引入依赖:spring-jdbc-5.3.23.jar ;spring-orm-5.3.23.jar ;spring-tx-5.3.23.jar
在Spring文件中配置数据库连接池
在Spring中,数据库配置文件使用 username 命名,程序运行后,windows的用户名会覆盖数据库的用户名,会造成数据库连接错误
1 2 3 4 5 6 7 8 9 10 11 12 <context:property-placeholder location ="jdbc.properties" /> <context:component-scan base-package ="aTest,Test3" /> <bean id ="dataSource" class ="com.alibaba.druid.pool.DruidDataSource" destroy-method ="close" > <property name ="driverClassName" value ="${driverClassName}" /> <property name ="url" value ="${url}" /> <property name ="username" value ="${name}" /> <property name ="password" value ="${password}" /> <property name ="initialSize" value ="${initialSize}" /> <property name ="maxActive" value ="${maxActive}" /> </bean >
配置 JdbcTemplate 对象,注入 DataSource
1 2 3 4 5 <bean id ="jdbcTemplate" class ="org.springframework.jdbc.core.JdbcTemplate" > <property name ="dataSource" ref ="dataSource" > </property > </bean >
在 dao 注入 jdbcTemplate 对象
1 2 3 4 5 6 @Repository public class BookDaoImpl implements BookDao { @Autowired private JdbcTemplate jt; ... }
在service注入dao对象
调用 JdbcTemplate 对象中的方法实现功能
增删改查
update(String sql, Object…args)
该方法可以实现增删改功能:
1 2 3 4 5 6 @Override public void updateBook (Book book) { String sql="update book set name=?,price=? where id=?" ; int update = jt.update(sql, book.getName(), book.getPrice(),book.getId()); System.out.println(update); }
queryForObject(String sql, Class<T> requiredType)
第二个参数:返回类型 Class
该方法可以实现例如查询记录数等功能:
1 2 3 4 5 6 @Override public int countAll () { String sql="select count(*) from book" ; Integer count = jt.queryForObject(sql, Integer.class); return count; }
queryForObject(String sql, RowMapper<T> rowMapper, Object…args)
第二个参数:RowMapper 是接口,针对返回不同类型数据,使用这个接口里面实现类完成数据封装
该方法可以实现单个JavaBean对象的查询:
1 2 3 4 5 @Override public Book findBook (int id) { String sql="select * from book where id=?" ; return jt.queryForObject(sql, new BeanPropertyRowMapper <>(Book.class), id); }
query(String sql, RowMapper<T> roMapper, Object…args)
该方法可以实现分页查询等功能:
1 2 3 4 5 @Override public List<Book> findAllBook () { String sql="select * from book" ; return jt.query(sql,new BeanPropertyRowMapper <>(Book.class)); }
批量操作
batchUpdate(String sql, List<Object[]> batchArgs)
第二个参数:List 集合,添加多条记录数据
该方法可以实现批量添加、删除、修改
1 2 3 4 5 @Override public void batchUpdate (List<Object[]> args) { String sql="update book set name=?,price=? where id=?" ; jt.batchUpdate(sql, args); }
这个方法返回一个 int[] ,但是返回值有特定含义,例如 -2 表示操作成功但不能计数
5. 事务操作 事务是数据库操作最基本单元。(在JDBC.概述有详细介绍)
事务的四个特性(ACID):原子性;一致性;隔离性;持久性。
经典例子:转账
Spring事务管理 事务一般在Service层(业务逻辑层)添加。
在Spring进行事务管理有两种方式:编程式事务管理和声明式事务管理
编程式事务管理即以往学习的五个步骤:1. 开启事务(关闭自动提交)2. 进行业务操作 3. 没有异常,提交事务;出现异常,回滚事务
声明式事务管理 基于xml 配置文件
配置事务管理器
配置通知
配置切入点和切面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <bean id ="transactionManager" class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name ="dataSource" ref ="dataSource" /> </bean > <tx:advice id ="txadvice" > <tx:attributes > <tx:method name ="save*" propagation ="REQUIRED" /> </tx:attributes > </tx:advice > <aop:config > <aop:pointcut id ="pt" expression ="execution(* Test.UserService.*(..))" /> <aop:advisor advice-ref ="txadvice" pointcut-ref ="pt" /> </aop:config >
事务管理参数配置
propagation:设置事务传播行为
当一个事务方法被其它事务方法调用时,这个事务方法如何进行
事务的传播行为可以由传播属性指定。Spring定义了7种传播行为。
传播属性
描述
REQUIRED
如果有事务在运行,当前的方法就在这个事务内运行,否则,就启动一个新的事务,并在自己的事务内运行
REQUIRED_NEW
当前的方法必须启动新事务,并在它自己的事务内运行,如果有事务正在运行,应该将它挂起
SUPPORTS
如果有事务在运行,当前的方法就在这个事务内运行,否额它可以不运行在事务中
NOT_SUPPORTED
当前的方法不应该运行在事务中,如果有运行的事务,将他挂起
MANDATORY
当前的方法必须运行在事务内部,如果没有正在运行的事务,就抛出异常
NEVER
当前的方法不应该运行在事务中,如果有运行的事务,就抛出异常
NESTED
如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则,就启动一个新的事务,并在它自己的事务内运行
REQUIRED:在事务A中调用方法B,方法B会加入到事务A中,合并成一个事务,互相影响(事务A或方法B若出现异常,全部回滚)
REQUIRED_NEW:在事务A中调用方法B,方法B会开启内层事务B,互不影响(两个事务谁出异常谁回滚,正常运行的会提交)
ioslation:设值事务隔离级别
三个读问题:脏读、不可重复读、虚(幻)读。(在JDBC.概述有详细介绍)
Spring可以设置四种隔离级别:
脏读
不可重复读
幻读
READ UNCOMMITTED(读未提交数据)
有
有
有
READ COMMITTED(读已提交数据)
无
有
有
REPEATABLE READ(可重复读)
无
无
有
SERIALIZABLE(串行化)
无
无
无
timeout:超时时间
事务需要在一定时间内进行提交,如果不提交进行回滚。
默认值是 -1 ,即无超时时间。设置时间以秒单位进行计算
readOnly:是否只读
读:查询操作,写:添加修改删除操作
readOnly 默认值 false,表示可以增删改查操作,设置成 true 之后,只能查询
rollbackFor:回滚
设置出现哪些异常进行事务回滚。默认运行时异常回滚,建议有异常都回滚
noRollbackFor:不回滚
设置出现哪些异常不进行事务回滚
基于注解方式
在Spring配置文件配置事务管理器
1 2 3 4 5 6 <bean id ="transactionManager" class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name ="dataSource" ref ="dataSource" /> </bean >
在Spring配置文件引入名称空间并中开启事务注解
1 2 <tx:annotation-driven transaction-manager ="transactionManager" />
在 service 类上面(或者 service 类的方法上面)添加事务注解
如果添加在类上面,这个类里面所有的方法都添加事务;添加方法上面,为这个方法添加事务。
1 2 3 4 5 6 7 @Transactional(propagation = Propagation.REQUIRED) public void change () { bd.decline(); int i=12 /0 ; bd.increase(); }
完全注解声明式 创建配置类,使用配置类替代 xml 配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 @Configuration @ComponentScan(basePackages = "Test3") @EnableTransactionManagement public class Config { @Bean public DataSource getDruidDataSource () throws Exception { DruidDataSource dataSource = new DruidDataSource (); InputStream is = new FileInputStream ("src/jdbc.properties" ); Properties ps = new Properties (); ps.load(is); dataSource.setDriverClassName(ps.getProperty("driverClassName" )); dataSource.setUrl(ps.getProperty("url" )); dataSource.setUsername(ps.getProperty("name" )); dataSource.setPassword(ps.getProperty("password" )); dataSource.setInitialSize(5 ); dataSource.setMaxActive(10 ); is.close(); return dataSource; } @Bean public JdbcTemplate getJdbcTemplate (DataSource dataSource) { JdbcTemplate jdbcTemplate = new JdbcTemplate (); jdbcTemplate.setDataSource(dataSource); return jdbcTemplate; } @Bean public DataSourceTransactionManager getDataSourceTransactionManager (DataSource dataSource) { DataSourceTransactionManager transactionManager = new DataSourceTransactionManager (); transactionManager.setDataSource(dataSource); return transactionManager; } }
1 2 3 4 5 6 @Test public void test3 () { ApplicationContext ac=new AnnotationConfigApplicationContext (Config.class); BookService bs = ac.getBean("bookService" , BookService.class); bs.change(); }
同一个测试方法中不能既使用配置文件,又使用全注解,比如配置文件中创建了 JdbcTemplate 的Bean对象,又在配置类中创建了,注入时会报错(不能分辨注入哪一个)
6. Spring5框架新功能 Spring5 框架的代码基于 Java8,运行时兼容 JDK9,删除了许多不建议使用的类和方法
日志封装 Spring 5.0 框架自带了通用的日志封装。移除了 Log4jConfigListener,官方建议使用 Log4j2
引入jar包:log4j-api-2.11.2.jar ;log4j-core-2.11.2.jar ;log4j-slf4j-impl-2.11.2.jar ;slf4j-api-1.7.30.jar ;
创建 log4j2.xml 配置文件(名字固定)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?xml version="1.0" encoding="UTF-8" ?> <configuration status ="INFO" > <appenders > <console name ="Console" target ="SYSTEM_OUT" > <PatternLayout pattern ="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" /> </console > </appenders > <loggers > <root level ="info" > <appender-ref ref ="Console" /> </root > </loggers > </configuration >
@Nullable 注解 Spring5 框架核心容器支持@Nullable 注解;可以使用在方法、属性或参数上面
注解用在方法上面,表示方法返回值可以为空
1 2 @Nullable public String getId () ;
注解使用在方法参数里面,表示方法参数可以为空
1 public void change (@Nullable String name,int id)
注解使用在属性上面,表示属性值可以为空
1 2 @Nullable private String name;
函数式风格 Spring5 核心容器支持函数式风格 GenericApplicationContext
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Test public void testGenericApplicationContext () { GenericApplicationContext context = new GenericApplicationContext (); context.refresh(); context.registerBean("book1" ,Book.class, Book::new ); Book book = (Book)context.getBean("book1" ); System.out.println(book); }
registerBean()方法中有三个参数,第一个是bean的别名,最后一个是Supplier接口,这两个参数都有@Nullable注解
测试整合 整合 JUnit4,需要导入jar包
使用注解方式创建测试类:
1 2 3 4 5 6 7 8 9 10 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:bean1.xml") public class JTest4 { @Autowired private UserService userService; @Test public void test1 () { userService.accountMoney(); } }
Spring5 支持整合 JUnit5,需要引入jar 包
使用注解创建测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:bean1.xml") public class JTest5 { @Autowired private UserService userService; @Test public void test1 () { userService.accountMoney(); } } @SpringJUnitConfig(locations = "classpath:bean1.xml") public class JTest5 { @Autowired private UserService userService; @Test public void test1 () { userService.accountMoney(); } }
Webflux 点击此处接着学习