Spring5入门学习笔记

博主 1032 2020-07-09

Spring学习笔记

基于Spring5的学习笔记

1.Spring概述

1)Spring是什么

一款轻量级开源的JavaEE框架

2)Spring解决的问题

解决企业应用开发的复杂性

3)Spring核心部分

IOC:控制反转,把创建对象的过程交给Spring统一管理
AOP:面向切面编程,达到不修改源代码进行功能扩展(解耦)

4)Spring特点

方便解耦,简化开发
AOP编程支持
方便程序测试
方便与其他框架整合
方便事务操作
降低API开发难度

5)Spring HelloWorld

使用Spring创建一个User类
编写User类代码:

public class User {
    public void add(){
        System.out.println("add method");
    }
}

新建一个xml文件bean1.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">


    <!-- User对象创建
        id属性
        user属性
    -->
    <bean id="user" class="com.learn.demo1.User"></bean>
</beans>

新建一个测试类

public class SpringTest1 {
    /**
     * SpringHelloWorld
     * 使用Spring的xml方式创建对象
     */
    @Test
    public void testAdd() {
        //1. 加载spring配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");

        //2.获取配置创建的对象
        User user;
        user = context.getBean("user", User.class);
        //3.调用方法
        user.add();
    }
}

2.IOC容器

什么是IOC?
Inversion of Control控制反转,是OOP中的一种设计原则,用来降低计算机代码之间的耦合度,其中最常见的方式叫做DI(Dependency Injection,简称DI),还有一种方式叫以来查找(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它,也可以说,依赖被注入到对象中。

为什么使用IOC?
为了使耦合度降低

1)IOC底层原理

底层使用xml解析、工程模式、反射
在对象工厂中解析xml文件,通过反射获得类的实例

2)IOC接口(BeanFactory和ApplicationContext)

Spring提供IOC容器实现两种方式:
BeanFactory:IOC容器基本实现,是Spring内部的使用接口,不提供给开发人员进行使用。特点:采用懒加载的方式,在需要对象的时候才加载对象

ApplicationContext:BeanFactory接口的子接口,提供更多更强大的功能,提供给开发人员使用。特点:不采用懒加载,加载配置文件的时候就加载对象。(将创建对象的过程放在服务器启动的时候更好)
两个主要实现类:
ClassPathXmlApplicationContext
FileSystemXmlApplicationContext

3)IOC操作Bean管理(基于XML)

什么是Bean管理:1.Spring创建对象 2.Spring注入属性

  1. 首先使用bean标签,标签里面添加对应属性,就可以实现对象创建
    image.png
  2. bean标签常用属性:
    id属性:类的一个唯一标识
    class属性:类的全路径(包类路径)
    name属性:很少使用
  3. Spring创建对象,使用无参构造器
  4. 注入属性
    常见方式:set方法注入/有参构造器注入
    如何使用xml配置文件完成对象创建和属性注入

set方法注入:
image.png

有参构造注入:
image.png

其他方式(了解):p名称空间注入

  1. 在xml文件添加一个p名称空间
    image.png
  2. 配置bean标签
    在属性中用p:属性=值 的方式,实际使用set方法注入
    image.png

  1. 如何给属性设置空值?
    使用null标签
    image.png
  2. 如果属性值包含特殊符号<和>,可以使用&lt;&gt;表示
    image.png
    还可以使用CDATA
    image.png
  3. 如何注入外部bean
    以Service层和Dao层为例
    image.png
    其中UserService持有一个UserDaoImpl的实例
创建UserDao,UserDaoImpl,UserService
public class UserService {

    private UserDao userDao= new UserDaoImpl();

