Spring5

Spring5

一、Spring5入门

1.1、Spring概念

https://repo.spring.io/ui/native/release/org/springframework/spring/
Spring是一个JavaEE开源的轻量级别的框架,可以解决我们企业开发中遇到的难题,
能够让编码变的更加简单,核心组件 IOC容器和Aop面向切面编程。

1.2、Spring 特点

(1)方便解耦,简化开发
(2)Aop 编程支持
(3)方便程序测试
(4)方便和其他框架进行整合
(5)方便进行事务操作
(6)降低 API 开发难度

1.3、Spring 有两个核心部分:IOC 和 Aop

  • IOC 控制反转:把整个对象创建的过程,统一交给我们SpringIOC容器来实现管理,底层使用反射+工厂模式实现。
  • AOP面向切面编程:不修改源代码对我们功能(方法)前后实现增强,比如打印日志、事务原理、权限管理,底层是基于动态代理模式实现的,减少代码的冗余性问题。

1.4、Spring与SpringBoot关系

SpringBoot直接采用注解化的方式启动,底层会依赖于Spring/SpringMVC注解方式启动。

总结:SpringBoot底层基于Spring/SpringMVC注解化方式实现包装。

1.5、Spring5 入门案例

maven

 <dependencies>
    <!--spring-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.9</version>
    </dependency>
</dependencies>

spring-content.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user" class="com.java521.domain.User"></bean>
</beans>

domain

public class User {
    private String name;
    private Integer age;

    public User() {
    }

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

测试 Test01

public class Test01 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-content.xml");
        User user = context.getBean("user", User.class);
        System.out.println("user = " + user);
    }
}

二、IOC(概念和原理)

2.1、什么是 IOC

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

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

2.2、IOC容器底层实现原理

  • IOC容器中非常核心的接口 BeanFactory
    BeanFactory
    Bean对象 Factory工厂

  • IOC容器基本的概念:控制反转
    把对象的创建过程与使用统一都交给我们的Spring来进行原理。
    不需要开发者自己去new对象

  • IOC容器底层实现技术:反射技术、解析xml、工厂模式

  • IOC作用 降低我们代码的耦合度。

2.3、创建对象的方式有那些:

  • 单独new方式—耦合度太高了
    每次单独new对象,没有实现统一管理对象,如果后期userDao的名称信息发生变化的情况下,需要改变的引用地方比较多,耦合度太高。

  • 工厂模式—降低我们耦合度
    统一的管理和维护我们每个对象创建与使用的过程。不需要自己new对象。

  • 反射的方式
    降低代码的-耦合度

SpringIOC容器底层实现原理:反射+工厂模式+解析xml技术实现

  • 使用解析xml技术 解析spring.xml配置文件;
  • 获取<bean id="" class=""/> 类的完整路径地址
  • 使用到反射技术初始化对象
  • 需要使用工厂模式封装初始化对象

2.4、IOC核心的接口

IOC的核心思想底层基于反射+工厂模式实现
Spring提供IOC容器实现两种方式:

  • BeanFactory IOC容器基本的实现,是spring内部自己使用的接口,不提供给开发者使用。
    加载配置文件过程的时候,不会创建对象,当我们在获取对象的时候才会获取创建对象。
  • ApplicationContext BeanFactory 接口的子接口,提供更多的强大功能,适合于开发者使用。

当我们在加载配置文件的过程中,就会将配置文件中的对象创建。

在做服务器端开发的时候,使用ApplicationContext 比较多,因为所有bean初始化操作在项目启动完成之前都已经初始化了。
##2.5、ApplicationContext主要实现类
ClassPathXmlApplicationContext:对应类路径下的XML格式的配置文件

FileSystemXmlApplicationContext:对应文件系统中的XML格式的配置文件

ConfigurableApplicationContext 是ApplicationContext的子接口,包含一些扩展方法

refresh()和close()让ApplicationContext具有启动、关闭和刷新上下文的能力。所以要关闭ApplicationContext需要new此接口的对象调用close()方法

WebApplicationContext 专门为WEB应用而准备的,它允许从相对于WEB根目录的路径中完成初始化工作

三、DI 依赖注入

3.1、基于 xml 方式创建对象

<bean id="user" class="com.java521.domain.User"></bean>

3.2、使用 set 方法进行注入

<bean id="user2" class="com.java521.domain.User">
    <property name="name" value="哈哈"></property>
    <property name="age" value="27"></property>
</bean>

3.3、基于有参构造方式注入属性

<!--constructor-arg name  指定参数列表名称-->
<bean id="user3" class="com.java521.domain.User">
    <constructor-arg name="name" value="java521"></constructor-arg>
    <constructor-arg name="age" value="25"></constructor-arg>
</bean>

<!--constructor-arg index 指定参数列表索引-->
<bean id="user4" class="com.java521.domain.User">
    <constructor-arg index="0" value="hello"></constructor-arg>
    <constructor-arg index="1" value="18"></constructor-arg>
</bean>

3.4、p名称空间注入

使用p标签为属性注入值:调用set方法注入值

  1. Xml头部引入P标签:
<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	<bean id="user5" class="com.java521.domain.User" p:name="我是p名称空间" p:age="19"></bean>
</beans>

3.5、注入空值属性

<bean id="user6" class="com.java521.domain.User">
    <property name="name" value="zhangsan"></property>
    <property name="age">
        <null></null>
    </property>
