Spring框架学习
Spring框架学习

Spring框架学习

Spring框架

Spring5简介

1.Spring 两大核心:

  • IOC: 控制反转,把创建对象的过程交给Spring进行管理
  • AOP: 面向切面,不修改源代码进行功能增强

2.Spring特点:

(1)方便解耦,简化开发

(2)Aop 编程支持

(3)方便程序测试

(4)方便和其他框架进行整合

(5)方便进行事务操作

(6)降低 API 开发难度

3.入门案例

  • 通过Spring配置文件获取类的对象
<!--xml中配置User类创建对象-->
    <bean id="user" class="user.User"></bean>
       /*加载Spring配置文件*/
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); //src下的xml
        /*通过Spring配置文件获取对象*/
        User user = context.getBean("user",User.class); //id:user

IOC

1.IOC简介

1.什么是IOC

(1)控制反转,把对象创建和对象之间的调用过程,交给 Spring 进行管理

(2)使用 IOC 目的:为了耦合度降低

(3)做入门案例就是 IOC 实现

2.IOC 底层原理

(1)xml 解析、工厂模式、反射

3.画图讲解IOC底层原理

![](C:\Users\Odin\Desktop\Odin\Typora photo\QQ截图20211208224001.png)

  • IOC(BeanFactory接口)

1、IOC 思想基于 IOC 容器完成,IOC 容器底层就是对象工厂

2、Spring 提供 IOC 容器实现两种方式:(两个接口)

(1) BeanFactory:IOC 容器基本实现,是 Spring 内部的使用接口,不提供开发人员进行使用

  • 加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象

(2) ApplicationContext:BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人 员进行使用

  • 加载配置文件时候就会把在配置文件对象进行创建

    2.Bean管理XML


(1)什么是 Bean 管理

1、Bean 管理指的是两个操作

(1)Spring 创建对象

(2)Spirng 注入属性

2、Bean 管理操作有两种方式

(1)基于 xml 配置文件方式实现

(2)基于注解方式实现

(2)属性注入

  • set方法注入属性

在Bean里先创建类,创建其set方法

手动装配:

<bean id="book" class="test.Book">
        <!--name: 属性名   value: 属性值-->
        <property name="name" value="Java编程思想"></property>
        <property name="price" value="98"></property>
    </bean>

p名称空间注入,在标签中添加属性,在标签添加p.属性名

<beans xmlns:p="http://www.springframework.org/schema/p">
<bean id="book" class="test.Book" p:name="Java编程思想2" p:price="108"></bean>

注入空值:

<property name="name">
            <null/>
</property>

特殊符号:

<property name="name">
            <value><![CDATA[<<"Java编程思想">>]]></value>
</property>
  • 使用有参构造注入属性

在Bean中写入类的有参构造器

<!--有参构造器注入属性-->
    <bean id="order" class="test.Order">
        <constructor-arg name="name" value="Java核心技术"></constructor-arg>
        <constructor-arg name="price" value="144"></constructor-arg>
    </bean>

(3)对象注入

外部Bean:

private UserDao userDao;

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

    public void serviceMethod(){
        System.out.println("UserService is realize");
        userDao.method();
    }
<!--外部bean-->
    <!--创建UserService对象-->
    <bean id="userService" class="test.Service.UserService">
        <!--注入userDao对象
        name:UserService中的属性名
        ref: userDao配置文件的id值
        -->
        <property name="userDao" ref="userDao"></property>
    </bean>
    <!--创建userDao对象-->
    <bean id="userDao" class="test.Dao.UserDaoImpl"></bean>

内部Bean:

public class Employee {
    private String name;
    private int age;

    private Department department;

    public void setDepartment(Department department) {
        this.department = department;
    }

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

    public void setAge(int age) {
        this.age = age;
    }
}
<!--内部bean-->
    <!--创建Employee对象-->
    <bean name="employee" class="test.bean.Employee">
        <!--设置基本属性-->
        <property name="name" value="cxk"></property>
        <property name="age" value="20"></property>

        <!--注入Department对象-->
        <property name="department">
            <!--创建Department对象-->
            <bean name="department" class="test.bean.Department">
                <property name="deptName" value="生产部"></property>
                <property name="deptId" value="1"></property>
            </bean>
        </property>

    </bean>