    public void add(){
        System.out.println("UserService's add method");
        userDao.Update();
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

public interface UserDao {
    void Update();
}

public class UserDaoImpl implements UserDao {
    @Override
    public void Update() {
        System.out.println("DAO's Update method");
    }
}

<!-- 使用set方法注入userDao到userService中-->
    <bean id="userService" class="com.learn.service.UserService">
        <!-- 不再使用value属性,而是ref属性,属性值是需要注入的类的id值-->
        <property name="userDao" ref="userDao"></property>
    </bean>
    <bean id="userDao" class="com.learn.dao.impl.UserDaoImpl"></bean>


```xml
4. 如何注入内部bean和级联赋值
典型一对多关系:部门和员工
一个部门有多个员工,一个员工在一个部门
<bean id="employee" class="com.learn.bean.Employee">
    <property name="id" value="1"></property>
    <property name="name" value="mary"></property>
    <property name="gender" value="男"></property>
    <property name="department">
        <bean id="dept" class="com.learn.bean.Department">
            <property name="departmentName" value="安保部门"></property>
        </bean>
    </property>
</bean>
```xml
方式一:
<!-- 使用级联赋值 给员工所在部门注入属性-->
    <bean id="employee" class="com.learn.bean.Employee">
        <property name="id" value="1"></property>
        <property name="name" value="mary"></property>
        <property name="gender" value="男"></property>
        <property name="department" ref="dept"></property>
    </bean>
    <bean id="dept" class="com.learn.bean.Department">
        <property name="departmentName" value="财务部门"></property>
    </bean>

方式二:
<!-- 使用级联赋值 给员工所在部门注入属性 需要employee的department属性的get方法-->
    <bean id="employee" class="com.learn.bean.Employee">
        <property name="id" value="1"></property>
        <property name="name" value="mary"></property>
        <property name="gender" value="男"></property>
        <property name="department" ref="dept"></property>
        <property name="department.departmentName" value="技术部门"></property>
    </bean>
    <bean id="dept" class="com.learn.bean.Department"></property>-->
    </bean>
  1. 注入集合类型属性
    创建Student类和Course类

public class Student {
    private String[] courses;
    private List<String> list;
    private Map<Integer, String> map;
    private Set<String> set;
    private List<Course> courseList;

    @Override
    public String toString() {
        return "Student{" +
                "courses=" + Arrays.toString(courses) +
                ", list=" + list +
                ", map=" + map +
                ", set=" + set +
                ", courseList=" + courseList +
                '}';
    }

    public void setCourseList(List<Course> courseList) {
        this.courseList = courseList;
    }

    public void setSet(Set<String> set) {
        this.set = set;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setMap(Map<Integer, String> map) {
        this.map = map;
    }

    public void setCourses(String[] courses) {
        this.courses = courses;
    }
}

public class Course {
    @Override
    public String toString() {
        return "Course{" +
                "name='" + name + '\'' +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    private String name;

}


<?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="student" class="com.learn.collectiontype.Student">
        <!-- 数组 list map set类型属性注入-->
        <property name="courses">
            <array value-type="java.lang.String">
                <value>Java</value>
                <value>离散数学</value>
                <value>操作系统</value>
            </array>
        </property>
        <property name="list">
            <list value-type="java.lang.String">
                <value>xxx</value>
                <value>yyy</value>
            </list>
        </property>
        <property name="map">
            <map>
                <entry key="1" value="光头强"></entry>
                <entry key="2" value="熊大"></entry>
            </map>
        </property>
        <property name="set">
            <set>
                <value>JavaEE</value>
                <value>JavaWeb</value>
            </set>
        </property>
        <property name="courseList">
            <list value-type="com.learn.collectiontype.Course">
                <ref bean="course1"></ref>
                <ref bean="course2"></ref>
            </list>
        </property>

    </bean>

    <bean id="course1" class="com.learn.collectiontype.Course">
        <property name="name" value="Java"></property>
    </bean>
    <bean id="course2" class="com.learn.collectiontype.Course">
        <property name="name" value="C++"></property>
    </bean>
</beans>

使用util标签将需要的集合内容抽取出来,不写在bean内部

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:util="http://www.springframework.org/schema/util"
       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
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">



    <bean id="student" class="com.learn.collectiontype.Student">
        <!-- 注入集合类型属性
        需要使用util名称空间
        -->
    <util:list id="bookList">
        <value>Cppp</value>
        <value>Cpppp</value>
        <value>Java Core</value>
    </util:list>
    <bean id="book" class="com.learn.collectiontype.Student">
        <property name="list" ref="bookList"></property>
    </bean>
</beans>
  1. IOC操作Bean管理的方式
    普通bean和工厂bean(FactoryBean)
    普通Bean:在配置文件中定义的bean类型,就是返回类型
    工厂bean:在配置文件中定义bean类型,和返回类型可以不一致,返回类型由继承FactoryBean时的泛型决定

  2. bean的作用域
    在Spring里面,设置创建bean实例是单实例还是多实例
    默认情况下,bean是单实例的
    通过bean标签的scope属性设置单实例还是多实例
    区别:
    单实例对象在加载配置文件时就创建,多实例对象在调用getBean方法时才创建

<bean id="myBean" class="com.learn.factorybean.MyBean" scope="singleton"></bean>
<bean id="book" class="com.learn.collectiontype.Book" scope="singleton">
  1. bean的生命周期
    不加后置处理器:
    1)通过构造器创建bean实例(无参构造)
    2)为bean的属性设置值,对其他bean对象引用(调用set方法)
    3)调用bean的初始化的方法(需要进行配置属性init-method)
    4)成功获取到bean对象,可以使用了
    5)当容器关闭时候,调用bean的销毁的方法(需要配置属性destroy-method)