</bean>

3.6、注入特殊符号

<bean id="user7" class="com.java521.domain.User">
    <property name="name" value="&gt;&gt;zhangsan&lt;&lt;"></property>
    <property name="age">
        <null></null>
    </property>
</bean>

3.7、CDATA注入方式

<![CDATA[<<>>]]>
<bean id="user8" class="com.java521.domain.User">
    <property name="name" >
        <value>
            <![CDATA[56wq<<!@#$%^^&*(){}[];';.,`/>>]]>
        </value>
    </property>
    <property name="age">
        <null></null>
    </property>
</bean>

3.8、注入外部 bean

public class UserService {
    //创建 UserDao 类型属性,生成 set 方法
    private UserDao userDao;

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

    public void add() {
        System.out.println("service add......");
        userDao.update();
    }
}
public interface UserDao {
    void update();
}
public class UserDaoImpl implements UserDao {
    @Override
    public void update() {
        System.out.println("dao update......");
    }
}
<bean id="userService" class="com.java521.demo02.service.UserService">
    <property name="userDao" ref="userDao"></property>
</bean>

<bean id="userDao" class="com.java521.demo02.dao.impl.UserDaoImpl"></bean>
public class Test01 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-content.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
    }
}

3.9、注入内部 bean

public class Dept {
    private String name;

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

    public String getName() {
        return name;
    }

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

    public Dept() {
    }

    public Dept(String name) {
        this.name = name;
    }
}
public class Emp {
    private String name;
    private Integer age;
    private  Dept dept;

    public Emp() {
    }

    public Emp(String name, Integer age, Dept dept) {
        this.name = name;
        this.age = age;
        this.dept = dept;
    }

    public String getName() {
        return name;
    }


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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Dept getDept() {
        return dept;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", dept=" + dept +
                '}';
    }
}
<bean id="emp" class="com.java521.domain.Emp">
    <property name="name" value="lisi"></property>
    <property name="age" value="19"></property>
    <property name="dept">
        <bean id="dept" class="com.java521.domain.Dept">
            <property name="name" value="开发部"></property>
        </bean>
    </property>
</bean>
public class Test02 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-content.xml");
        Emp emp = context.getBean("emp", Emp.class);
        System.out.println("emp = " + emp);
    }
}

3.10、注入级联赋值

<!--写法1-->
<bean id="emp" class="com.java521.domain.Emp">
    <property name="name" value="lisi"></property>
    <property name="age" value="19"></property>
    <property name="dept">
        <bean id="dept" class="com.java521.domain.Dept">
            <property name="name" value="开发部"></property>
        </bean>
    </property>
</bean>

<!--写法2-->
<bean id="emp2" class="com.java521.domain.Emp">
    <property name="name" value="wangwu"></property>
    <property name="age" value="22"></property>
    <property name="dept" ref="dept"></property>
</bean>

<bean id="dept" class="com.java521.domain.Dept">
    <property name="name" value="项目部"></property>
</bean>

3.11、注入集合属性

  • 注入数组类型属性
  • 注入 List 集合类型属性
  • 注入 Map 集合类型属性
<bean id="stu" class="com.java521.domain.Stu">
    <property name="courses">
        <array>
            <value>java</value>
            <value>php</value>
            <value>python</value>
        </array>
    </property>

    <property name="list">
        <list>
            <value>reading</value>
            <value>sporting</value>
        </list>
    </property>

    <property name="sets">
        <set>
            <value>旅游</value>
            <value>美食</value>
        </set>
    </property>
    <property name="maps">
        <map>
            <entry key="php" value="no.1"></entry>
            <entry key="java" value="no.2"></entry>
            <entry key="c" value="no.3"></entry>
        </map>
    </property>
</bean>
public class Stu {
    //1 数组类型属性
    private String[] courses;
    //2 list 集合类型属性
    private List<String> list;
    //3 map 集合类型属性
    private Map<String,String> maps;
    //4 set 集合类型属性
    private Set<String> 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;
    }

    public void setSets(Set<String> sets) {
        this.sets = sets;
    }

    @Override
    public String toString() {
        return "Stu{" +
                "courses=" + Arrays.toString(courses) +
                ", list=" + list +
                ", maps=" + maps +
                ", sets=" + sets +
                '}';
    }
}
public class Test03 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-content.xml");

        Stu stu = context.getBean("stu", Stu.class);
        System.out.println("stu = " + stu);
    }
}

四、FactoryBean

4.1、Bean的分类

1、Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)
2、普通 bean:在配置文件中定义 bean 类型就是返回类型
3、工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样

第一步 创建类,让这个类作为工厂 bean,实现接口 FactoryBean
第二步 实现接口里面的方法,在实现的方法中定义返回的 bean 类型

public class MyBean implements FactoryBean<User> {
    @Override
    public User getObject() throws Exception {
        User user = new User();
        user.setName("zhangsan");
        return user;
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }
	
	@Override
    public boolean isSingleton() {
        return false;
    }
}
<bean id="myBean" class="com.java521.demo02.MyBean"></bean>
public class Test05 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-content.xml");
        User user= context.getBean("myBean", User.class);
        System.out.println(user);
    }
}

4.2、bean的name属性

类别描述
名称name
类型属性
所属bean标签
功能定义bean的别名,可定义多个,使用逗号(,)分号(;)空格( )分隔
范例<bean id=“bookDao” name=“dao dao2 dao3”
class=“com.java521.service.impl.BookDaoImpl”/>