(4)数组与集合注入

public class Into {
    private String[] arr;
    private List<String> list;
    private Map<String,Person> personMap;

    private Set<Person> personSet;

    public String[] getArr() {
        return arr;
    }
}
<bean name="into" class="collectionType.Into">
        <!--数组属性注入-->
        <property name="arr">
            <array>
                <value>蔡徐坤</value>
                <value>卢本伟</value>
            </array>
        </property>

        <!--list集合属性注入-->
        <property name="list">
            <list>
                <value>cxk</value>
                <value>lbw</value>
            </list>
        </property>

        <!--Map集合属性注入-->
        <property name="personMap">
            <map>
                <entry key="cxk" value-ref="person1"></entry>
                <entry key="lbw" value-ref="person2"></entry>
            </map>
        </property>

        <!--set集合属性注入-->
        <property name="personSet">
            <set>
                <ref bean="person1"></ref>
                <ref bean="person2"></ref>
            </set>
        </property>
    </bean>

    <!--创建Person类对象-->
    <bean name="person1" class="collectionType.Person">
        <property name="name" value="MapCxk"></property>
        <property name="age" value="10"></property>
    </bean>
    <bean name="person2" class="collectionType.Person">
        <property name="name" value="MapLBW"></property>
        <property name="age" value="20"></property>
    </bean>
  • 将集合部分提取出来赋值
public class Book {
    private List<String> books;

    public void setBooks(List<String> books) {
        this.books = books;
    }
}
  • 在配置文件中引入名称空间
<!--增加util-->
<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">
    <!--创建Book对象,直接使用bookList进行赋值-->
    <bean name="book" class="collectionType.Book">
        <property name="books" ref="bookList"></property>
    </bean>
    <!--提取list集合类型进行属性注入-->
    <util:list id="bookList">
        <value>Java编程思想</value>
        <value>Java核心技术</value>
        <value>Effective Java</value>
    </util:list>

</beans>

(5)FactoryBean

1、Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)

2、普通 bean:在配置文件中定义 bean 类型就是返回类型

3、工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样

  • 第一步 创建类,让这个类作为工厂 bean,实现接口 FactoryBean

  • 第二步 实现接口里面的方法,在实现的方法中定义返回的 bean 类型

public class MyBean implements FactoryBean<Person> {
    @Override
    public Person getObject() throws Exception {
        Person person = new Person();
        person.setName("kobe");
        person.setAge(10);
        return person;
    }
}
<bean name="myBean" class="factoryBean.MyBean"></bean>
@Test
    public void MyBeanTest(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
        Person myBean = context.getBean("myBean", Person.class);
        System.out.println(myBean);
    }

此时获取到的为Person对象

标签中的scope属性:

- singleton: 单实例对象 (默认)
- prototype: 多实例对象

#### (6)Bean的生命周期

(1)通过构造器创建 bean 实例(无参数构造)

(2)为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)

(3)把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization

(4)调用 bean 的初始化的方法(需要进行配置初始化的方法)

(5)把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization

(6)bean 可以使用了(对象获取到了)

(7)当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)

- xml配置文件:

```xml
<!--配置类的对象-->
<bean name="lifeCycleTest" class="lifeCycle.LifeCycleTest" init-method="initMethod" destroy-method="destroyMethod">
<property name="name" value="蔡徐坤"></property>
</bean>
<!--配置后置处理器-->
<bean name="postProcessors" class="lifeCycle.PostProcessors"></bean>
```

- 注意:配置后置处理器时,会为当前xml配置文件的所有添加后置处理器

#### (7)自动装配

```xml
<!--配置person对象,自动装配-->
<bean name="person" class="auto.Person" autowire="byName"></bean>
<!--配置dept对象-->
<bean name="dept" class="auto.Dept"></bean>
```