<bean id="orders" class="com.learn.bean.Orders" init-method="init" destroy-method="destroy">
	<property name="name" value="订单"></property>
</bean>

加上后置处理器
1)通过构造器创建bean实例(无参构造)
2)为bean的属性设置值,对其他bean对象引用(调用set方法)
3)把bean实例传递bean后置处理器的方法
4)调用bean的初始化的方法(需要进行配置属性init-method)
5)把bean实例传递bean后置处理器的方法
6)成功获取到bean对象,可以使用了
7)当容器关闭时候,调用bean的销毁的方法(需要配置属性destroy-method)

配置后置处理器的方法,就是将实现了BeanPostProcessor的类作为bean添加到当前文件中,会为当前文件所有bean对象都添加这个后置处理器

<bean id="orders" class="com.learn.bean.Orders" init-method="init" destroy-method="destroy" >
        <property name="name" value="订单"></property>
    </bean>

    <!-- 配置后置处理的方式
    这个bean标签会为当前文件中所有的bean对象都添加后置处理器
    -->
    <bean id="myBeanPost" class="com.learn.bean.postprocessor.MyBeanPost"></bean>

  1. xml自动装配
    什么是自动装配?
    手动装配:手动指定装配规则,指定name属性和value属性
    自动装配,根据指定装配规则,Spring自动将匹配的属性值装配到对象
    根据bean标签的autowire属性做自动装配,可以根据类型和名字做自动装配
 <!-- 自动装配 autowire属性
有两个值 byName根据属性名称注入,注入值bean的id值和类属性名称一致
byType根据属性类型注入
    -->
 <bean id="employee" class="com.learn.autowire.Employee" autowire="byType"></bean>
    <bean id="department" class="com.learn.autowire.Department"></bean>
  1. 外部属性文件
    将properties文件引入xml文件的步骤:
    首先引入context名称空间
    然后通过context:property-placeholder标签引入properties文件
    使用$
    获取properties中的value
<!-- 读取配置文件配置连接池-->
    <cotext:property-placeholder location="jdbc.properties"></cotext:property-placeholder>
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="username" value="${prop.username}"></property>
            <property name="password" value="${prop.password}"></property>
            <property name="url" value="${prop.url}"></property>
            <property name="driverClassName" value="${prop.driverClassName}"></property>
        </bean>

4)IOC操作Bean管理(基于注解)

  1. 注解
    注解:一种代码的特殊标记
    格式:@注解名称(key=value,key=value...)
    使用注解目的:简化xml配置

  2. Spring针对Bean管理创建对象提供注解
    1)@Component
    2)@Service
    3)@Controller
    4)@Repository
    实际上4个注解是一样的,都可以用来创建bean实例,但是建议不同注解用在不同的层上

  3. 基于注解方式实现对象创建
    1)引入依赖
    image.png
    2)开启组件扫描

<!-- 开启组件扫描
        1.如果扫描多个包,多个包使用逗号隔开
        2.如果扫描多个包,多个包在同一个子包,则写最上层的包
    -->
    <context:component-scan base-package="com.learn"></context:component-scan>

3)创建类,添加注解
value可以不写,默认id值就是类名首字母小写其他相同

@Component(value = "userService")//->这句话和 <bean id = "userService class="...">类似 
public class UserService {
}

一些扫描的细节,可以自定义filter