4.3、为什么bean默认为单例?

  • bean为单例的意思是在Spring的IOC容器中只会有该类的一个对象
  • bean对象只有一个就避免了对象的频繁创建与销毁,达到了bean对象的复用,性能高

4.4、bean在容器中是单例的,会不会产生线程安全问题?

  • 如果对象是有状态对象,即该对象有成员变量可以用来存储数据的,
  • 因为所有请求线程共用一个bean对象,所以会存在线程安全问题。
  • 如果对象是无状态对象,即该对象没有成员变量没有进行数据存储的,
  • 因方法中的局部变量在方法调用完成后会被销毁,所以不会存在线程安全问题。

4.5、哪些bean对象适合/不适合交给容器进行管理?

适合

  • 表现层对象
  • 业务层对象
  • 数据层对象
  • 工具对象

不适合

  • 封装实例的域对象,因为会引发线程安全问题,所以不适合。

4.6、bean实例化

实例化bean的三种方式,构造方法静态工厂实例工厂

bean本质上就是对象,对象在new的时候会使用构造方法完成,那创建bean也是使用构造方法完成的。

五、bean 作用域

在 Spring 里面,默认情况下,bean 是单实例对象

public class Books {
    private String name;
    private Double price;

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

    public void setPrice(Double price) {
        this.price = price;
    }
}
<bean id="books" class="com.java521.domain.Books"></bean>
public class Test06 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-content.xml");
        Books books1 = context.getBean("books", Books.class);
        Books books2 = context.getBean("books", Books.class);
        System.out.println(books1);
        System.out.println(books2);
    }
}

在这里插入图片描述
如何设置单实例还是多实例

  • 在 spring 配置文件 bean 标签里面有属性(scope)用于设置单实例还是多实例
  • scope 属性值
    singleton,默认值,表示是单实例对象
    prototype,表示是多实例对象
  <bean id="books" class="com.java521.domain.Books" scope="prototype"></bean>

在这里插入图片描述

singleton 和 prototype 区别:singleton 单实例,prototype 多实例

  • 设置 scope 值是 singleton 时候,加载 spring 配置文件时候就会创建单实例对象
  • 设置 scope 值是 prototype 时候,不是在加载 spring 配置文件时候创建 对象,在调用getBean 方法时候创建多实例对象

六、bean 生命周期

生命周期:从对象创建到对象销毁的过程

bean 生命周期

  1. 通过构造器创建 bean 实例(无参数构造)
  2. 为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
  3. 调用 bean 的初始化的方法(需要进行配置初始化的方法)
  4. bean 可以使用了(对象获取到了)
  5. 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
public class Orders {

    private String name;

    public Orders() {
        System.out.println("第一步:调用无参构造创建bean实例");
    }

    public void setName(String name) {
        this.name = name;
        System.out.println("第二步:调用set方法设置属性值");
    }

    public void initMethod(){
        System.out.println("第三步:执行初始化方法");
    }

    public void desotryMethod(){
        System.out.println("第五步:执行销毁的方法");
    }
}
<bean id="orders" class="com.java521.domain.Orders" init-method="initMethod" destroy-method="desotryMethod">
    <property name="name" value="huawei"></property>
</bean>
public class Test07 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-content.xml");
        Orders orders = context.getBean("orders", Orders.class);
        System.out.println("第四步:获取创建 bean 实例对象");
        System.out.println(orders);
        context.close();
    }
}

在这里插入图片描述
bean 的后置处理器,bean 生命周期有七步

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

    private String name;

    public Orders() {
        System.out.println("第一步:调用无参构造创建bean实例");
    }

    public void setName(String name) {
        this.name = name;
        System.out.println("第二步:调用set方法设置属性值");
    }

    public void initMethod() {
        System.out.println("第四步:执行初始化方法");
    }

    public void desotryMethod() {
        System.out.println("第七步:执行销毁的方法");
    }
}

spring-content2.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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


    <bean id="orders" class="com.java521.domain.Orders" init-method="initMethod" destroy-method="desotryMethod">
        <property name="name" value="xiaomi"></property>
    </bean>

    <!--配置后置处理器-->
    <bean id="myBeanPost" class="com.java521.demo02.MyBeanPost"></bean>
</beans>
public class Test08 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-content2.xml");
        Orders orders = context.getBean("orders", Orders.class);
        System.out.println("第六步:获取创建 bean 实例对象");
        System.out.println(orders);
        context.close();
    }
}

七、xml 自动装配

自动装配是根据指定装配规则(属性名称或者属性类型),Spring 自动将匹配的属性值进行注入

bean 标签属性 autowire,配置自动装配,autowire 属性常用两个值

  1. byName 根据属性名称注入 ,注入值 bean 的 id 值和类属性名称一样
<bean id="emp" class="com.java521.domain.Emp" autowire="byName"></bean>
<bean id="dept" class="com.java521.domain.Dept" autowire="byName">
public class Test01 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-content3.xml");
        Emp emp = context.getBean("emp", Emp.class);
        System.out.println("emp = " + emp);
    }
}
  1. byType 根据属性类型注入(相同类型的bean只能有一个)
<bean id="emp" class="com.java521.domain.Emp" autowire="byType"></bean>
<bean id="dept" class="com.java521.domain.Dept" autowire="byType">

八、外部属性文件

