什么是Spring?
Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring的核心是控制反转(IoC)和面向切面(AOP)。简单来说,Spring是一个分层的JavaSE/EEfull-stack(一站式) 轻量级开源框架
什么是IOC?
1、IOC思想是基于IOC容器来完成,IOC容易底层就是对象工程
Spring提供IOC容器实现的两种方式:
①: BeanFactory是IOC容器基本实现,是Spring使用的内部接口,不建议开发人员使用
* 加载配置文件的时候不会创建对象,直到获取对象的时候才会去创建对象
②: ApplicationContext接口, BeanFactory的子接口,提供更多强大的功能,供开发人员使用
* 加载配置文件时就会把配置文件里的对象进行创建
ApplicationContext接口的实现类:
ClassPathXmlApplicationContext与FileSystemXmlApplicationContext实现类的区别
1、ClassPathXmlApplicationContext类是以当前的src下的路径查找配置文件
2、FileSystemXmlApplicationContext类是以计算机的磁盘下的路径查找配置文件
小结: 开发时一般我们都使用ApplicationContext类,因为我们希望在程序运行时就把一些需要使用的对象都创建好
1、IOC操作Bean管理 - 基于xml配置
①、在Spring的xml配置中,使用bean标签来对类进行配置,其中bean标签内常用的属性有:
- id属性: 唯一标识,相当于给类创建别名
- class属性: 提供对象的全类名(相当于类的地址)
- name属性: 与id属性相同,唯一不同的是他可以添加特殊符号
通过bean标签创建对象
<!--配置Book的对象创建-->
<bean id="user" class="com.atguigu.bean.User"></bean>
②、IOC基于xml创建对象时使用的是无参构成器,假设类中只有带参构造器而没无参构造器,创建过程中会报错
2、IOC基于xml方式注入属性
DI: 依赖注入、注入属性,(dependency injection)依赖注入基于对象的创建方式实现
第一种注入的方式: 使用set方法进行注入
2.1、创建对象,定义属性和对应的set方法
package com.atguigu.bean;
/**
* @author gdcho
* @create 09-26-20:15
*/
public class Book {
private Integer id;
private String bookName;
private String author;
public Book(Integer id, String bookName, String author) {
this.id = id;
this.bookName = bookName;
this.author = author;
}
public Book() {}
public Integer getId() {return id;}
public void setId(Integer id) {this.id = id;}
public String getBookName() {return bookName;}
public void setBookName(String bookName) {this.bookName = bookName;}
public String getAuthor() {return author;}
public void setAuthor(String author) {this.author = author;}
}
2.2、在spring的xml配置中,配置bean标签配置创建对象,再在bean标签内部配置property注入属性
<!--1.配置Book的对象创建-->
<bean id="book" class="com.atguigu.bean.Book" >
<!--2.配置属性注入-->
<property name="id" value="5"></property>
</bean>
2.3、使用p名称空间可以简化xml配置属性注入的方式
①、添加p名称空间在xml配置文件中
xmlns:p="http://www.springframework.org/schema/p"
②、进行属性注入,在bean标签里面进行操作
<!--1.配置Book的对象创建-->
<bean id="book" class="com.atguigu.bean.Book">
<!--2.配置属性注入-->
<property name="id" value="5"></property>
</bean>
③、第二总注入方式: 使用有参构造器进行注入
<!--1.配置Book的对象创建-->
<bean id="book" class="com.atguigu.bean.Book">
<!--配置有参构造器注入属性-->
<constructor-arg name="author" value="gghhy66"></constructor-arg>
</bean>
2.4、在spring中的xml配置属性注入null值、特殊符号的值
①、属性中注入null值的方式,在property标签内创建null标签
<!--实现注入属性值null-->
<bean id="book2" class="com.atguigu.bean.Book">
<property name="bookName">
<null></null>
</property>
</bean>
②、属性中注入特殊字符
注入方式1: 把特殊符号转义
<bean id="book3" class="com.atguigu.bean.Book">
<property name="bookName" value="<<活着>>"></property>
</bean>
注入方式2: 把带特殊符号内容写到 <![CDATA[内容]]>
<bean id="book4" class="com.atguigu.bean.Book">
<property name="bookName" >
<value><![CDATA[<<活着>>]]></value>
</property>
</bean>
3、使用xml配置注入bean对象
3.1、首先使用spring中xml配置创建对象,与被注入的对象
3.2、在要注入属性的bean标签内创建property标签,内部使用ref属性引入被创建对象的别名
方式1: 在property标签内使用ref属性引入被创建对象的id
<!--创建要注入属性的对象-->
<bean id="userService" class="com.atguigu.service.UserService">
<!--ref属性注入Bean对象类型的id值-->
<property name="userDao" ref="userDao"></property>
</bean>
<!--被注入Bean对象类型的创建-->
<bean id="userDao" class="com.atguigu.dao.UserDaoImpl"></bean>
方式2: 使用xml配置文件注入属性 - 级联赋值
- 在xml配置文件内使用bean标签创建对象
- 在bean标签中创建property标签注入属性
- 在需要使用bean对象的属性内property标签内创建新的对象
<bean id="emp" class="com.atguigu.bean.Employee">
<!--在属性注入内部创建需要注入的Bean对象-->
<property name="manager">
<bean id="manger" class="com.atguigu.bean.Manager"></bean>
</property>
</bean>
小结: ref属性: 是property标签内的注入bean对象的属性
4、使用xml配置注入属性 - 注入集合类型
4.1、在xml配置中使用bean标签创建对象
4.2、在bean标签内使用property标签,name属性获取属性名
4.3、property内部使用各个集合的标签并且使用value标签添加元素
<bean id="stu" class="com.atguigu.collection.Stu">
<!--数组类型属性注入-->
<property name="courses">
<array>
<value>123</value>
<value>我是谁</value>
</array>
</property>
<!--list类型属性注入-->
<property name="list">
<list>
<value>小明</value>
<value>大明</value>
</list>
</property>
<!--map类型属性注入-->
<property name="maps">
<map>
<entry key="key1" value="value1"></entry>
<entry key="key2" value="value2"></entry>
</map>
</property>
<!--set类型属性注入-->
<property name="sets">
<set>
<value>set1</value>
<value>set2</value>
</set>
</property>
</bean>
4.4、集合类型属性注入 - 集合里的值为对象类型
<bean>
<!--list类型属性注入, 值为对象类型-->
<property name="listCourse">
<list>
<ref bean="course1"></ref>
<ref bean="course2"></ref>
</list>
</property>
</bean>
<!--创建要注入的list的元素对象-->
<bean id="course1" class="com.atguigu.collection.Course">
<property name="course" value="Java"></property>
</bean>
<bean id="course2" class="com.atguigu.collection.Course">
<property name="course" value="MyBatis"></property>
</bean>
4.5、集合类型属性注入 - 把集合提取出来成为公共的标签
①、xml配置文件中引入util命名空间
<?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: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 http://www.springframework.org/schema/util/spring-util.xsd">
②、使用<util:list id="list">
将集合类型抽取为公共的标签
<bean id="stu" class="com.atguigu.collection.Stu">
<!--属性注入这里引用公共的集合id-->
<property name="list" ref="list"></property>
</bean>
<!--将list抽取为公共的标签-->
<util:list id="list">
<value>123</value>
<value>abc456</value>
</util:list>
5、操作Bean管理 - FactoryBean工厂bean
Spring有两种Bean类,一种是普通Bean类,另一种是工厂Bean
1、普通Bean: 在配置文件中的类型就是返回值类型
2、工厂Bean: 在配置文件中的类型可以和返回值类型不一样
工厂Bean的创建方式:
①、创建工厂bean对象,实现接口FactoryBean泛型为指定为要创建的bean对象
②、实现接口里面的方法,在方法里面定义返回值类型
//泛型决定了要返回的对象
public class MyBean implements FactoryBean<Book> {
@Override
public Book getObject() throws Exception {
Book book = new Book(10,"活着","余华");
return book;
}
}
③、在配置文件中配置创建FactoryBean的类
<bean id="myBean" class="com.atguigu.bean.MyBean">
6、 在Spring中设置创建bean实例是单实例还是多实例
6.1、默认情况下,bean创建后为单实例
验证: 通过相同的配置文件创建两个Bean对象,输出后地址为相同值
6.2、如何在spring中配置文件中创建对象是单例还是多例
答: bean标签中有个属性scope,它有两个常用的值singleton设置单例(默认),prototype设置多例
singleton与prototype的区别
1)、single为单实例、prototype为多实例
2)、xml配置创建bean对象默认是单实例,而Bean对象随着配置的加载而创建
3)、xml配置创建bean对象设置scope属性为prototype时,它不会随着配置文件的加载而创建,而是在调用getBean方法时候创建
7、通过IOC创建的bean对象生命周期
①、通过构造器创建bean实例
②、调用初始化方法, 需要配置xml文件的bean标签
③、为bean属性设置值和对其它bean进行引用
④、获取到实例对象可以使用
⑤、容器关闭后,手动销毁容器, 需要配置xml文件的bean标签
第2、5步需要配置标签
配置文件
<bean id="person" class="com.atguigu.bean.Person" init-method="initMethod" destroy-method="destroyMethod">
<property name="name" value="Roy"></property>
<property name="age" value="88"></property>
</bean>
bean对象完成,手动销毁ioc容器
@Test
public void testBean2(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean6.xml");
Person person = context.getBean("person", Person.class);
System.out.println("第四步 实例化bean对象成功");
//手动销毁容器
context.close();
}
测试结果
8、操作Bean管理 - 完全基于注解bean对象创建
8.1、什么是注解?
1、注解是代码的特殊标记, 格式: @注解名(属性名=属性值,属性名=属性值,…)
2、注解可以使用在的地方: 类上、方法名上、属性上
3、使用注解的目的: 简化spring的xml配置
8.2、Spring针对Bean管理创建对象提供的注解
@Component 使用在视图层
@Service 使用在业务层
@Controller 使用在控制层
@Repository 使用在持久层
* 上面4个注解的功能是一样的,都可以用来创建Bean对象,在不同的层中用不同的注解以区分
8.3、通过注解的方式创建Bean对象的步骤
1、引入需要依赖的包
aop、beans、context、core、expression、logging这些jar包
2、创建xml配置文件,引入名称空间: context、开启组件扫描:<context:component-scan base-package="com.atguigu">
组件扫描如果要扫描多个包,用逗号相隔
<!--base-package属性: 扫描包下所有有创建bean对象的注解-->
<context:component-scan base-package="com.atguigu.dao"></context:component-scan>
3、创建类,并在类名上方添加Bean管理的注解
类上添加的注解可以不用写,默认类名称是把类名首字母小写
8.4、IOC操作Bean管理 - 基于注解方式实现属性注入
1、@Autowired 根据属性类型自动注入,添加到需要实现注入的属性名上
2、@Qualifier 与@Autowired注解搭配使用,如果自动注入类型对象有多个,@Qualifier注解选定其中一个
3、@Resource 可以根据属性类型注入,可以根据名称注入
4、@Value 能够注入普通类型属性
//创建bean对象的注解
@Service
public class UserService {
//根据类型注入属性
@Autowired
private UserDao userDao;
//根据类型与类名注入属性
@Autowired
@Qualifier("userDaoImpl")
private UserDao userDao;
//根据类型注入属性
@Resource
private UserDao userDao1;
//根据类名注入属性
@Resource(name="userDaoImpl2")
private UserDao userDao2;
//注入普通类型
@Value("Monica")
private String username;
}
8.5 IOC操作Bean管理 - 完全基于注解的Spring开发
1、创建一个类,作为配置类
2、在类名上添加@Configuration注解
3、在类名上添加@ComponentScan注解,扫描哪些包要创建对象
@Configuration
@ComponentScan(basePackages = {"com.atguigu.dao","com.atguigu.service"})
public class SpringConfig {
}
4、创建普通类,并在类名上添加创建对象的注解与属性注入注解
@Component("human")
public class Human {
@Value("Roy")
private String name;
@Value("22")
private Integer age;
@Value("180")
private Integer height;
public void setName(String name) {this.name = name;}
public void setAge(Integer age){this.age = age;}
public void setHeight(Integer height) {this.height = height;}
public void getInfo(){
System.out.println("name = " + name + ", age = " + age + ", height = " + height);
}
}
5、测试完全通过注解创建bean
@Test
public void test3(){
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
Human human = context.getBean("human", Human.class);
human.getInfo();
}
AOP是什么?
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方
式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个
热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑
的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高
了开发的效率
1、AOP操作 - 准备工作
1.1、Spring 框架一般都是基于 AspectJ 实现 AOP 操作
(1)AspectJ不是Spring组成部分,独立AOP框架,一般把AspectJ和Spring框架一起使用进行AOP操作
(2)基于AspectJ实现AOP操作的两种实现
①、基于xml配置的方式实现
②、基于注解方式实现
(3)在项目中导入AOP相关依赖
(4)切入点表达式: 知道对那个方法进行增强操作
切入点表达式格式: execution([权限修饰符][返回值类型][全类名][方法名](参数列表))
1.2、 AOP的术语
1、连接点: 类中实际可以增强的方法称为连接点
2、切入点: 类中实际被增强的方法称为切入点
3、通知(增强):
①、实际增强的逻辑部分称为通知
②、通知的种类: 前置通知、后置通知、环绕通知、异常通知、最终通知
4、切面: 把通知应用到切入点的过程
以下是五种通知的注解使用
@Repository //创建对象的注解
@Aspect //设置代理对象的注释
public class UserProxy {
//创建一个方法,方法上添加公共的切入点注解,作为增强方法上放的注解
@Pointcut("execution(* com.atguigu.service.User.add(..))")
public void pointdemo(){
}
//在增强类里面,作为通知方法上面添加通知类型注解,使用切入点表达式进行配置
//前置通知
@Before("pointdemo()")
public void before(){
System.out.println("before......");
}
//前置通知
@AfterReturning("pointdemo()")
public void afterReturning(){
System.out.println("before......");
}
//最终通知
@After("pointdemo()")
public void after(){
System.out.println("after......");
}
//环绕通知
@Around("pointdemo()")
public void surround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("Surround......Surround");
proceedingJoinPoint.proceed();
System.out.println("SurroundAfter......SurroundAfter");
}
//异常通知
@AfterThrowing("pointdemo()")
public void exception(){
System.out.println("AfterThrowing......");
}
}
2、AOP操作 - 基于xml配置的方式实现
1、创建类,在类里面添加方法
public class Human {
public void add(){
System.out.println("add......");
}
}
2、创建增强类
public class HumanProxy {
}
3、在spring配置,引入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: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">
<!--被增强类的创建对象-->
<bean id="human" class="com.atguigu.service.Human"></bean>
<!--这增强类的创建对象-->
<bean id="humanProxy" class="com.atguigu.service.HumanProxy"></bean>
<aop:config>
<!--aop:pointcut标签配置切面动作-->
<aop:pointcut id="p" expression="execution(* com.atguigu.service.Human.add(..)))"/>
<aop:aspect ref="humanProxy">
<!--aop:before标签配置: 前置通知-->
<aop:before method="before" pointcut-ref="p"></aop:before>
<!--aop:after-returning标签配置: 后置通知-->
<aop:after-returning method="afterReturning" pointcut-ref="p"></aop:after-returning>
</aop:aspect>
</aop:config>
</beans>
4、在增强类里面编写增强逻辑,创建不同类型的通知方法
public class HumanProxy {
//前置通知
public void before(){
System.out.println("before......");
}
//后置通知
public void afterReturning(){
System.out.println("afterReturning......");
}
}
5、测试要增强的方法
@Test
public void test3(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beantest.xml");
Human human = context.getBean("human", Human.class);
human.add();
}
结果
AOP操作的两个注意点:
①、如果通知方法上方的切入点注解表达式使用相同,可以将切入点表达式抽取出来创建一个方法,在方法上方添加切入点注解,作为增强方法上放的注解
②、有多个相同的增强方法增强同一个方法时,在增强方法上添加@Order(数值类型值)注解,值越小优先级越高
3、AOP操作 - 基于注解方式实现
1、创建一个类,在类上方使用@Configuration注解用于作为配置类
2、在配置类上添加@Component注解用于扫描匹配的注解创建对象@EnableAspectJAutoProxy通过扫描注解匹配增强方法
@Configuration //基于注解配置spring
@ComponentScan(basePackages = {"com.atguigu"}) //通过注解扫描匹配的注解创建对象
@EnableAspectJAutoProxy(proxyTargetClass = true) //通过扫描注解匹配增强方法
public class SpringConfig {
}
3、创建普通类添加创建对象注释,在类中创建被增强方法
@Repository //创建对象的注解
public class Dog {
public void wow(){
System.out.println("wow......");
}
}
4、创建增强类,在类上添加匹配增强类的注解、创建对象注释
@Aspect //注解代理类
@Repository
public class DogProxy {
//抽取切入点,创建为公共的切入点
@Pointcut("execution(* com.atguigu.config.Dog.wow(..))")
public void pointCut(){
}
//前置通知
@Before("pointCut()")
public void sleep(){
System.out.println("sleep......");
}
//后再通知
@AfterReturning("pointCut()")
public void eat(){
System.out.println("eat bone......");
}
}
5、测试被增强方法的调用
@Test
public void test2(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
Dog dog = context.getBean("dog", Dog.class);
dog.wow();
}
结果
JdbcTemplate是什么?
JdbcTemplate是Spring对JDBC的封装,目的是使JDBC更加易于使用。JdbcTemplate是Spring的一部分。JdbcTemplate处理了资源的建立和释放。他帮助我们避免一些常见的错误,比如忘了总要关闭连接。他运行核心的JDBC工作流,如Statement的建立和执行,而我们只需要提供SQL语句和提取结果。
1、使用JdbcTemplate
1、在工程中引入所需的jar包,这里需要使用JdbcTemplate、IOC、Mysql等jar包
2、准备一个数据库,User数据表中有准备些数据
3、创建xml配置文件,里面添加bean标签引入dataSource
<?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: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">
<!--加载mysql数据库-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<property name="url">
<value><![CDATA[jdbc:mysql://localhost:3306/book_review?useUnicode=true&characterEncoding=utf-8]]></value>
</property>
<property name="username" value="root" />
<property name="password" value="1234" />
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
</bean>
<!--创建JdbcTemplate类-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入dataSource-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--扫描注解创建对象-->
<context:component-scan base-package="com.atguigu"></context:component-scan>
</beans>
4、创建持久层dao,创建UserDao接口
@Repository
public interface UserDao {
/**
* 添加用户
*
* @param user 用户
*/
void addUser(User user);
/**
* 更新用户
*
* @param user 用户
*/
void updateUser(User user);
/**
* 删除用户的id
*
* @param id id
*/
void deleteUserById(int id);
/**
* 批量添加用户
*
* @param listUser 用户列表
*/
void batchAddUser(List<Object[]> listUser);
/**
* 查询用户的id
*
* @param id id
* @return {@link User}
*/
User queryUserById(int id);
List<User> queryAllUser();
}
5、创建UserDao的实现类
@Repository //创建对象的注解
public class UserDaoImpl implements UserDao {
@Autowired //自动转配属性
private JdbcTemplate jdbcTemplate;
@Override
public void addUser(User user) {
String sql = "insert into b_user(`id`,`username`,`password`,`email`) values(?,?,?,?)";
//调用jdbcTemplate类封装好的update方法进行增、删、改
jdbcTemplate.update(sql,user.getId(),user.getUsername(),user.getPassword(),user.getEmail());
}
@Override
public void updateUser(User user) {
String sql = "update b_user set `username`=?,`password`=?,`email`=? where id=?";
jdbcTemplate.update(sql,user.getUsername(),user.getPassword(),user.getEmail(),user.getId());
}
@Override
public void deleteUserById(int id) {
String sql = "delete from b_user where id=?";
jdbcTemplate.update(sql,id);
}
@Override
public void batchAddUser(List<Object[]> listUser) {
String sql = "insert into b_user(`id`,`username`,`password`,`email`) values(?,?,?,?)";
jdbcTemplate.batchUpdate(sql,listUser);
}
@Override
public User queryUserById(int id) {
String sql = "select `id`,`username`,`password`,`email` from b_user where id=?";
//调用jdbcTemplate类封装好的query方法进行查询
return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class),id);
}
@Override
public List<User> queryAllUser() {
String sql = "select `id`,`username`,`password`,`email` from b_user";
return jdbcTemplate.query(sql,new BeanPropertyRowMapper<User>(User.class));
}
}
6、创建服务层Service
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void addUser(User user){
userDao.addUser(user);
}
public void updateUser(User user){
userDao.updateUser(user);
}
public void deleteUserById(int id){
userDao.deleteUserById(id);
}
public void batchAddUser(List<Object[]> listUser){
userDao.batchAddUser(listUser);
}
public User queryUserById(int id){
return userDao.queryUserById(id);
}
public List<User> queryAllUser(){
return userDao.queryAllUser();
}
}
7、测试类
public class UserServiceTest {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
UserService userService = context.getBean("userService", UserService.class);
@Test
public void addUser() {
userService.addUser(new User(null,"abc678","123456abc","abc678@qq.com"));
}
@Test
public void updateUser(){
userService.updateUser(new User(1,"channel123","channel123","channel@qq.com"));
}
@Test
public void deleteUserById(){
userService.deleteUserById(10);
}
@Test
public void batchAddUser(){
List<Object[]> list = new ArrayList<>();
Object[] o1 = {null,"Morty","morty123","morty@qq.com"};
Object[] o2 = {null,"Monica","monica123","monica@qq.com"};
list.add(o1);
list.add(o2);
userService.batchAddUser(list);
}
@Test
public void queryUserById(){
System.out.println(userService.queryUserById(1));
}
@Test
public void queryAllUser(){
userService.queryAllUser().forEach(System.out::println);
}
}
Jdbctemplate中的事物操作管理
1、什么是事物?
事物是数据库操作的最基本单元,逻辑上一组操作,要么都成功,如果有一个失败则都失败
事务四个特性(ACID)
(1)原子性
(2)一致性
(3)隔离性
(4)持久性
在Spring进行事物管理操作
有两种处理事物的方式: 编程式事物管理、声明式事物管理(注解与配置文件)
在开发中,我们都使用声明式事物管理的方式,因为编程式事物管理过于麻烦,不利于维护
声明式事物管理的两种方式
1、基于配置文件的事物管理
2、基于注解的事物管理
Spring提供的事物管理API
PlatformTransactionManager接口,代表事物管理器,针对不同的框架提供不同的实现类
2、搭建事物操作环境
在开发过程中我们service、dao层都可以用于添加事物管理,但一般在service添加事物管理更有利于我们后期维护
2.1、引入工程需要的jar包
2.2、需要有个Account账户数据表,添加两条数据
2.3、创建配置文件,引入名称空间
context命名空间: 用于扫描要创建bean对象的注解
aop命名空间: 用于实现动态代理
tx命名空间: 开启事物管理器,tx的底层使用的是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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--开启组件扫描: 扫描有创建对象的注解-->
<context:component-scan base-package="com.atguigu"></context:component-scan>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<property name="url">
<value><![CDATA[jdbc:mysql://localhost:3306/book_review?useUnicode=true&characterEncoding=utf-8]]></value>
</property>
<property name="username" value="root" />
<property name="password" value="1234" />
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
</bean>
<!--jdbcTemplate注入dataSource-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--创建事物管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启事物管理器-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
2.4、创建service、搭建dao、完成对象创建和注入关系
service注入dao、dao注入JdbcTemplate、JdbcTemplate注入DataSource
AccountDao类
@Repository
public interface AccountDao {
/**
* 减少钱
*
* @param id id
* @param money 钱
*/
void reduceMoney(int id,int money);
/**
* 添加钱
*
* @param id id
* @param money 钱
*/
void addMoney(int id,int money);
}
AccountDao实现类,注入JdbcTemplate属性
@Repository
public class AccountDaoImpl implements AccountDao{
//注入JdbcTemplate
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void reduceMoney(int id, int money) {
String sql = "update b_account set `balance`=`balance`-? where id=?";
jdbcTemplate.update(sql,money,id);
}
@Override
public void addMoney(int id, int money) {
String sql = "update b_account set `balance`=`balance`+? where id=?";
jdbcTemplate.update(sql,money,id);
}
}
在service 类上面(或者 service 类里面方法上面)添加事务注解
(1)@Transactional,这个注解添加到类上面,也可以添加方法上面
(2)如果把这个注解添加类上面,这个类里面所有的方法都添加事务
(3)如果把这个注解添加方法上面,为这个方法添加事务
@Service
@Transactional //添加事物管理的注释
public class AccountService {
@Autowired //注入AccountDao对象
private AccountDao accountDao;
public void transfer(int id,int toId,int money){
//A账户减少金额
accountDao.reduceMoney(id,money);
//B账户增加金额
accountDao.addMoney(toId,money);
}
}
2.5 测试方法
public class AccountServiceTest {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
@Test
public void transfer() {
AccountService accountService = context.getBean("accountService", AccountService.class);
//1号转账给2号500
accountService.transfer(1,2,500);
}
}
在对数据库修改过程中无异常,成功结果
往service层添加异常测试
@Service
@Transactional //添加事物管理的注释
public class AccountService {
@Autowired //注入AccountDao对象
private AccountDao accountDao;
public void transfer(int id,int toId,int money){
//A账户减少金额
accountDao.reduceMoney(id,money);
//在两个事物传播之间插入一条异常
int i = 10/0;
//B账户增加金额
accountDao.addMoney(toId,money);
}
}
测试增加异常后的结果
3、事物管理操作 - 声明式事物管理参数配置
3.1、在service中添加@Transactional事物管理注解,它有几个参数可以用于配置事物相关数据
以下几个重要的参数划出来
(1)、propogation() : 事物传播行为
多个事物方法进行调用,这个过程如何进行管理的
Spring框架事物传播行为有7种,其中这两种比较常用
(2)、isolation(): 事物隔离级别
1、事物与事物之间的特性称为隔离性, 多个事物操作不会产生影响, 不考虑隔离层的事物会产生很多问题
2、有三个读的问题: 脏堵、不可重复读、幻读
脏读:一个未提交事务读取到另一个未提交事务的数据
不可重复读:一个未提交事务读取到另一提交事务修改数据
虚读:一个未提交事务读取到另一提交事务添加数据
3、解决:通过设置事务隔离级别,解决读问题
(3)、timeout:超时时间
1、事务需要在一定时间内进行提交,如果不提交进行回滚
2、默认值是 -1 ,设置时间以秒单位进行计算
(4)、readOnly:是否只读
1、读:查询操作,写:添加修改删除操作
2、readOnly 默认值 false,表示可以查询,可以添加修改删除操作
3、设置 readOnly 值是 true,设置成 true 之后,只能查询
(5)、rollbackFor:回滚
设置出现哪些异常进行事务回滚
(6)、noRollbackFor:不回滚
设置出现哪些异常不进行事务回滚
4、事物操作 - xml配置声明式事物管理
这里创建对象、连接数据库、事物管理全都使用xml配置文件实现
<!--1 创建事务管理器-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--2 配置通知-->
<tx:advice id="txadvice">
<!--配置事务参数-->
<tx:attributes>
<!--指定哪种规则的方法上面添加事务-->
<tx:method name="accountMoney" propagation="REQUIRED"/>
<!--<tx:method name="account*"/>-->
</tx:attributes>
</tx:advice>
<!--3 配置切入点和切面-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pt" expression="execution(*
com.atguigu.spring5.service.UserService.*(..))"/>
<!--配置切面-->
<aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
</aop:config>
5、事物操作 - 完全注解声明式事物管理
1、创建配置类,替代xml配置文件
@Configuration //设置为配置类
@ComponentScan(basePackages = {"com.atguigu"}) //开启对象扫描
@EnableTransactionManagement //开启事物管理
public class SpringConfig {
@Bean //Bean注解是创建的这个对象注册到IOC容器中
public DruidDataSource getDruidDataSource(){
//创建一个DruidDataSource();
DruidDataSource dataSource = new DruidDataSource();
//获取到配置信息
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("1234");
dataSource.setUrl("jdbc:mysql://localhost:3306/book_review?useUnicode=true&characterEncoding=utf-8");
return dataSource;
}
@Bean //Bean注解也可以根据参数列表的类型获取IOC容器中的对象
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
//创建JdbcTemplate对象获取数据源
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
@Bean //将DataSourceTransactionManager类型的对象注册到IOC容器中
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
//获取事物管理的数据库
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
2、测试方法
@Test
public void transfer2() {
//使用AnnotationConfigApplicationContext(SpringConfig.class)获取到IOC容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
AccountService accountService = context.getBean("accountService", AccountService.class);
//1号转账给2号500
accountService.transfer(1,2,500);
}
Spring5框架的新功能
整个Spring5框架基于Java8,运行时兼容JDK9,许多不建议使用的类与方法在代码块中删除
1、Spring5自带了通用的日志封装
(1)、Spring移出了Log5jConfigLisenter,采用了Log4j2
(2)、Spring5框架整合了Log4j
第一步: 引入jar包
第二步: 创建log4j2.xml
配置文件(配置文件名只能是这个)
并且配置文件的内容都是固定的
<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出-->
<configuration status="INFO">
<!--先定义所有的appender-->
<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>
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
<!--root:用于指定项目的根日志,如果没有单独指定Logger,则会使用root作为默认的日志输出-->
<loggers>
<root level="info">
<appender-ref ref="Console"/>
</root>
</loggers>
</configuration>
运行后控制台就会输出日志内容
2、Spring框架核心容器支持@Nullable注解
@Nullable可以使用在方法上、属性上、参数上, 表示方法返回值可以为空、属性值可以为空、参数可以为空
①、注解用在方法上面,方法返回值可以为空
②、注解使用在属性上面,表示属性值可以为空
③、注解使用在方法参数里面,方法参数可以为空
3、Spring5核心容器支持函数式风格GenericApplicationContext
首先函数式风格是Java8引入的lambda表达式编程,可以以更加简洁的方式编写代码
所以Spring5框架支持函数式编程能够更加高效的编写代码
@Test
///函数式风格创建对象,交给 spring 进行管理
public void testGenericApplicationContext(){
//1、创建GenericApplicationContext对象
GenericApplicationContext context = new GenericApplicationContext();
//2.调用context方法注册IOC容器
context.refresh();
context.registerBean("user", User.class,() -> new User());
//3.获取spring注册的对象
User user = (User) context.getBean("user");
System.out.println(user);
}