<!-- 配置filter 只扫描带Controller注解的类-->
    <context:component-scan base-package="com.learn" use-default-filters="false">
        <context:include-filter type="annotation" 
                                expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!-- 配置filter 不扫描带Controller注解的类-->

    <context:component-scan base-package="com.learn">
        <context:exclude-filter type="annotation" 
                                expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
  1. 基于注解方式实现属性注入
    1)@AutoWired:根据属性类型进行自动装配
    2)@Qualifer:根据名称进行注入
    Qualifer需要和AutoWired一起使用
    @Qualifer(value="id") 填入id需要与类的注解给的值一样
    3)@Resource:可以根据类型注入,可以根据名称注入
    4)@Value:注入基本类型

步骤:
1)创建service和dao对象,在service和dao类添加注解
2)在service类中注入dao对象,在service中声明dao类的属性,在属性上添加@AutoWired

@Service
public class UserService {
    @Autowired
    @Qualifier(value = "udi")
    private UserDao userDao;
    public void add(){

        userDao.print();
    }
}

@Repository(value = "udi")
public class UserDaoImpl implements UserDao {
    @Override
    public void print() {
        System.out.println("UserDaoImplement");
    }
}

5) 全注解开发

步骤:

  1. 创建配置类
@Configuration//作为配置类 替代xml配置文件
@ComponentScan("com.learn")//开启扫描的扫描路径
public class SpringConfig {
}
  1. 添加bean对象
  2. 测试
//全注解开发
    @Test
    public void testUserServiceAndDao2(){
        ApplicationContext annotationConfigApplicationContext
                = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserService userService = annotationConfigApplicationContext.getBean("userService", UserService.class);
        userService.add();

    }

3.AOP面向切面编程(Aspect-Oriented Programming)

什么是AOP?
面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
通俗描述:不通过修改源代码的方式,在主干功能里面添加新的功能

1) AOP底层原理

底层使用动态代理
有两种动态代理情况

  1. 有接口情况,使用JDK动态代理
    创建接口实现类代理对象,增强类的方法
    使用JDK动态代理,需要Proxy类
    下面是自己写的动态代理
/**
 * 代理工厂:负责动态产生被代理实例的代理实例
 * object是被代理类的实例
 */
public class ProxyFactory{
    public static Object getProxy(Object object){
        return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),
                (proxy, method, args)-> {
                //String name = method.getName();
                //System.out.println("方法名:" + name);
                Object invoke = method.invoke(object, args);
                return invoke;
        });

    }
}

//接口
public interface UserDao {
    int add(int a, int b);
    String update(String id);
}

//接口的实现类,被代理类
public class UserDaoImpl implements UserDao {
    @Override
    public int add(int a, int b) {
        return a+b;
    }

    @Override
    public String update(String id) {
        return "id:" + id;
    }
}

//测试类
public class UserDaoProxyTest {
    public static void main(String[] args) {
        UserDaoImpl userDao = new UserDaoImpl();
        UserDao proxy = (UserDao)ProxyFactory.getProxy(userDao);
        String update = proxy.update("1");
        System.out.println(update);
    }
}
  1. 没有接口情况,使用CGLIT动态代理
    步骤:
    创建被代理类的子类,作为代理对象

2) AOP的术语

  1. 连接点
    类里面那些方法可以被增强,这些方法称为连接点
  2. 切入点
    实际被真正增强的方法,称为切入点
  3. 通知(增强)
    实际增强的逻辑部分称为通知(增强)
    通知的多种类型:前置通知、后置通知、环绕通知、异常通知、最终通知
  4. 切面
    是动作,把通知应用到切入点的过程就是切面。

3)基于Spring的AOP操作

1. 前置准备:Spring框架一般都是基于AspectJ实现AOP操作

1)什么是AspectJ:AspectJ不是Spring组成部分,是一个独立的AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作
2)基于AspectJ实现AOP操作有两种方式,基于xml配置文件实现和基于注解方式实现
3)引入AOP相关依赖
4)切入点表达式
切入点表达式的作用:知道对哪个类的哪个方法进行增强
语法结构:execution([权限修饰符][返回类型][全类名][方法名称][参数列表])
返回类型可不写,可用星号*代表所有权限修饰符,或者全部子包/类的全部方法
举例execution(*com.learn.dao.UserDao.add(..))

2. 使用AspectJ注解进行AOP操作

1)创建被代理类在类里面定义方法
2)创建增强类的方法
3)进行通知的配置
步骤:
1.在spring配置文件中(或配置类),开启组件扫描
2.使用注解创建User和UserProxy的bean对象
3.在增强类UserProxy类上添加Aspect注解
4.在配置文件中开启Aspect生成代理对象
5.配置不同类型的通知:在增强类的里面,在作为通知的方法上面添加类型注解,使用切入点表达式配置