1、直接配置数据库信息
(1)配置德鲁伊连接池
(2)引入德鲁伊连接池依赖 jar 包

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    <property name="url" value="jdbc:mysql://127.0.0.1/user"></property>
    <property name="username" value="root"></property>
    <property name="password" value="root"></property>
</bean>

2、引入外部属性文件配置数据库连接池
(1)创建外部属性文件,properties 格式文件,写数据库信息

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1/user
jdbc.username=root
jdbc.password=root
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
 <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
     <property name="driverClassName" value="${jdbc.driverClassName}"></property>
     <property name="url" value="${jdbc.url}"></property>
     <property name="username" value="${jdbc.username}"></property>
     <property name="password" value="${jdbc.password}"></property>
 </bean>

九、基于注解方式

什么是注解

  • 注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值…)
  • 使用注解,注解作用在类上面,方法上面,属性上面
  • 使用注解目的:简化 xml 配置

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

  • @Component
  • @Service
  • @Controller
  • @Repository

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

3、基于注解方式实现对象创建
第一步 引入依赖

<!--spring aop-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.3.9</version>
</dependency>

第二步 开启组件扫描

<!--开启组件扫描
     1 如果扫描多个包,多个包使用逗号隔开
     2 扫描包上层目录
-->
<context:component-scan base-package="com.java521"/>

第三步 创建类,在类上面添加创建对象注解

@Component(value = "userService")
public class UserService {

    public void add(){
        System.out.println("service add ......");
    }
}
public class Test01 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-content5.xml");
        UserService userService = context.getBean("userService", UserService.class);
        System.out.println(userService);
        userService.add();
    }
}

4、开启组件扫描细节配置

<!--开启组件扫描
     1 如果扫描多个包,多个包使用逗号隔开
     2 扫描包上层目录
-->
<context:component-scan base-package="com.java521"/>

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

<!--示例 2
    context:exclude-filter  设置哪些内容不进行扫描
-->
<context:component-scan base-package="com.java521" use-default-filters="false">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>

5、基于注解方式实现属性注入

  • @Autowired:根据属性类型进行自动装配
  • @Qualifier:根据名称进行注入
  • @Resource:可以根据类型注入,可以根据名称注入
  • @Value:注入普通类型属性

(1)@Autowired:根据属性类型进行自动装配
第一步 把 service 和 dao 对象创建,在 service 和 dao 类添加创建对象注解
第二步 在 service 注入 dao 对象,在 service 类添加 dao 类型属性,在属性上面使用注解

public interface UserDao {
    void add();
}
@Repository
public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        System.out.println("userDao  add  ...");
    }
}
@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    public void add() {
        System.out.println("service add ......");
        userDao.add();
    }
}

(2)@Qualifier:根据名称进行注入
这个@Qualifier 注解的使用,和上面@Autowired 一起使用

@Service
public class UserService {

    @Autowired
    @Qualifier(value = "userDaoImpl1")
    private UserDao userDao;

    public void add() {
        System.out.println("service add ......");
        userDao.add();
    }
}
@Repository(value = "userDaoImpl1")
public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        System.out.println("userDao  add  ...");
    }
}

(3)@Resource:可以根据类型注入,可以根据名称注入

@Service
public class UserService {
    //根据类型注入
    //@Resource
    //根据名称注入
    @Resource(name = "userDaoImpl1")
    private UserDao userDao;
    public void add() {
        System.out.println("service add ......");
        userDao.add();
    }
}

(4)@Value:注入普通类型属性

@Service
public class UserService {
    //根据类型注入
    //@Resource
    //根据名称注入
    @Resource(name = "userDaoImpl1")
    private UserDao userDao;

    @Value(value = "abc")
    private String name;

    public void add() {
        System.out.println("service add ......" + name);
        userDao.add();
    }
}

6、完全注解开发
(1)创建配置类,替代 xml 配置文件

@Configuration
@ComponentScan(basePackages = "com.java521")
public class SpringConfig {
}

(2)编写测试类

public class Test02 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserService userService = context.getBean("userService", UserService.class);
        System.out.println(userService);
        userService.add();
    }
}

十、AOP(概念)

10.1、什么是 AOP

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

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

使用登录例子说明 AOP
在这里插入图片描述

10.2、AOP底层原理

AOP 底层使用动态代理
(1)有两种情况动态代理

  • 有接口情况,使用 JDK 动态代理
    创建接口实现类代理对象,增强类的方法

  • 没有接口情况,使用 CGLIB 动态代理
    创建子类的代理对象,增强类的方法

10.3、AOP术语

  • 连接点
    类里面哪些方法可以被增强,这些方法称为连接点

  • 切入点
    实际被真正增强的方法,称为切入点

  • 通知(增强)
    实际增强的逻辑部分称为通知(增强)
    通知有多种类型
    – 前置通知
    – 后置通知
    – 环绕通知
    – 异常通知
    – 最终通知

  • 切面
    是动作,把通知应用到切入点过程

10.4、准备工作

1、Spring 框架一般都是基于 AspectJ 实现 AOP 操作
AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使用,进行 AOP 操作

2、基于 AspectJ 实现 AOP 操作
(1)基于 xml 配置文件实现
(2)基于注解方式实现(使用)

3、在项目工程里面引入 AOP 相关依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.9</version>
</dependency>

<!--spring aop-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.3.9</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.3.9</version>
</dependency>

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