#### (8)引入外部属性文件

- properties配置文件

```properties
prop.url=jdbc:mysql://localhost:3306/test
prop.username=root
prop.password=502502
prop.driverClassName=com.mysql.cj.jdbc.Driver
```

- 在xml配置文件中需要加入context属性

```xml
<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="classpath*:jdbc.properties"/>
<!--配置连接池-->
<bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${prop.url}" />
<property name="username" value="${prop.username}" />
<property name="password" value="${prop.password}" />
<property name="driverClassName" value="${prop.driverClassName}" />
</bean>
```

### 3.Bean管理注解

------

#### (1)使用注解创建对象

1、什么是注解

(1)注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值..)

(2)使用注解,注解作用在类上面,方法上面,属性上面

(3)使用注解目的:简化 xml 配置

2、Spring 针对 Bean 管理中创建对象提供注解

(1)@Component

(2)@Service

(3)@Controller

(4)@Repository

- 上面四个注解功能是一样的,都可以用来创建 bean 实例

第一步:xml配置文件中开启组件扫描

- 加入context

```xml
<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:component-scan base-package="bean1"></context:component-scan>
```

- base-package表示扫描的包名,可以写多个包

第二部,创建实体类,并添加注解

```java
/*若不写value,则id默认为类的名称,首字母小写*/
@Component(value = "bean1Test") //相当于<bean id="bean1Test" class=""></bean>
public class Bean1Test {
public void say(){
System.out.println("name is Jack");
}
}
```

- **组件扫描的细节**

```xml
<!--示例 1
use-default-filters="false" 表示现在不使用默认 filter,自己配置 filter
context:include-filter ,设置扫描哪些内容
-->
<context:component-scan base-package="com.atguigu" use-defaultfilters="false">
<context:include-filter type="annotation"

expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
```

```xml
<!--示例 2
下面配置扫描包所有内容
context:exclude-filter: 设置哪些内容不进行扫描
-->
<context:component-scan base-package="com.atguigu">
<context:exclude-filter type="annotation"

expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
```

#### (2)使用注解注入属性&对象

- **@Autowired**:根据属性类型进行注入

```java
/*创建UserService对象*/
@Service
public class UserService {

/*注入属性:注入UserDaoImpl对象*/
@Autowired //根据属性类型进行注入
private UserDao userDao;
}
```

```java
/*创建UserDaoImpl对象*/
@Component
public class UserDaoImpl implements UserDao{
}
```

- **@Qualifier**:根据名称进行注入

必须与@Autowired一起使用

```java
/*创建UserService对象*/
@Service
public class UserService {

/*注入属性:注入UserDao对象*/
@Autowired //根据属性类型进行注入
@Qualifier(value = "userDaoImpl") //根据属性名称进行注入
private UserDao userDao;
}
```

```java
/*创建UserDaoImpl对象*/
@Component(value = "userDaoImpl")
public class UserDaoImpl implements UserDao{
}
```

- **@Resource:**可根据类型注入,也可根据名称注入(javax包中,spring不建议用)

```java
/*根据类型进行注入*/
@Resource
private UserDao userDao;
```

```java
/*根据名称进行注入*/
@Resource(name = "userDaoImpl")
private UserDao userDao;
```

- **@Value:**基本类型属性值的注入

```java
/*基本类型属性的注入*/
@Value(value = "cxk")
private String name;
```

#### (3)完全注解开发

使用配置类的方式替换xml配置文件

- Configuration: 表示此类为配置类,可替代xml
- ComponentScan(basePackages={"包路径","包路径"})

```java
@Configuration //表示此类为配置类,替代xml配置文件
@ComponentScan(basePackages = {"dao","service"}) //组件扫描,设置需要扫描的包
public class SpringConfig {
}
```

测试方式:

```java
@Test
public void springConfigTest(){
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); //配置类.class
UserService userService = context.getBean("userService", UserService.class);
userService.serviceMethod();
}
```

## AOP

### 1.AOP简介

------