范例:
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"
       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="com.learn"></context:component-scan>

    <!-- 开启Aspect生成代理对象-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

Java代码:

/**
 * 被代理类 被增强的类
 */
@Repository
public class User {
    public void add(){
        System.out.println("add method");
    }
}

@Component
@Aspect//生成代理对象
public class UserProxy {
    //Before注解表示作为前置通知
    @Before("execution(* com.learn.aopanno.User.add(..))")
    public void before(){
        System.out.println("before...");
    }

    //最终通知,一定执行
    @After("execution(* com.learn.aopanno.User.add(..))")
    public void after(){
        System.out.println("after...");
    }

    //后置通知(返回通知)
    @AfterReturning("execution(* com.learn.aopanno.User.add(..))")
    public void afterReturning(){
        System.out.println("afterReturning...");
    }

    //异常通知
    @AfterThrowing("execution(* com.learn.aopanno.User.add(..))")
    public void AfterThrowing(){
        System.out.println("AfterThrowing...");
    }

    //环绕通知
    @Around("execution(* com.learn.aopanno.User.add(..))")
    public void Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕之前...");
        proceedingJoinPoint.proceed();
        System.out.println("环绕之后...");
    }
}

public class TestAopAnno {
    @Test
    public void test1(){
        ClassPathXmlApplicationContext classPathXmlApplicationContext
                = new ClassPathXmlApplicationContext("bean1.xml");
        User user = classPathXmlApplicationContext.getBean("user", User.class);
        user.add();
    }
}

可以对公共切入点进行抽取

    @Pointcut("execution(* com.learn.aopanno.User.add(..))")
    public void pointDemo(){

    }

    //Before注解表示作为前置通知
    @Before("pointDemo()")
    public void before(){
        System.out.println("before...");
    }

有多个增强类对同一个被增强类进行增强,可以设置增强类的优先级
通过在增强类上面添加注解@Order(优先级值) 值越小优先级越高
(测试发现before和环绕之前方法优先级高先执行,after和afterreturning和环绕之后优先级高后执行)

3. 使用AspectJ配置文件形式完成AOP操作

1)创建增强类和被增强类
2)在spring配置文件中创建两个bean对象
3)在配置文件配置切入点,切面

4. 全注解开发的AOP配置类

@Configuration//配置类
@ComponentScan("com.learn.aopanno")//开启注解扫描
@EnableAspectJAutoProxy(proxyTargetClass = true)//开启Aspect生成代理对象
public class Configure {
}

4.JdbcTemplate

1. 什么是JdbcTemplate

Spring框架对JDBC进行封装,使用JdbcTemplate方便实现对数据库操作

2. 准备工作

引入依赖
在spring配置文件配置数据库连接池
配置jdbctemplate对象,注入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: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">
    <context:property-placeholder location="jdbc.properties"></context:property-placeholder>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="${prop.username}"></property>
        <property name="password" value="${prop.password}"></property>
        <property name="url" value="${prop.url}"></property>
        <property name="driverClassName" value="${prop.driverClassName}"></property>
    </bean>
    
    <!-- 配置jdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

  <!-- 开启组件扫描-->
    <context:component-scan base-package="com.learn"></context:component-scan>

</beans>


---------jdbc.properties文件--------
prop.username=root
prop.password=123456
prop.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8
prop.driverClassName=com.mysql.jdbc.Driver

3. 创建service类,dao类,在dao类中注入jdbctemplate的datasource

public interface BookDao {
}

@Repository
public class BookDaoImpl implements BookDao{

    @Autowired
    private JdbcTemplate jdbcTemplate;
}

@Service
public class BookService {
    @Autowired
    private BookDao bookDao;
}

4. 使用JdbcTemplate完成CRUD

使用到的方法
jdbcTemplate.update()
jdbcTemplate.queryForObject(String sql,Class requiredType)-比如查询数据库的总条数
查询单挑记录和多条记录:
注意RowMapper接口