语法结构: execution([权限修饰符] [返回类型] [类全路径] 方法名称 )

举例 1:对 com.java521.dao.BookDao 类里面的 add 进行增强

execution(* com.java521.dao.BookDao.add(..))

举例 2:对 com.java521.dao.BookDao 类里面的所有的方法进行增强

execution(* com.java521.dao.BookDao.* (..))

举例 3:对 com.java521.dao 包里面所有类,类里面所有方法进行增强

execution(* com.java521.dao.*.* (..))

10.5、AspectJ 注解

1、创建类,在类里面定义方法

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

2、创建增强类(编写增强逻辑)
在增强类里面,创建方法,让不同方法代表不同通知类型

public class UserProxy {
    //前置通知
    public void before() {
        System.out.println("user before...");
    }
}

3、进行通知的配置
在 spring 配置文件中,开启注解扫描

<?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 https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.java521.anpanno"></context:component-scan>
</beans>

使用注解创建 User 和 UserProxy 对象

@Component
public class User {

@Component
public class UserProxy {

在增强类上面添加注解 @Aspect

@Component
@Aspect //生成代理对象
public class UserProxy {

在 spring 配置文件中开启生成代理对象

<?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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:component-scan base-package="com.java521.anpanno"></context:component-scan>
    
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

4、配置不同类型的通知
在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置

@Component
@Aspect //生成代理对象
public class UserProxy {
    //前置通知
    @Before(value = "execution(* com.java521.anpanno.User.add(..))")
    public void before() {
        System.out.println("user before...");
    }

    //后置通知(返回通知)
    @AfterReturning(value = "execution(* com.java521.anpanno.User.add(..))")
    public void afterReturning() {
        System.out.println("afterReturning.........");
    }
    //最终通知
    @After(value = "execution(* com.java521.anpanno.User.add(..))")
    public void after() {
        System.out.println("after.........");
    }
    //异常通知
    @AfterThrowing(value = "execution(* com.java521.anpanno.User.add(..))")
    public void afterThrowing() {
        System.out.println("afterThrowing.........");
    }
    //环绕通知
    @Around(value = "execution(* com.java521.anpanno.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws
            Throwable {
        System.out.println("环绕之前.........");
        //被增强的方法执行
        proceedingJoinPoint.proceed();
        System.out.println("环绕之后.........");
    }
}

相同的切入点抽取

@Component
@Aspect //生成代理对象
public class UserProxy {
    //相同切入点抽取
    @Pointcut(value = "execution(* com.java521.anpanno.User.add(..))")
    public void pointdemo() {
    }
  
    //前置通知
    @Before(value = "pointdemo()")
    public void before() {
        System.out.println("user before...");
    }
    //后置通知(返回通知)
    @AfterReturning(value = "execution(* com.java521.anpanno.User.add(..))")
    public void afterReturning() {
        System.out.println("afterReturning.........");
    }
    //最终通知
    @After(value = "execution(* com.java521.anpanno.User.add(..))")
    public void after() {
        System.out.println("after.........");
    }
    //异常通知
    @AfterThrowing(value = "execution(* com.java521.anpanno.User.add(..))")
    public void afterThrowing() {
        System.out.println("afterThrowing.........");
    }
    //环绕通知
    @Around(value = "execution(* com.java521.anpanno.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws
            Throwable {
        System.out.println("环绕之前.........");
        //被增强的方法执行
        proceedingJoinPoint.proceed();
        System.out.println("环绕之后.........");
    }
}

6、有多个增强类多同一个方法进行增强,设置增强类优先级
在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高

@Component
@Aspect
@Order(1)
public class PersonProxy {
    //前置通知
    @Before(value = "execution(* com.java521.anpanno.User.add(..))")
    public void before() {
        System.out.println("person before...");
    }
}

7、完全使用注解开发
创建配置类,不需要创建 xml 配置文件

@Configuration
@ComponentScan(basePackages = {"com.java521"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop {
}

10.6、AspectJ 配置文件

1、创建两个类,增强类和被增强类,创建方法

public class Book {
    public void buy(){
        System.out.println("book buy ...");
    }
}
public class BookProxy {
    public void before() {
        System.out.println("BookProxy buy before ...");
    }
}

2、在 spring 配置文件中创建两个类对象

<!--创建对象-->
<bean id="book" class="com.java521.aopxml.Book"></bean>
<bean id="bookProxy" class="com.java521.aopxml.BookProxy"></bean>

3、在 spring 配置文件中配置切入点

<?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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--创建对象-->
    <bean id="book" class="com.java521.aopxml.Book"></bean>
    <bean id="bookProxy" class="com.java521.aopxml.BookProxy"></bean>


    <!--配置 aop 增强-->

    <aop:config>
        <aop:pointcut id="p" expression="execution(* com.java521.aopxml.Book.buy(..)))"/>
        <aop:aspect ref="bookProxy">
            <aop:before method="before" pointcut-ref="p"/>
        </aop:aspect>
    </aop:config>
</beans>

测试类

public class Test01 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-content7.xml");
        Book book = context.getBean("book", Book.class);
        book.buy();
    }
}

十一、JdbcTemplate

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

11.1、准备工作

导入依赖

<dependencies>
    <!--mysql-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.26</version>
    </dependency>

    <!--druid-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.6</version>
    </dependency>