#### (1)什么是 AOP

(1)**面向切面编程**(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得 业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

(2)**通俗描述**:不通过修改源代码方式,在主干功能里面添加新功能。

2.AOP底层原理

- 有接口:创建接口实现类的代理对象
- 无接口:创建当前子类的代理对象

![](C:\Users\Odin\Desktop\Odin\Typora photo\Snipaste_2021-12-14_14-10-58.png)

------

#### (2)编写JDK动态代理代码

(1)调用 newProxyInstance 方法

方法有三个参数:

- 第一参数,类加载器

- 第二参数,增强方法所在的类,这个类实现的接口,支持多个接口

- 第三参数,实现这个接口 InvocationHandler,创建代理对象,写增强的部分

```java
public static void main(String[] args) {
Class[] interfaces={UserDao.class};
TheProxy theProxy = new TheProxy(new UserDaoImpl());
/*调用newProxyInstance方法调用代理类和代理类方法*/
UserDao dao = (UserDao) Proxy.newProxyInstance(UserDaoProxy.class.getClassLoader(), interfaces, theProxy);
System.out.println(dao.method1("cxk", 20));
System.out.println(dao.method2("hello world"));
}
```

(2)创建接口及实现类

```java
public interface UserDao {
public String method1(String name, int age);
public String method2(String str);
}

public class UserDaoImpl implements UserDao{
@Override
public String method1(String name, int age) {
System.out.println("method1执行了");
return "name: "+name+" age: "+age;
}

@Override
public String method2(String str) {
System.out.println("method2执行了");
return "字符串为:"+str;
}
}
```

(3)创建代理类

- method.getName(): 调用被代理类的方法名
- Object proxy:传入被代理类的对象
- Method method:传入被代理的方法
- Object[] args:传入方法参数
- method.invoke(obj,args):运行被代理的方法 obj:传入的被代理类对象

```java
/*创建代理类*/
class TheProxy implements InvocationHandler {

/*传入被代理类的对象*/
private Object obj;
/*通过有参构造方法,将被代理类的对象传入*/
public TheProxy(Object obj){
this.obj=obj;
}

/*代理逻辑*/
@Override //被代理类的对象 被代理的方法 方法传入的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*代理方法之前*/
System.out.println("代理方法之前执行:"+method.getName()+"传入的参数:"+ Arrays.toString(args)); //method.getName():方法名

/*被代理的方法执行*/
Object invoke = method.invoke(obj, args);

/*代理方法之后执行*/
System.out.println("代理方法之后执行:"+method.getName()+"传入的参数为:"+Arrays.toString(args));

return invoke;
}
}
```

------

#### (3)AOP操作术语

**1、连接点**

类中可以被增强的方法,称为连接点

**2、切入点**

类中实际被增强的方法,称为切入点

**3、通知(增强)**

(1)、实际增强的部分称为通知(增强)。

(2)、通知有种类型

- 前置通知:增强的方法前 的部分
- 后置通知:增强的方法后的部 分
- 环绕通知:增强的方法前后的部分
- 异常通知:增强的方法出异常时执行的部分
- 最终通知:增强的方法最终执行的部分(相当于fianally)

**4、切面**

是把通知运用到切入点的过程(是动作)

### 2.AOP操作

------

#### (1)操作描述

**1、Spring 框架一般都是基于 AspectJ 实现 AOP 操作**

AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使 用,进行 AOP 操作

**2、基于 AspectJ 实现 AOP 操作**

(1)基于 xml 配置文件实现

(2)基于注解方式实现(使用)

**3、切入点表达式**

(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强

(2)语法结构:

execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )

- 举例 1:对 com.atguigu.dao.BookDao 类里面的 add 进行增强 execution(* com.atguigu.dao.BookDao.add(..))

- 举例 2:对 com.atguigu.dao.BookDao 类里面的所有的方法进行增强 execution(* com.atguigu.dao.BookDao.* (..))

- 举例 3:对 com.atguigu.dao 包里面所有类,类里面所有方法进行增强 execution(* com.atguigu.dao.*.* (..))

------

#### (2)配置不同类型的通知(增强)

- **Component** 创建对象

- **@Aspect** 声明此类为增强类,生成代理注解
- **@Before** 前置通知
- **@AfterReturning** 后置通知
- **@Around** 环绕通知
- **@AfterThrowing** 异常通知
- **@After** 最终通知

**1、创建被增强类**

```java
/*被增强类*/
@Component //创建被增强类的对象
public class UserDao {
public void add(){
System.out.println("add方法执行");
}
}
```

**2、创建配置文件**

```xml
<!--添加context和aop-->
<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="aopTest"/>

<!--开启Aspect生成代理对象: 寻找类中是否有@Aspect对象,若有则为增强类-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
```

**3、创建增强类**

```java
/*增强类*/
@Component //创建增强类的对象
@Aspect //声明此类为增强类,生成代理注解
public class UserDaoProxy {

/*前置增强方法(前置通知)*/
/*写上切入点表达式,表示指名哪个方法进行增强 @Before: 表示在切入点前执行*/
@Before(value = "execution(* aopTest.UserDao.add(..))")
public void before(){
System.out.println("before方法执行");
}

/*后置通知*/
@AfterReturning(value = "execution(* aopTest.UserDao.add(..))")
public void AfterReturning(){
System.out.println("afterReturning方法执行");
}

/*异常通知*/
@AfterThrowing(value = "execution(* aopTest.UserDao.add(..))")
public void afterThrowing(){
System.out.println("afterThrowing方法执行");
}

/*环绕通知*/
@Around(value = "execution(* aopTest.UserDao.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("前环绕通知");
proceedingJoinPoint.proceed(); //执行切入点
System.out.println("后环绕通知");
}

/*最终通知*/
@After(value = "execution(* aopTest.UserDao.add(..))")
public void After(){
System.out.println("after方法执行");
}
}
```

#### (3)抽取切入点&设置优先级

- **Pointcut** 对切入点进行抽取
- **Order** 设置增强类的优先级

1、@Pointcut(value="切入点表达式")

```java
/*抽取相同的切入点,增强方法中调用即可*/
@Pointcut(value = "execution(* aopTest.UserDao.add(..))")
public void pointcut(){}

@Before(value = "pointcut()") //调用切入点方法指定切入点
public void before(){}
```

2、@Order(数字):数字越小,优先级越高

```java
/*设置增强类的优先级*/
@Order(1)
public class UserDaoProxy {}

@Order(2)
public class UseDaoProxy2 {}
```

------

#### (4)AspectJ XML配置文件操作

- 开启组件扫描
- 配置增强

```xml
<!-- 添加组件扫描,获取指定包中对象-->
<context:component-scan base-package="aopxml"/>

<!--配置aop增强-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="p1" expression="execution(* aopxml.Book.book(..))"/>

<!--配置切面-->
<aop:aspect ref="bookProxy"> <!--填写增强类的对象-->
<aop:before method="before" pointcut-ref="p1"/> <!--设置增强类型,指名切入点-->
</aop:aspect>
</aop:config>
```

------

## JdbcTemplate

### 1.JbcTemplate的概述和准备

- 什么是 JdbcTemplate

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

- 准备Jdbc配置文件

```properties
prop.url=jdbc:mysql://localhost:3306/test
prop.username=root
prop.password=502502
prop.driverClassName=com.mysql.cj.jdbc.Driver
```

- 在 spring 配置文件配置数据库连接池

```xml
<!--引入外部属性文件-->
<context:property-placeholder location="classpath*:jdbc.properties"/>
<!--配置连接池-->
<bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${prop.url}" />
<property name="username" value="${prop.username}" />
<property name="password" value="${prop.password}" />
<property name="driverClassName" value="${prop.driverClassName}" />
</bean>

<!--创建JdbcTemplate对象-->
<bean name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入dataSource-->
<property name="dataSource" ref="dataSource"/>
</bean>
```

- 开启组件扫描,获取指定包中含有创建对象的注释,并为其创建对象

```xml
<context:component-scan base-package="project1"/> <!--扫描project1中所有内容-->
```

### 2.JdbcTemplate的使用

#### (1)基本书写层次

- 创建数据库,数据表

![](C:\Users\Odin\Desktop\Odin\Typora photo\Snipaste_2021-12-15_17-17-27.png)

- 创建bean类

```java
@Component
public class Book {
private String userId;
private String username;
private String ustatus;
/*get,set,toString,构造器方法*/
}
```

- 创建service层(设置保存信息方法)

```java
@Component //创建对象
public class BookService {

/*注入BookDaoImpl对象*/
@Autowired
private BookDao bookDao;

/*添加图书*/
public void addBook(Book book){
bookDao.add(book);
}
```

- 创建dao层

利用@Autowired注解获取JdbcTemplate对象

使用JdbcTemplate类的update方法,传入sql语句及参数就能实现增删改操作

```java
public interface BookDao {
void add(Book book);
}

@Component //创建对象
public class BookDaoImpl implements BookDao{

/*注入JdbcTemplate对象*/
@Autowired
private JdbcTemplate jdbcTemplate;

@Override
public void add(Book book) {
String sql="insert into spring5.t_book values (?,?,?)";
Object[] args={book.getUserId(), book.getUsername(), book.getUstatus()};
/*update:jdbcTemplate的增删改操作*/
int update = jdbcTemplate.update(sql,args);
System.out.println(update); //影响的行数
}
}
```

- 测试

由于调用BookService类的add方法,则测试需要获取BookService类的对象

运行后可将book对象信息保存在数据库中

```Java
@Test
public void BookTest1(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
BookService bookService = context.getBean("bookService", BookService.class);
Book book = new Book();
book.setUserId("1");
book.setUsername("cxk");
book.setUstatus("kobe");
bookService.addBook(book);
}
```

#### (2)JdbcTemplate方法使用

- **增加,删除,修改:**

update为操作影响的行数

```java
int update = jdbcTemplate.update(sql, args); //sql语句,传入参数
```

- **查询总记录数**

```java
public int selectCount() {
String sql="select count(*) from spring5.t_book";
return jdbcTemplate.queryForObject(sql,Integer.class); //返回值类型的class
}
```

- **根据某一字段查询并返回对象**

BeanPropertyRowMapper<返回类型>(返回类型.class)

```java
public Book findBook(String user_id) {
String sql="select * from spring5.t_book where user_id=?";
Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), user_id);
return book;
}
```

- **查询返回多条记录(list集合对象)**

BeanPropertyRowMapper<返回类型>(返回类型.class)

```java
public List<Book> findAll() {
String sql="select * from spring5.t_book";
List<Book> books = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));
return books;
}
```

- **批量添加操作**

Object[] args 数组的各元素为每一个对象的各个字段

int[] update 数组为集合中的每条数据所影响的行数

```java
public void batchAdd(List<Object[]> args) {
String sql="insert into spring5.t_book values(?,?,?)";
int[] update = jdbcTemplate.batchUpdate(sql, args);
}
```

- **多行修改操作**

```java
public void batchUpdate(List<Object[]> args) {
String sql="update spring5.t_book set username=?,ustatus=? where user_id=?";
int[] ints = jdbcTemplate.batchUpdate(sql, args);
}
```

- **多行删除操作**

```java
public void batchDelete(List<Object[]> args) {
String sql="delete from spring5.t_book where user_id=?";
int[] ints = jdbcTemplate.batchUpdate(sql, args);
}
```

---

## 事物操作

### 1.事物概念

#### (1)什么是事务

(1)事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操 作都失败

(2)典型场景:银行转账 * lucy 转账 100 元 给 mary * lucy 少 100,mary 多 100

2、事务四个特性(ACID)

(1)原子性

(2)一致性

(3)隔离性

(4)持久性

#### (2)Spring 事务管理介绍

1、一般将事物添加到service(业务逻辑层)

2、在 Spring 进行事务管理操作

(1)有两种方式:~~编程式~~事务管理和**声明式**事务管理

- 编程式(不常用)

```java
public void accountMoney(){
/*marry转账给lucy*/

/*开启事物*/
try {
/*lucy多钱*/
accountDao.addMoney();

/*marry少钱*/
accountDao.reduceMoney();

/*若无异常,提交事物*/
System.out.println("无异常,提交事物");
} catch (Exception e) {
/*若出现异常,回滚事物*/
System.out.println("出现异常,回滚事物");
}
}
```

3、声明式事务管理

(1)基于注解方式(使用)

(2)基于 xml 配置文件方式

4、在 Spring 进行声明式事务管理,底层使用 AOP 原理

### 2.事物操作

#### (1)声明式事物操作(基于注解)

- 在spring的xml配置文件中添加事物配置

引入名称空间tx

```xml
<xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd>
```

创建事物管理器,开启事物注解,并引入事物管理器

```xml
<!--创建事物管理器-->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入dataSource-->
<property name="dataSource" ref="dataSource"/>
</bean>

<!--开启事物注解,引入事物管理器-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
```

- 添加事物注解

(1)@Transactional,这个注解添加到类上面,也可以添加方法上面

(2)如果把这个注解添加类上面,这个类里面所有的方法都添加事务

(3)如果把这个注解添加方法上面,为这个方法添加事务

```java
@Transactional //开启事物(在类上加表明类中所有方法都添加了事物)
public class AccountServiceImpl {}
```

#### (2)事务管理参数配置

**1.propagation:**事务传播行为

多事务方法直接进行调用,这个过程中事务 是如何进行管理的

**(Spring定义的7种传播行为)**

![](https://odinpeng.fun/wp-content/uploads/2022/05/Snipaste_2021-12-18_17-29-35-300x177.png)

**2.isolation:** 事物隔离级别

(1)事务有特性成为隔离性,多事务操作之间不会产生影响。不考虑隔离性产生很多问题

(2)有三个读问题:脏读、不可重复读、虚(幻)读

- 脏读:一个未提交事务读取到另一个未提交事务的数据
- 不可重复读:一个未提交事务读取到另一提交事务修改数据、
- 幻读:一个未提交事务读取到另一提交事务添加数据

(3)解决:通过设置事务隔离级别,解决读问题

**3.timeout**: 超时时间

(1)事务需要在一定时间内进行提交,如果不提交进行回滚

(2)默认值是 -1 ,设置时间以秒单位进行计算

**4.readOnly**:是否只读

(1)读:查询操作,写:添加修改删除操作

(2)readOnly 默认值 false,表示可以查询,可以添加修改删除操作

(3)设置 readOnly 值是 true,设置成 true 之后,只能查询

**5.rollbackFor**:回滚

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

**6.noRollbackFor**:不回滚

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

#### (3)XML文件配置事物

在 spring 配置文件中进行配置

- 第一步 配置事务管理器

- 第二步 配置通知

- 第三步 配置切入点和切面

```xml
<!--创建事物管理器-->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入dataSource-->
<property name="dataSource" ref="dataSource"/>
</bean>

<!--配置通知-->
<tx:advice id="txadvice">
<!--配置事物参数-->
<tx:attributes>
<!--规定哪个方法上添加事物,并设置事物参数-->
<tx:method name="accountMoney"/>
</tx:attributes>
</tx:advice>

<!--配置切入点和切面-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pt" expression="(execution(* project.service.*(..)))"/>
<!--配置切面-->
<aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
</aop:config>
```

#### (4)完全注解声明事物管理

- 创建配置类代替xml配置文件

```java
/*使用配置类的方式替代SpringXml配置文件*/
@Configuration //设为配置类
@ComponentScan(basePackages = "project") //设置组件扫描
@EnableTransactionManagement //开启事物
public class TxConfig {

/*创建数据库连接池*/
@Bean
public DruidDataSource getDruidDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("502502");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
return dataSource;
}

/*创建JdbcTemplate的对象*/
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource){ //在ioc容器中根据名称寻找到dataSource对象
JdbcTemplate jdbcTemplate = new JdbcTemplate();
/*注入dataSource*/
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}

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

- 测试

```java
@Test
public void txConfigTest(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class); //配置类.class
AccountServiceImpl accountServiceImpl = context.getBean("accountServiceImpl", AccountServiceImpl.class);
accountServiceImpl.accountMoney();
}
```

## Spring5新功能

### 1.日志封装

1、Spring 5.0 框架自带了通用的日志封装

(1)Spring5 已经移除 Log4jConfigListener,官方建议使用 Log4j2

(2)Spring5 框架整合 Log4j2

2、创建 log4j2.xml 配置文件

```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.Spring5 核心容器支持函数式风格

- **@Nullable注解**

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

(2)注解用在方法上面,方法返回值可以为空

- **函数式风格创建对象,交个Spring管理**

```java
@Test
public void testGenericApplicationContext() {
//1 创建 GenericApplicationContext 对象
GenericApplicationContext context = new GenericApplicationContext();
//2 调用 context 的方法对象注册
context.refresh();
context.registerBean("user1",User.class,() -> new User());
//3 获取在 spring 注册的对象
// User user = (User)context.getBean("com.atguigu.spring5.test.User");
User user = (User)context.getBean("user1");
System.out.println(user);
}
```

### 3.Spring整合JUnit

**(1)整合JUnit4**

```java
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import project.junitTest.JunitClass;

@RunWith(SpringJUnit4ClassRunner.class) //单元测试类框架
@ContextConfiguration("classpath:bean.xml") //加载配置文件
public class Junit4Test {
/*注入被测试类的对象*/
@Autowired
private JunitClass junitClass;
/*创建单元测试*/
@Test
public void Test1(){
junitClass.method();
}
}
```

**(2)整合Junit5**

```java
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import project.junitTest.JunitClass;

@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:bean.xml")
public class Junit5Test {

@Autowired
private JunitClass junitClass;

@Test
public void Test1(){
junitClass.method();
}
}
```

**(3)复合注解整合JUnit5**

```java
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import project.junitTest.JunitClass;

@SpringJUnitConfig(locations="classpath:bean.xml") //合并测试类注解
public class JunitConfigTest {

@Autowired
private JunitClass junitClass;

@Test
public void Test(){
junitClass.method();
}
}
```

### 4.Webflux

#### (1)SpringWebflux介绍

- SpringWebflux是 Spring5 添加新的模块,用于 web 开发的,功能和 SpringMVC 类似的,Webflux 使用 当前一种比较流行响应式编程出现的框架。

- 使用传统 web 框架,比如 SpringMVC,这些基于 Servlet 容器,Webflux 是一种异步非阻 塞的框架,异步非阻塞的框架在 Servlet3.1 以后才支持,核心是基于 Reactor 的相关 API 实现 的。

- 解释什么是异步非阻塞

异步和同步

非阻塞和阻塞

上面都是针对对象不一样

- 异步和同步针对调用者,调用者发送请求,如果等着对方回应之后才去做其他事情就是同 步,如果发送请求之后不等着对方回应就去做其他事情就是异步

- 阻塞和非阻塞针对被调用者,被调用者受到请求之后,做完请求任务之后才给出反馈就是阻 塞,受到请求之后马上给出反馈然后再去做事情就是非阻塞

- Webflux特点:

第一 非阻塞式:在有限资源下,提高系统吞吐量和伸缩性,以 Reactor 为基础实现响应式编程

第二 函数式编程:Spring5 框架基于 java8,Webflux 使用 Java8 函数式编程方式实现路由请求

- 比较 SpringMVC

![](C:\Users\Odin\Desktop\Odin\Typora photo\Snipaste_2021-12-17_17-32-13.png)

第一 两个框架都可以使用注解方式,都运行在 Tomet 等容器中

第二 SpringMVC 采用命令式编程,Webflux 采用异步响应式编程

发表回复

您的电子邮箱地址不会被公开。