@Override
    public Book queryById(String id) {
        String sql = "select user_id userId, username, ustatus from book where user_id = ?";
        //BeanPropertyRowMapper 返回一个bean
        //RowMapper是一个接口,用来封装数据的
        Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), id);
        return book;

    }

    @Override
    public List<Book> queryAllBook() {
        String sql = "select * from book";
        List<Book> query = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));
        return query;
    }
```Java
批量添加:
jdbcTemplate.batchUpdate(String sql, List<Object[]> batchArgs)
```Java
实现类的方法:
/**
     * 批量添加
     * @param batchArgs
     * List每一个元素是一个Object数组,一个数组存放对象的所有属性
     */
    @Override
    public void batchAddBook(List<Object[]> batchArgs) {
        String sql = "insert into book(user_id, username, ustatus) values(?,?,?) ";
        int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
        System.out.println(Arrays.toString(ints));
    }
---------------------------------------------------------------
Service方法
public void batchAdd(List<Object[]> batchArgs){
        bookDao.batchAddBook(batchArgs);
    }
---------------------------------------------------------------
测试类的方法
@org.junit.jupiter.api.Test
    void batchAdd(){
        ClassPathXmlApplicationContext classPathXmlApplicationContext
                = new ClassPathXmlApplicationContext("bean1.xml");
        BookService bookService = classPathXmlApplicationContext.getBean("bookService", BookService.class);
//        Book book = bookService.getBook("12");
        Object[] o1 = {"3", "java", "a"};
        Object[] o2= {"4", "python", "c"};
        Object[] o3= {"5", "php", "b"};
        bookService.batchAdd(new ArrayList<Object[]>(Arrays.asList(o1, o2, o3)));
    }

批量修改的方式