    <!--事务-->
    <dependency>
        <groupId>org.springframework</groupId>
         <artifactId>spring-tx</artifactId>
         <version>5.3.9</version>
     </dependency>

    <!--orm-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>5.3.9</version>
    </dependency>
    
    <!--spring-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.9</version>
    </dependency>

    <!--spring aop-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>5.3.9</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.3.9</version>
    </dependency>

    <!--lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.20</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

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

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}"></property>
    <property name="url" value="${jdbc.url}"></property>
    <property name="username" value="${jdbc.username}"></property>
    <property name="password" value="${jdbc.password}"></property>
</bean>

配置 JdbcTemplate 对象,注入 DataSource

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

组件扫描

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

创建 service 类,创建 dao 类,在 dao 注入 jdbcTemplate 对象

public class UserService {
    //注入 dao
    @Autowired
    private UserDao userDao;
}
public interface UserDao {

}

@Repository
public class UserDaoImpl implements UserDao {
    //注入 JdbcTemplate
    @Autowired
    private JdbcTemplate jdbcTemplate;
}

11.2、JdbcTemplate 操作数据库(添加)

1、对应数据库创建实体类

@Data
public class User {
    private int id;
    private String name;
    private int age;
    private String email;
}

2、编写 service 和 dao

public class UserService {
    //注入 dao
    @Autowired
    private UserDao userDao;

    public void addUser(User user){
        userDao.addUser(user);
    }
}

在 dao 进行数据库添加操作
调用 JdbcTemplate 对象里面 update 方法实现添加操作
有两个参数

  • 第一个参数:sql 语句
  • 第二个参数:可变参数,设置 sql 语句值
public interface UserDao {
    void addUser(User user);
}
@Repository
public class UserDaoImpl implements UserDao {
    //注入 JdbcTemplate
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void addUser(User user) {
        String sql = "insert into user values(?, ?, ?, ?)";
        Object[] args = {user.getId(), user.getName(), user.getAge(), user.getEmail()};

        jdbcTemplate.update(sql, args);
    }
}

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

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

    <bean id="userService" class="com.java521.jdbc.UserService"/>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://127.0.0.1/user"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
</beans>

测试

public class TestJdbcTemplate {

    public static void main(String[] args) {

        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-jdbc.xml");
        UserService userService = context.getBean("userService", UserService.class);
        User user = new User();
        user.setId(7);
        user.setName("张三");
        user.setAge(18);
        user.setEmail("123456@163.com");
        userService.addUser(user);
    }
}

11.3、修改和删除

1、修改

@Test
public void updateUser() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-jdbc.xml");
    UserService userService = context.getBean("userService", UserService.class);
    User user = new User();
    user.setId(7);
    user.setName("张三三");
    user.setAge(19);
    user.setEmail("123456@qq.com");
    userService.updateUser(user);
}
@Override
public void updateUser(User user) {

    String sql = "update user set name= ?, age =?, email=? where id = ?";
    Object[] args = {user.getName(), user.getAge(), user.getEmail(), user.getId()};

    int update = jdbcTemplate.update(sql, args);
    System.out.println(update);
}

2、删除

@Test
public void deleteUser() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-jdbc.xml");
    UserService userService = context.getBean("userService", UserService.class);
    int id = 7;
    userService.deleteUser(id);
}
@Override
public void deleteUser(int id) {
    String sql = "delete from user where id = ?";
    int update = jdbcTemplate.update(sql, id);
    System.out.println(update);
}

11.4、查询返回某个值

1、查询表里面有多少条记录,返回是某个值
2、使用 JdbcTemplate 实现查询返回某个值代码,有两个参数

  • 第一个参数:sql 语句
  • 第二个参数:返回类型 Clas
@Test
public void selectCount() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-jdbc.xml");
    UserService userService = context.getBean("userService", UserService.class);
    int count = userService.selectCount();
    System.out.println(count);
}
@Override
public int selectCount() {
    String sql = "select count(*) from user";
    Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
    return count;
}

11.5、查询返回对象

1、场景:查询图书详情
2、JdbcTemplate 实现查询返回对象,有三个参数

  • 第一个参数:sql 语句
  • 第二个参数:RowMapper 是接口,针对返回不同类型数据,使用这个接口里面实现类完成数据封装
  • 第三个参数:sql 语句值
@Test
public void findUserById() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-jdbc.xml");
    UserService userService = context.getBean("userService", UserService.class);
    int id = 6;
    User user = userService.findUserById(id);
    System.out.println(user);
}
@Override
public User findUserById(int id) {
    String sql = "select * from user where id =?";
    User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), id);
    return user;
}

11.6、查询返回集合

1、场景:查询图书列表分页…
2、调用 JdbcTemplate 方法实现查询返回集合, 有三个参数

  • 第一个参数:sql 语句
  • 第二个参数:RowMapper 是接口,针对返回不同类型数据,使用这个接口里面实现类完成数据封装
  • 第三个参数:sql 语句值
@Test
public void getUserList() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-jdbc.xml");
    UserService userService = context.getBean("userService", UserService.class);
    List<User> userList = userService.getUserList();
    System.out.println(userList);
}
@Override
public List<User> getUserList() {
    String sql = "select * from user";
    List<User> userList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<User>(User.class));
    return userList;
}

11.7、批量操作

1、批量操作:操作表里面多条记录
2、JdbcTemplate 实现批量添加操作,有两个参数

  • 第一个参数:sql 语句
  • 第二个参数:List 集合,添加多条记录数据
@Test
public void batchAddUser() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-jdbc.xml");
    UserService userService = context.getBean("userService", UserService.class);
    List<Object[]> list = new ArrayList<>();

    Object[] o1 = {10, "java", 19, "123456@163.com"};
    Object[] o2 = {11, "php", 24, "12345@163.com"};
    Object[] o3 = {12, "python", 18, "1234@163.com"};
    list.add(o1);
    list.add(o2);
    list.add(o3);
    userService.batchAddUser(list);
}
@Override
public void batchAddUser(List<Object[]> list) {
    String sql = "insert into user values(?,?,?,?)";
    int[] ints = jdbcTemplate.batchUpdate(sql, list);
    System.out.println(Arrays.toString(ints));
}

3、JdbcTemplate 实现批量修改操作

@Test
public void batchUpdateUser() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-jdbc.xml");
    UserService userService = context.getBean("userService", UserService.class);
    List<Object[]> list = new ArrayList<>();

    Object[] o1 = {"java", 22, "123456@163.com", 10};
    Object[] o2 = {"php", 22, "123456@163.com", 11};
    Object[] o3 = {"python", 22, "123456@163.com", 12};
    list.add(o1);
    list.add(o2);
    list.add(o3);
    userService.batchUpdateUser(list);
}
@Override
public void batchUpdateUser(List<Object[]> list) {
    String sql = "update user set name=?, age=?, email=? where id = ?";
    int[] ints = jdbcTemplate.batchUpdate(sql, list);
    System.out.println(Arrays.toString(ints));
}

4、JdbcTemplate 实现批量删除操作

@Test
public void batchDeleteUser() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-jdbc.xml");
    UserService userService = context.getBean("userService", UserService.class);
    List<Object[]> list = new ArrayList<>();

    Object[] o1 = {10};
    Object[] o2 = {11};
    Object[] o3 = {12};
    list.add(o1);
    list.add(o2);
    list.add(o3);
    userService.batchDeleteUser(list);
}
@Override
public void batchDeleteUser(List<Object[]> list) {
    String sql = "delete from user where id=?";
    int[] ints = jdbcTemplate.batchUpdate(sql, list);
    System.out.println(Arrays.toString(ints));
}

十二、事务操作

12.1、事务概念

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

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

事务四个特性(ACID)

  • 原子性
  • 一致性
  • 隔离性
  • 持久性

2、环境搭建
创建sq语句