jdbcTemplate.batchUpdate(String sql, List<Object[]> batchArgs)
```Java
实现类的方法:
@Override
    public void batchUpdateBook(List<Object[]> batchArgs) {
        String sql = "update book set username=?, ustatus=? where user_id = ?";
        int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
        System.out.println(ints);
    }
---------------------------------------------------------------
Service类的方法
public void batchUpdate(List<Object[]> batchArgs){
        bookDao.batchUpdateBook(batchArgs);
    }
---------------------------------------------------------------
测试类的方法
@org.junit.jupiter.api.Test
    void batchUpdate(){
        ClassPathXmlApplicationContext classPathXmlApplicationContext
                = new ClassPathXmlApplicationContext("bean1.xml");
        BookService bookService = classPathXmlApplicationContext.getBean("bookService", BookService.class);
//        Book book = bookService.getBook("12");
        Object[] o1 = {"java11", "a", "3"};
        Object[] o2= {"py33", "c", "4"};
        Object[] o3= {"php123", "b", "5"};
        bookService.batchUpdate(new ArrayList<Object[]>(Arrays.asList(o1, o2, o3)));
    }

批量删除操作类似,不再记录

5.Spring事务

1. 什么是事务

事务是数据库操作中的一个基本单元,逻辑上一个事务中的操作要么都成功,要么都失败

2. 事务的4个特性:ACID

3. Spring事务管理(声明式事务管理)

声明式事务管理有两种方式,基于注解(推荐)/基于xml配置
声明式事务管理,底层使用AOP

4. Spring事务管理API

一个重要接口PlatformTransactionManager,代表事务管理器,这个管理器针对不同框架有不同的实现类

5. Spring事务管理操作(基于注解)

1)在配置文件中创建事务管理器,注入数据源

<!-- 创建事务管理器-->
    <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 注入数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

2)在spring配置文件,开启事务注解

引入名称空间tx,开启事务注解

 <!-- 开启事务注解-->
    <tx:annotation-driven transaction-manager="dataSourceTransactionManager"></tx:annotation-driven>

3)在service类上或者需要进行事务操作的方法上添加事务注解

@Transactional注解

4)Transactional注解的参数

1. propagation 事务传播行为

多事务方法直接进行调用,这个过程中事务是如何进行管理的
事务方法:指改变数据库中数据的方法
image.png
Spring框架定义了七种传播行为,默认为REQUIRED
image.png

2. isolation 事务隔离级别

READ UNCOMMITTED
READ COMMITTED
REPEATABLE_READ
SERIALIZABLE
事务的读问题:脏读,不可重复读,幻读

3. timeout 超时时间

事务需要在一定时间内提交,如果超时则回滚,默认值是-1
如果需要设置超时时间,单位为秒s

4. readOnly 是否只读

读:查询操作,写:添加修改删除操作
默认值为false,设置readOnly值为true后,只可以读操作

5. rollbackFor 回滚

设置出现哪些异常进行事务回滚
rollbackFor = Exception.class

6. noRollbackFor

设置出现哪些异常不进行事务回滚

6.Spring事务操作(基于XML)

  1. 在Spring配置文件进行配置
  2. 配置通知
  3. 配置切入点和切面
    参考配置文件
<?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:tx="http://www.springframework.org/schema/tx"
       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/tx
       http://www.springframework.org/schema/tx/spring-tx.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:property-placeholder location="jdbc.properties"></context:property-placeholder>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="${prop.username}"></property>
        <property name="password" value="${prop.password}"></property>
        <property name="url" value="${prop.url}"></property>
        <property name="driverClassName" value="${prop.driverClassName}"></property>
    </bean>

    <!-- 配置jdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 开启组件扫描-->
    <context:component-scan base-package="com.learn"></context:component-scan>

    <!-- 1 创建事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 注入数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 2 配置通知-->
    <tx:advice id="txadvice" transaction-manager="transactionManager">
        <!-- 配置事务参数-->
        <tx:attributes>
            <!-- 在那种规则的方法上添加事务-->
            <tx:method name="Transfer" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <!-- 配置切入点和切面-->
    <aop:config>
        <!-- 配置切入点-->
        <aop:pointcut id="pointcut" expression="execution(* com.learn.service.AccountService.Transfer(..))"/>
        <!-- 配置切面 -->
        <aop:advisor advice-ref="txadvice" pointcut-ref="pointcut"></aop:advisor>
    </aop:config>
</beans>

7.Spring事务操作(完全注解)

配置类参考:

@Configuration//配置类
@ComponentScan("com.learn")//开启组件扫描
@EnableTransactionManagement//开启事务注解
public class TransactionConfig {

    //创建数据库连接池
    @Bean()
    public DataSource getDruidDataSource() throws Exception {
        Properties properties = new Properties();
        InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
        properties.load(resourceAsStream);
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);

        return dataSource;
    }

    //创建JdbcTemplate对象
    //参数dataSource由IOC容器自动找到生成的bean对象
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource){
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    //创建事务管理器
    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource);
        return dataSourceTransactionManager;
    }
}

6.Spring5新特性

1)Spring5框架基于JDK8实现,运行时兼容JDK9

许多不建议使用的类和方法在代码库中删除

2)Spring5框架自带了通用的日志封装,已经移除了Log4jConfigListener,官方建议使用Log4j2

Spring5框架整合Log4j2
步骤:

  1. 引入依赖
  2. 创建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>

手动输出日志信息:

public class UserLog {
    private static final Logger logger = LoggerFactory.getLogger(UserLog.class);

    public static void main(String[] args) {
        logger.info("hello log4j2");
        logger.warn("hello log4j2");
    }
}

3)Spring5框架核心容器支持@Nullable注解

@Nullable注解可以使用在方法,属性,参数上面
表示方法返回值可以为空,属性可以为空,参数可以为空

4)支持函数式风格编程

函数式风格注册对象

@Test
    public void testGenericApplicationContext(){
        //1.创建GenericApplicationContext对象
        GenericApplicationContext context = new GenericApplicationContext();
        //2.调用context的方法进行对象注册
        context.refresh();
        context.registerBean("user1", User.class, ()->new User());

//        User user = context.getBean("com.learn.pojo.User", User.class);
//        System.out.println(user);
        User user1 = context.getBean("user1", User.class);
        System.out.println(user1);
    }

5)整合Junit4

@RunWith(SpringJUnit4ClassRunner.class)//指定单元测试版本
@ContextConfiguration("classpath:bean1.xml")//加载配置文件
public class Junit4 {
    @Autowired
    private AccountService accountService;

    @Test
    public void test1(){
        accountService.Transfer();
    }
}

6)整合Junit5

//同时写两个注解,可以用SpringJUnitConfig代替
//@ExtendWith(SpringExtension.class)
///@ContextConfiguration("classpath:bean1.xml")//加载配置文件
@SpringJUnitConfig(locations = "classpath:bean1.xml")
public class Junit5 {
    @Autowired
    private AccountService accountService;

    @Test
    public void test1(){
        accountService.Transfer();
    }
}

7)Webflux