CREATE TABLE `user`.`Untitled`  (
  `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `money` int(10) NULL DEFAULT NULL,
  PRIMARY KEY (`name`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

INSERT INTO `user`.`account`(`name`, `money`) VALUES ('zhangsan', 1000);
INSERT INTO `user`.`account`(`name`, `money`) VALUES ('lisi', 1000);

配置文件

<?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 https://www.springframework.org/schema/context/spring-context.xsd">

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

    <bean id="userService" class="com.java521.transactional.UserService"/>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://127.0.0.1/user"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
</beans>
@Service
public class UserService {
    @Autowired
    private UserDao userDao;

 	public void accountMoney() {
		userDao.reduceMoney();
        userDao.addMoney();
    }
}
@Repository
public class UserDaoImpl implements UserDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void reduceMoney() {
        String sql = "update account set money = money - ? where name = ?";
        int update = jdbcTemplate.update(sql, 100, "zhangsan");
        System.out.println("减钱成功条数: " + update);
    }

    @Override
    public void addMoney() {
        String sql = "update account set money = money + ? where name = ?";
        int update = jdbcTemplate.update(sql, 100, "lisi");
        System.out.println("加钱成功条数: " + update);
    }
}
public class TransactionalTest {
    @Test
    public void accountMoney1() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-transactional.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.accountMoney();

    }
}

上面代码,如果正常执行没有问题的,但是如果代码执行过程中出现异常,有问题
zhangsan减了100,而lisi却没有增加

@Service
public class UserService {
    @Autowired
    private UserDao userDao;

 	public void accountMoney() {
		userDao.reduceMoney();
		 System.out.println(1/0);
        userDao.addMoney();
    }
}![请添加图片描述](https://img-blog.csdnimg.cn/2f6d9ea9735c4c15b34d703a63ce7fe1.bmp?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55So55Sf5ZG956CU5Y-R5oqA5pyv,size_17,color_FFFFFF,t_70,g_se,x_16)

在这里插入图片描述
遇到这样题可以使用事务进行解决

事务操作过程

public class TransactionalTest {
    @Test
    public void accountMoney() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-transactional.xml");
        UserService userService = context.getBean("userService", UserService.class);
        try {
            //1.开启事务

            //2.进行业务操作
            userService.reduceMoney();
            //模拟异常
            System.out.println(1/0);
            userService.addMoney();
            //3.没有发生异常, 提交事务

        } catch (Exception e) {
            //4.发生异常, 回滚事务
            e.printStackTrace();
        }
    }
}

12.2、Spring 事务管理介绍

事务添加到 JavaEE 三层结构里面 Service 层(业务逻辑层)

在 Spring 进行事务管理操作有两种方式

  • 编程式事务管理
  • 声明式事务管理(使用)

声明式事务管理

  • 基于注解方式(使用)
  • 基于 xml 配置文件方式

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

Spring 事务管理 API
提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类

12.3、注解声明式事务管理

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

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

1、在 service 类上面(或者 service 类里面方法上面)添加事务注解

  • @Transactional,这个注解添加到类上面,也可以添加方法上面
  • 如果把这个注解添加类上面,这个类里面所有的方法都添加事务
  • 如果把这个注解添加方法上面,为这个方法添加事务
@Repository
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ)
public class UserDaoImpl implements UserDao {

2、propagation:事务传播行为

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

事务的传播行为可以由传播属性指定,Spring定义了7种传播行为
在这里插入图片描述
3、ioslation:事务隔离级别
事务有特性为隔离性,多事务操作之间不会产生影响。不考虑隔离性产生很多问题

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

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

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

脏 读不可重复读幻 读
READ UNCOMMITTED
(读末提交)
READ COMMITTED
(读已提交)
REPEATABLE READ
(可重复读)
SERIALIZABLE
(串行化)

4、timeout:超时时间
(1)事务需要在一定时间内进行提交,如果不提交进行回滚
(2)默认值是 -1 ,设置时间以秒单位进行计算

5、readOnly:是否只读
(1)读:查询操作,写:添加修改删除操作
(2)readOnly 默认值 false,表示可以查询,可以添加修改删除操作
(3)设置 readOnly 值是 true,设置成 true 之后,只能查询

6、rollbackFor:回滚
设置出现哪些异常进行事务回滚

7、noRollbackFor:不回滚
设置出现哪些异常不进行事务回滚

12.4、XML 声明式事务管理

1、在 spring 配置文件中进行配置
第一步 配置事务管理器
第二步 配置通知
第三步 配置切入点和切面

<?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/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

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

    <bean id="userService" class="com.java521.transactional.UserService"/>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://127.0.0.1/user"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

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

    <!--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:attributes>
    </tx:advice>

    <!--3 配置切入点和切面-->
    <aop:config>
        <aop:pointcut id="pt" expression="execution(* com.java521.transactional.UserService.*(..))"/>

        <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
    </aop:config>
</beans>
public class TransactionalTest {
    @Test
    public void accountMoney() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-transactional2.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.accountMoney();
    }
}

12.5、完全注解声明式事务管理

创建配置类,使用配置类替代 xml 配置文件

@Configuration
@ComponentScan(basePackages = "com.java521")
@EnableTransactionManagement
public class TxConfig {

    @Bean
    public DruidDataSource getDruidDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(("com.mysql.jdbc.Driver"));
        dataSource.setUrl("jdbc:mysql://127.0.0.1/user");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        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;
    }
}

测试

@Test
public void accountMoney3() {
    ApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);
    UserService userService = context.getBean("userService", UserService.class);
    userService.accountMoney();
}

十三、Spring5 框架新功能

1、整个 Spring5 框架的代码基于 Java8,运行时兼容 JDK9,许多不建议使用的类和方
法在代码库中删除
2、Spring 5.0 框架自带了通用的日志封装
(1)Spring5 已经移除 Log4jConfigListener,官方建议使用 Log4j2
(2)Spring5 框架整合 Log4j2

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.13.3</version>
</dependency>

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.13.3</version>
</dependency>

第二步 创建 log4j2.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE >
ALL -->
<!--Configuration 后面的 status 用于设置 log4j2 自身内部的信息输出,可以不设置,
当设置成 trace 时,可以看到 log4j2 内部各种详细输出-->
<configuration status="DEBUG">
    <!--先定义所有的 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>

3、Spring5 框架核心容器支持@Nullable 注解
(1)@Nullable 注解可以使用在方法上面,属性上面,参数上面,表示方法返回可以为空,属性值可以为空,参数值可以为空
(2)注解用在方法上面,方法返回值可以为空

@Nullable
String getId();

(3)注解使用在方法参数里面,方法参数可以为空

public <T> void registerBean(@Nullable String beanName, Class<T> beanClass, @Nullable Supplier<T> supplier, BeanDefinitionCustomizer... customizers) {
    this.reader.registerBean(beanClass, beanName, supplier, customizers);
}

(4)注解使用在属性上面,属性值可以为空

@Nullable
private String name;

4、Spring5 核心容器支持函数式风格 GenericApplicationContext
(1)整合 JUnit4
第一步 引入 Spring 相关针对测试依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.3.9</version>
</dependency>

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.1</version>
</dependency>

<dependency>
    <groupId>org.hamcrest</groupId>
    <artifactId>hamcrest-core</artifactId>
    <version>2.2</version>
</dependency>

第二步 创建测试类,使用注解方式完成

@RunWith(SpringJUnit4ClassRunner.class) //单元测试框架
@ContextConfiguration("classpath:spring-transactional2.xml") //加载配置文件
public class JTest4 {
    @Autowired
    private UserService userService;
    @Test
    public void test1() {
        userService.accountMoney();
    }
}

Spring5 整合 JUnit5
第一步 引入 JUnit5 的 jar 包

@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath: spring-transactional2.xml")
public class JTest5 {
    @Autowired
    private UserService userService;

    @Test
    public void test1() {
        System.out.println("userService = " + userService);
        userService.accountMoney();
    }
}

3)使用一个复合注解替代上面两个注解完成整合

@SpringJUnitConfig(locations = "classpath:spring-transactional2.xml")
public class JTest5 {
    @Autowired
    private UserService userService;

    @Test
    public void test1() {
        System.out.println("userService = " + userService);
        userService.accountMoney();
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值