Spring5学习笔记

Spring5学习笔记

文章目录

一、Spring框架概述

1、轻量级的开源JavaEE框架

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

3、核心:IOC和AOP

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

4、Spring特点:

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

5、案例

​ 官网:spring.io

​ 下载地址:https://repo.spring.io/release/org/springframework/spring/

image-20210510103739767

​ 解压后目录文件:
在这里插入图片描述

​ 导入spring核心jar包,和日志包
在这里插入图片描述

​ 创建普通类,在这个类创建普通方法:

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

​ 创建spring配置文件,在配置文件中配置创建的对象

​ 在spring中,配置文件属于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对象创建-->
    <bean id="user" class="com.tina.spring5.User"></bean>
</beans>

​ 测试代码编写

@Test
public void testAdd(){
    //加载spring的配置文件
    ClassPathXmlApplicationContext context =
            new ClassPathXmlApplicationContext("beantest.xml");t5
    //获取配置创建的对象
    User user = context.getBean("user", User.class);
    System.out.println(user);
    user.add();
}

二、IOC容器

1、IOC底层原理

(1)什么是IOC?

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

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

​ 做入门案例就是IOC实现

(2)IOC底层原理

​ xml解析、工厂模式、反射

(3)画图解释IOC底层原理

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2、IOC接口(BeanFactory)

(1)IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
(2)spring提供IOC容器的两种实现方式

​ ①、BeanFactory:IOC容器基本实现,是spring内部的使用接口,不提供给开发人员进行使用

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

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

​ 加载配置文件时,就会把在配置文件中的对象进行创建

(3)ApplacationContext接口的两个主要实现类

在这里插入图片描述

​ BeanFactory接口的两个主要实现类

在这里插入图片描述

3、Bean管理

​ 概念:Spring创建对象和Spring注入属性

​ Bean管理的两种方式:基于xml和基于注解

4、IOC操作Bean管理(基于xml)

(1)基于xml创建对象
<!--配置user对象创建-->
<!--id是类创建之后的名字,class是该类的路径-->
<bean id="user" class="com.tina.spring5.User"></bean>

​ ①、在Spring配置文件中,使用bean标签,标签里添加相应属性,就可以实现对象的创建。

​ ②、在bean标签里有很多属性,常用的属性有:

​ id属性:获取对象中唯一的标识

​ class属性:创建的类的全路径

​ name属性:和id的作用一样,不同的是name可以加特殊符号而id不可以。

​ ③、创建的时候,默认的执行无参构造方法完成对象的创建

(2)基于xml注入普通属性

方法一:使用set方法进行注入

​ ①、DI:依赖注入,就是注入属性

​ 第一步:创建类和类中的属性及其set方法

public class Book {
    private String bname;
    private String bauthor;

    public void setBauthor(String bauthor) {
        this.bauthor = bauthor;
    }

    //set方法注入属性
    public void setBname(String bname) {
        this.bname = bname;
    }

    public static void main(String[] args) {
        Book book = new Book();
        book.setBname("高级语言编译原理");
    }
}

​ 第二步:在Spring的配置文件中配置对象的创建和配置属性的注入

<bean id="book" class="com.tina.spring5.Book">
    <!--        使用property完成属性注入
            name:类中的属性名称
            value:向类中注入的属性的值
    -->
    <property name="bname" value="计算机组成原理"></property>
    <property name="bauthor" value="张荣"></property>
</bean>

​ 第三步:创建测试方法:

@Test
public void testBook(){
    ApplicationContext context =
            new ClassPathXmlApplicationContext("beantest.xml");
    Book book = context.getBean("book", Book.class);
    System.out.println("书名:"+book.getBname()+"       "+"作者:"+book.getBauthor());
}

​ 测试方法运行结果:
在这里插入图片描述

方法二使用有参构造方式

​ 第一步:创建类和类中的属性及其set方法

public class Order {
    private String oname;
    private String address;

    public String getOname() {
        return oname;
    }

    public String getAddress() {
        return address;
    }

    public Order(String oname, String address) {
        this.oname = oname;
        this.address = address;
    }
}

​ 第二步:在Spring的配置文件中配置对象的创建和配置属性的注入

<!--  使用有参构造注入属性  -->
<bean id="order" class="com.tina.spring5.Order">
    <constructor-arg name="oname" value="ipad"></constructor-arg>
    <constructor-arg name="address" value="上海"></constructor-arg>
</bean>

注意:在constructor-arg标签中有个index的属性,用来表示类中属性的索引值,也可以完成注入,但是不推荐,名称更为准确。

​ 第三步:创建测试方法:

@Test
public void testOrder(){
    ApplicationContext context =
            new ClassPathXmlApplicationContext("beantest.xml");
    Order order = context.getBean("order", Order.class);
    System.out.println("订单名称:"+ order.getOname() +"       "+"订单地址:"+order.getAddress());

}

​ 测试方法运行结果:
在这里插入图片描述

方法三:基于p名称标签注入(可简化基于xml配置方式)

​ 第一步:添加一个p名称空间在beans标签的属性中

<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">
</beans>

​ 第二步:在bean标签里进行属性的注入

<bean id="book" class="com.tina.spring5.Book" p:bname="高级语言编译原理" p:bauthor="陈火旺">
</bean>

​ 第三步:测试结果

@Test
public void testBook(){
    ApplicationContext context =
            new ClassPathXmlApplicationContext("beantest.xml");
    Book book = context.getBean("book", Book.class);
    System.out.println("书名:"+book.getBname()+"       "+"作者:"+book.getBauthor());
}

​ 结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w7xgKzvU-1625713376798)(C:\Users\kexis\AppData\Roaming\Typora\typora-user-images\image-20210515112316880.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4kikXL65-1625713782540)(C:%5CUsers%5Ckexis%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5Cimage-20210515112316880.png#pic_center)]

(3)基于xml注入其他类型的属性

①、字面量:设置属性的固定值

设置null值:在该属性的标签中只设置name属性,不设置value属性,在标签内部添加一个标签

<bean id="book" class="com.tina.spring5.Book">
        <property name="bname" value="计算机组成原理"></property>
        <property name="bauthor" value="张荣"></property>
    <property name="address" >
        <null/>
    </property>
</bean>

测试

@Test
public void testBook(){
    ApplicationContext context =
            new ClassPathXmlApplicationContext("beantest.xml");
    Book book = context.getBean("book", Book.class);
    System.out.println("书名:"+book.getBname()+"       "+"作者:"+book.getBauthor());
    book.testBook();
}

结果
在这里插入图片描述

属性值包含特殊符号

如果属性值中包含特殊符号如><,在运行时会报错org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 38 in XML document from class path resource [beantest.xml] is invalid; nested exception is org.xml.sax.SAXParseException; lineNumber: 38; columnNumber: 41; 与元素类型 “property” 相关联的 “value” 属性值不能包含 ‘<’ 字符。

为了避免这种情况发生,我们需要在使用时将它们做一些特殊处理

第一种方式:使用转义字符,如:<>等

<bean id="book" class="com.tina.spring5.Book">
        <property name="bname" value="计算机组成原理"></property>
        <property name="bauthor" value="张荣"></property>
<!--        报错-->
<!--        <property name="address" value="<北京>" ></property>-->
        <property name="address" value="&lt;北京&gt;" ></property>
    </bean>

测试代码同上

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OrSlSpNL-1625713376801)(C:\Users\kexis\AppData\Roaming\Typora\typora-user-images\image-20210515153113670.png)]

第二种方式:使用Spring自带的一种结构CDATA

配置文件的写法:

注意在内部使用了标签后,就不能再在标签中写value属性,否则编译器会报错。

<bean id="book" class="com.tina.spring5.Book">
        <property name="bname" value="计算机组成原理"></property>
        <property name="bauthor" value="张荣"></property>
        <property name="address" >
            <value>
                <![CDATA[<上海>]]>
            </value>
        </property>
</bean>

测试代码同上

结果:
在这里插入图片描述

(4)在属性中注入外部bean

创建两个类,service和dao类,在service里面调用dao里面的方法,就叫做注入外部bean

原始方法调用:

创建UserDao接口并创建它的实现类UserDaoImpl

public interface UserDao {
    public void update();
}
public class UserDaoImpl implements UserDao {
    @Override
    public void update() {
        System.out.println("dao update............");
    }
}

创建UserService类在它的add方法中创建UserDaoImpl类的对象,再调用它的update()方法

public class UserService {
    public void add(){
        System.out.println("user add.............");
        //原始调用方式
        UserDao userDao = new UserDaoImpl();
        userDao.update();
    }
}

在Spring配置文件中配置:使用ref属性

第一步:在UserService类中创建UserDao类型的属性,并生成相应的set方法

public class UserService {
    private UserDao userDao;
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    public void add(){
        System.out.println("user add.............");
        userDao.update();
    }
}

在xml文件中配置对应的bean标签

<bean id="userService" class="com.tina.spring5.service.UserService">
    <property name="userDao" ref="userDaoImpl"></property>
</bean>
<bean id="userDaoImpl" class="com.tina.spring5.dao.UserDaoImpl">
</bean>

测试方法

@Test
public void testService(){
    ApplicationContext context =
            new ClassPathXmlApplicationContext("beantest.xml");
    UserService userService = context.getBean("userService", UserService.class);
    userService.add();
}

测试结果
在这里插入图片描述

(5)在属性中注入内部bean

在实体类中表示一对多的关系:以班级和学生为例,一个班级有多个学生,而一个学生只能属于一个班级。班级是一,学生是多。学生表示所属的班级,使用对象类型的属性进行表示。

public class ClassTest {
    private String cname;

    public String getCname() {
        return cname;
    }

    public void setCname(String cname) {
        this.cname = cname;
    }
}
public class Student {
    private String name;
    private String gender;
    //学生所属的班级
    private ClassTest classTest;

    public ClassTest getClassTest() {
        return classTest;
    }

    public void setClassTest(ClassTest classTest) {
        this.classTest = classTest;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
     @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", classTest=" + classTest +
                '}';
    }
}

新建xml文件名为one2more.xml,在其中进行配置

<bean id="student" class="com.tina.spring5.bean.Student">
        <property name="name" value="sttt"></property>
        <property name="gender" value=""></property>
<!--        设置对象类型的属性(内部bean)-->
        <property name="classTest">
            <bean id="classTest2" class="com.tina.spring5.bean.ClassTest">
                <property name="cname" value="三年级二班"></property>
            </bean>
        </property>
    </bean>

测试方法:

@Test
public void testStudent(){
    ApplicationContext context = new ClassPathXmlApplicationContext("one2more.xml");
    Student student = context.getBean("student", Student.class);
    System.out.println(student.toString());
}

测试结果:
在这里插入图片描述

(6)在属性中注入级联bean

方法一

配置xml文件:

<bean id="student" class="com.tina.spring5.bean.Student">
    <property name="name" value="sttt"></property>
    <property name="gender" value=""></property>
    <!--        设置对象类型的属性(级联赋值)-->
    <property name="classTest" ref="classTest">
    </property>
</bean>
<bean id="classTest" class="com.tina.spring5.bean.ClassTest">
    <property name="cname" value="三年级一班"></property>
</bean>

测试方法同上

测试结果:
在这里插入图片描述

方法二

xml文件配置:

<bean id="student" class="com.tina.spring5.bean.Student">
    <property name="name" value="sttt"></property>
    <property name="gender" value=""></property>
    <!--        设置对象类型的属性(级联赋值)-->
    <property name="classTest" ref="classTest"></property>
    <!--        注意:在调用该对象的属性时一定要在对应类中生成该属性的get方法-->
    <property name="classTest.cname" value="三年级三班"></property>
</bean>
<bean id="classTest" class="com.tina.spring5.bean.ClassTest">
    <property name="cname" value="三年级一班"></property>
</bean>

测试方法同上

测试结果:
在这里插入图片描述

(7)在属性中注入集合

①在集合里放入字符串类型的值

创建一个类,其中包含了数组、List集合、Map集合、Set集合类型的属性,并且生成它们对应的get 、set方法。

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

    public Set<String> getSet() {
        return set;
    }

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

    public Map<String, String> getMap() {
        return map;
    }

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

    public List<String> getList() {
        return list;
    }

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

    public String[] getCourses() {
        return courses;
    }

    public void setCourses(String[] courses) {
        this.courses = courses;
    }
    
    @Override
    public String toString() {
        return "Student{" +
                "courses=" + Arrays.toString(courses) +
                ", list=" + list.toString() +
                ", map=" + map.toString() +
                ", set=" + set.toString() +
                '}';
    }
}

在配置文件中进行注入:

<bean id="student" class="com.stt.collectiontype.Student">
    <property name="courses" >
    <!--            List标签也可以完成-->
        <array>
            <value>java</value>
            <value>c语言</value>
            <value>c++</value>
            <value>python</value>
        </array>
    </property>
    <property name="list">
        <list>
            <value>数据库</value>
            <value>编译原理</value>
            <value>教我C语言</value>
            <value>Linux高级编程</value>
        </list>
    </property>
    <property name="map">
        <map>
            <entry key="你快乐吗" value="我很快乐"></entry>
            <entry key="我很快乐" value="你一点也不快乐"></entry>
        </map>
    </property>
    <property name="set">
        <set>
            <value>白藏绍序</value>
            <value>朱律谢期</value>
        </set>
    </property>
</bean>

测试方法:

@Test
public void testStudent(){
    ApplicationContext context = new ClassPathXmlApplicationContext("collectiontype.xml");
    Student student = context.getBean("student", Student.class);
    System.out.println(student.toString());
}

测试结果:
在这里插入图片描述

②在集合里放入对象类型的值

创建新的课程类

public class Course {
    private String name;

    public String getName() {
        return name;
    }

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

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

修改Student类

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

    public void setCourses(List<Course> courses) {
        this.courses = courses;
    }

    public Set<String> getSet() {
        return set;
    }

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

    public Map<String, String> getMap() {
        return map;
    }

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

    public List<String> getList() {
        return list;
    }

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

    public String[] getStrings() {
        return strings;
    }

    public void setStrings(String[] strings) {
        this.strings = strings;
    }

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

修改配置文件

<bean id="course1" class="com.stt.collectiontype.Course">
    <property name="name" value="hello1"></property>
</bean>
<bean id="course2" class="com.stt.collectiontype.Course">
    <property name="name" value="hello2"></property>
</bean>
<bean id="course3" class="com.stt.collectiontype.Course">
    <property name="name" value="hello3"></property>
</bean>
<bean id="course4" class="com.stt.collectiontype.Course">
    <property name="name" value="hello4"></property>
</bean>


<bean id="student" class="com.stt.collectiontype.Student">
    <property name="strings" >
    <!--            List标签也可以完成-->
        <array>
            <value>java</value>
            <value>c语言</value>
            <value>c++</value>
            <value>python</value>
        </array>
    </property>
    <property name="list">
        <list>
            <value>数据库</value>
            <value>编译原理</value>
            <value>教我C语言</value>
            <value>Linux高级编程</value>
        </list>
    </property>
    <property name="courses" >
        <list>
            <ref bean="course1"></ref>
            <ref bean="course2"></ref>
            <ref bean="course3"></ref>
            <ref bean="course4"></ref>
        </list>
    </property>
    <property name="map">
        <map>
            <entry key="你快乐吗" value="我很快乐"></entry>
            <entry key="我很快乐" value="你一点也不快乐"></entry>
        </map>
    </property>
    <property name="set">
        <set>
            <value>白藏绍序</value>
            <value>朱律谢期</value>
        </set>
    </property>
</bean>

测试方法同上

测试结果
在这里插入图片描述

③把集合注入的部分提取出来做成公共部分

第一步:创建新的类,其中含有List集合类型的属性

public class Book {
    private List<String> author;
    public List<String> getAuthor() {
        return author;
    }
    public void setAuthor(List<String> author) {
        this.author = author;
    }
    @Override
    public String toString() {
        return "Book{" +
                "author=" + author.toString() +
                '}';
    }
}

第二步:在Spring的配置文件中引入util名称空间,并进行相关配置

<?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">

    <!--    提取List集合类型注入-->
    <util:list id="authorList">
        <value>张三</value>
        <value>李四</value>
        <value>王五</value>
        <value>赵六</value>
    </util:list>
    <bean id="book" class="com.stt.draw.Book">
        <property name="author" ref="authorList"></property>
    </bean>
</beans>

第三步:创建测试方法

@Test
public void testBook(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    Book book = context.getBean("book", Book.class);
    System.out.println(book.toString());
}

测试结果:
在这里插入图片描述

5、IOC操作bean管理(FactoryBean)

Spring有两种类型的bean:

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

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

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

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

public class MyBean implements FactoryBean<Course> {
    @Override
    public Course getObject() throws Exception {
        Course course = new Course();
        course.setName("高级语言编译原理");
        return course;
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }
}

第三步:测试

@Test
public void testMyBean(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
    //注意:如果在这里第二个参数类型不为Course.class而是MyBean.class的话,编译器会报错
    Course myBean = context.getBean("myBean", Course.class);
    System.out.println(myBean.getClass());
}

结果:
在这里插入图片描述

6、IOC操作bean管理(bean的作用域)

(1)在Spring里,我们可以设置创建的bean是单例的还是多实例的。
(2)在默认的情况下,bean是单实例对象

示例:以上面的Book类作为示例

@Test
public void testBook(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    Book book1 = context.getBean("book", Book.class);
    Book book2 = context.getBean("book", Book.class);
    System.out.println(book1 == book2);
}

测试结果:

在这里插入图片描述

(3)如何设置单实例还是多实例

①在Spring配置文件的bean标签里有属性(scope)用于设置单实例还是多实例
在这里插入图片描述

(4)scope属性值

常见属性:

①默认值singleton,表示是单实例对象

②prototype,表示是多实例对象

不常见对象

③request,表示在创建对象时对象自动放入request域中

④session,表示在创建对象时对象自动放入session域中

<bean id="book" class="com.stt.draw.Book" scope="prototype">
    <property name="author" ref="authorList"></property>
</bean>

测试方法同上

测试结果
在这里插入图片描述

③singleton和prototype的区别

singleton表示单实例,prototype表示多实例

设置scope值是singleton时,加载Spring配置文件时就会创建单实例对象

设置scope值是prototype时,不是在Spring配置文件时创建对象,而是在调用getBean方法时创建多实例对象。

7、IOC操作Bean管理(Bean的生命周期)

(1)生命周期:从对象创建到对象销毁的过程
(2)Bean的生命周期

①通过构造器创建Bean实例

②为Bean的属性设置值和其他Bean的引用(调用set方法)

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

④Bean可以使用了(获取到了对象)

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

(3)代码演示Bean的生命周期

创建Order类,在其中加入初始化方法和销毁方法

public class Order {
    private String oname;

    public Order() {
        System.out.println("①通过构造器创建Bean实例");
    }

    public String getOname() {
        return oname;
    }

    public void setOname(String oname) {
        this.oname = oname;
        System.out.println("②为Bean的属性设置值和其他Bean的引用(调用set方法)");
    }
    public void initMethod(){
        System.out.println("③调用Bean的初始化方法(需要进行配置)");
    }

    public void destroyMethod(){
        System.out.println("⑤当容器关闭时,调用Bean的销毁方法(需要进行配置销毁方法)");
    }
}

配置配置文件,在配置文件中声明初始化方法和销毁方法

<bean id="order" class="com.stt.bean.Order" init-method="initMethod" destroy-method="destroyMethod">
    <property name="oname" value="ipad" ></property>
</bean>

编写测试方法

@Test
public void testBean(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
    Order order = context.getBean("order", Order.class);
    System.out.println("④Bean可以使用了(获取到了对象)");
    System.out.println(order);
    //销毁对象
    ((ClassPathXmlApplicationContext)context).close();
}

测试结果
在这里插入图片描述

(4)bean的后置处理器(bean的生命周期共七步)

①通过构造器创建Bean实例

②为Bean的属性设置值和其他Bean的引用(调用set方法)

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

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

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

④Bean可以使用了(获取到了对象)

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

(5)bean的后置处理器演示示例

①创建一个类,实现BeanPostProcessor接口

public class MyBeanPost implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("把bean的实例传给bean的后置处理器的方法postProcessBeforeInitialization()");
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("把bean的实例传给bean的后置处理器的方法postProcessAfterInitialization()");
        return bean;
    }
}

配置文件

<bean id="myBeanPost" class="com.stt.bean.MyBeanPost" >
</bean>

测试方法

@Test
public void testMyBeanPost(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
    MyBeanPost myBeanPost = context.getBean("myBeanPost", MyBeanPost.class);
    System.out.println("④Bean可以使用了(获取到了对象)");
    System.out.println(myBeanPost);
    //销毁对象
    ((ClassPathXmlApplicationContext)context).close();
}

测试结果

image-20210517113053623

遇到的问题:在测试时并没有写到Order类和MyBeanPost类有任何联系,但是测试MyBeanPost时依然执行了Order中的方法,原因是:

把testBean方法中只留下了第一句,依然会得到前五句的输出,是因为我定义的context是ApplicationContext类型的,加载配置文件时,就会把在配置文件中的对象进行创建,和有没有写输出没有关系

8、IOC操作Bean管理(自动装配)

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

bean标签里有一个属性叫autowire,常用两个值:

byName:根据属性名进行注入,注入值bean的ID值和类属性名一样

byType:根据属性类型进行注入

创建两个类

public class School {

    private String schoolName;

    public String getSchoolName() {
        return schoolName;
    }

    public void setSchoolName(String schoolName) {
        this.schoolName = schoolName;
    }

    @Override
    public String toString() {
        return "School{" +
                "schoolName='" + schoolName + '\'' +
                '}';
    }
}
public class Student {
    private String name;
    private School school;

    public School getSchool() {
        return school;
    }

    public void setSchool(School school) {
        this.school = school;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", school=" + school.toString() +
                '}';
    }
}

属性名称注入配置文件

<bean id="student" class="com.stt.autowire.Student" autowire="byName">
</bean>
<bean id="school" class="com.stt.autowire.School">
    <property name="schoolName" value="hello"></property>
</bean>

测试方法

@Test
public void testStudent1(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
    com.stt.autowire.Student student = context.getBean("student", com.stt.autowire.Student.class);
    System.out.println(student.toString());
}

测试结果
在这里插入图片描述

属性类型注入配置文件

<bean id="student" class="com.stt.autowire.Student" autowire="byType">
</bean>
<bean id="school" class="com.stt.autowire.School">
    <property name="schoolName" value="hello1"></property>
</bean>

测试方法同上

测试结果
在这里插入图片描述

注意:如果使用byType进行自动注入,那么相同类型的属性就不能定义多个,否则就会报错。而且,基于xml做自动装配在实际应用中用的很少,一般用注解的方式做自动装配。

9、IOC操作bean管理(引入外部属性文件)

(1)直接配置数据库信息

配置德鲁伊连接池,引入德鲁伊连接池的jar包
在这里插入图片描述

配置配置文件

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
    <property name="url" value="jdbc:mysql://localhost:3306/ttms?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone = GMT"></property>
    <property name="username" value="root"></property>
    <property name="password" value="password"></property>
</bean>
(2)引入外部属性文件配置数据库连接池

创建外部属性文件.properties格式文件,写数据库信息

在这里插入图片描述

引入context名称空间,把外部.properties格式文件引入到Spring配置文件中,在property标签中的value值通过表达式${}从配置文件中读取,在花括号内部写的是配置文件等号左边即key的值

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       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/context http://www.springframework.org/schema/context/spring-context.xsd">
             
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${driverClassName}"></property>
        <property name="url" value="${url}"></property>
        <property name="username" value="${username}"></property>
        <property name="password" value="${password}"></property>
    </bean>
</beans>

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

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

注解可以作用在类、属性、方法上面

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

(2)Spring针对Bean管理中创建对象提供注解
@Service
@Component
@Controller
@Repository

上面四个注解在实际应用的时候没有区别

(3)代码演示

第一步:添加Aop包
在这里插入图片描述

第二步:在配置文件中引入context名称空间,并设置要扫描的包

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       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/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!--如果要扫描多个包,可以在多个包中间用,隔开-->
    <!--    <context:component-scan base-package="com.hello,com.stt,com.tina">-->
    <!--    </context:component-scan>-->
    <!--        也可以扫描到这些包的共同上层目录-->
    <context:component-scan base-package="com">
    </context:component-scan>
</beans>

第三步:创建类,并在类上加上注解

//在注解里value的值可以省略,默认值是类名称首字母小写
@Service(value = "userService")
public class UserService {

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

第四步:创建测试类和测试方法

public class UserServiceTest {
    @Test
    public void add() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
    }
}

测试结果
在这里插入图片描述

(4)开启组件扫描的细节配置
<!--    示例1
    use-default-filters="false"表示现在不使用默认的filter,自己配置filter
    <context:include-filter />标签表示自己设置扫描的内容,比如:
    type="annotation"表示扫描注解
    expression="org.springframework.stereotype.Controller"
    表示只扫描带有Controller的注解
-->
<context:component-scan base-package="com" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>


<!--    示例2
    这个示例中context标签不带use-default-filters="false",表示扫描所有
    <context:exclude-filter />标签表示自己设置不扫描的内容,比如:
    type="annotation"表示扫描注解
    expression="org.springframework.stereotype.Controller"
    表示不扫描带有Controller的注解
-->
<context:component-scan base-package="com" >
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
(5)基于注解方式实现属性注入

创建两个类,UserService和UserDao,UserDao实现UserService接口中的add方法

@Service(value = "userService")
public class UserService {
    private UserDao userDao;
    public void add(){
        System.out.println("user add.......");
        userDao.add();
    }
}
@Controller
public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        System.out.println("user add........");
    }
}
public interface UserDao {
    public void add();
}

①实现注解注入属性的三个常用注解

@AutoWaired:根据属性类型进行自动装配

第一步:创建两个类的对象,在UserService和UserDao上面都添加创建对象的注解

第二步:在UserService类中创建UserDao类型的属性,添加注入属性注解,不需要加入set方法

第三步:创建测试方法

@Service(value = "userService")
public class UserService {
    @Autowired
    private UserDao userDao;
    public void add(){
        System.out.println("service add.......");
        userDao.add();
    }
}
@Controller
public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        System.out.println("user add........");
    }
}
@Test
public void add() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml");
    UserService userService = context.getBean("userService", UserService.class);
    userService.add();
}

在这里插入图片描述

@Qualifier:根据属性名称进行注入,需要和上面的@AutoWaired一起使用

如果一个属性的类型是接口,这个接口有多个实现类,那么就要使用@Qualifier注解,以声明到底使用该接口的哪个实现类。

@Controller(value = "userDaoImpl1")
public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        System.out.println("user add........");
    }
}
@Service(value = "userService")
public class UserService {
    @Autowired
    @Qualifier(value = "userDaoImpl1")
    private UserDao userDao;
    public void add(){
        System.out.println("service add.......");
        userDao.add();
    }
}

测试方法和结果同上

@Resource:可以根据属性类型注入,也可以根据属性名称进行注入

@Service(value = "userService")
public class UserService {
    @Resource(name = "userDaoImpl1")
//    @Resource()
    private UserDao userDao;
    public void add(){
        System.out.println("service add.......");
        userDao.add();
    }
}

测试方法和结果同上

注意: @Resource()注解引入的包是import javax.annotation.Resource; 说明它是Java自带的注解,所以Spring官方更推荐使用上面两种。

@Value:注入普通类型属性

@Service(value = "userService")
public class UserService {
    @Resource(name = "userDaoImpl1")
    private UserDao userDao;
    @Value(value = "123456")
    private String name;
    public void add(){
        System.out.println("service add......."+name);
        userDao.add();
    }
}

测试方法同上

测试结果
在这里插入图片描述

11、IOC操作bean管理(完全注解开发)
(1)创建配置类,代替配置文件
@Configuration
@ComponentScan(basePackages = {"com.hello"})
public class SpringConfig {
}
(2)编写测试类
@Test
public void add1() {
    ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
    UserService userService = context.getBean("userService", UserService.class);
    userService.add();
}

三、Aop

1、AOP:

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

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

2、AOP底层原理

AOP底层使用动态代理的方式,有两种情况的动态代理

(1)有接口情况,使用JDK动态代理

创建接口实现类的代理对象,增强类的方法

(2)无接口情况,使用CGLIB动态代理

创建当前类的子类的代理对象,增强类的方法

3、AOP示例(动态代理实现)

使用Proxy类里面的方法创建代理对象

(1)调用NewProxyInstance方法

方法有三个参数:

第一个参数:类加载器

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

第三个参数:实现接口InvocationHandler,创建代理对象,写增强方法

(2)编写JDK动态代理的方法

①创建接口,定义方法

public interface UserDao {
    public int add( int a, int b);
    public String register(String name);
}

②创建接口实现类,实现方法

public class UserDaoImpl implements UserDao {
    @Override
    public int add(int a,int b) {
        System.out.println("add方法执行了......");
        return a+b ;
    }
    @Override
    public String register(String name) {
        return "hello" + name;
    }
}

③使用Proxy类创建接口代理对象

public class JDKProxy {
    public static void main(String[] args) {
        Class[] interfaces = {UserDao.class};
        //创建接口实现类代理对象
//        Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
//            @Override
//            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//                return null;
//            }
//        })
        UserDao userDao = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(new UserDaoImpl()));
        System.out.println(userDao.add(45, 22));
    }
}


//创建代理类对象代码
class UserDaoProxy implements InvocationHandler {
    private Object obj;

    //把创建的谁的代理对象,把那个谁传过来
    public UserDaoProxy(Object obj){
        this.obj = obj;
    }


    //增强的逻辑
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //方法之前做处理
        System.out.println("在方法之前执行......" + method.getName()+args.toString());
        Object res = method.invoke(obj, args);
        //方法之后做处理
        System.out.println("在方法之后执行......");
        return res;
    }
}

输出结果:

image-20210518175136500

4、AOP操作相关的术语

(1)连接点:类中可以被增强的方法
(2)切入点:实际被真正增强的方法
(3)通知(增强):实际增强的逻辑部分称为通知

通知的类型:

前置通知:在方法前执行

后置通知:在方法后执行

环绕通知:在方法前后都执行

异常通知‘:在方法发生异常时执行

最终通知:无论方法有没有发生异常都执行

(4)切面:是动作,把通知应用到切入点的过程

5、AOP操作准备

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

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

在Spring中基于AspectJ实现AOP操作有两种方式

基于xml文件实现

基于注解实现

(2)在项目工程中引入AspectJ相关依赖
(3)切入点表达式
①切入点表达式的作用:知道对哪个类里面哪个方法进行增强
②语法结构:

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

eg1:对com.tina.dao.BookDao类里面的add方法进行增强

execution(* com.tina.dao.BookDao.add(…))

eg2:对com.tina.dao.BookDao类里面的所有方法进行增强

execution(* com.tina.dao.BookDao.*(…))

eg3:对com.tina.dao.BookDao类里面的所有类、类里的所有方法进行增强

execution(* com.tina.dao. * . *(…))

6、AOP操作(基于AspectJ注解方式)

(1)创建类,在类里面定义方法
public class User {
    public void add(){
        System.out.println("add.........");
    }
}
(2)创建增强类(编写增强逻辑)

在增强类里创建方法,让不同的方法代表不同的通知类型

public class UserProxy {
    public void before(){
        System.out.println("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"
       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.tina.aopanno"></context:component-scan>
</beans>
②使用注解创建User和UserProxy对象
@Component
public class User {
    public void add(){
        System.out.println("add.........");
    }
}
@Component
public class UserProxy {
    //前置通知
    public void before(){
        System.out.println("before.........");
    }
}
③在增强类上面添加注解@Aspect
@Component
@Aspect
public class UserProxy {
    //前置通知
    public void before(){
        System.out.println("before.........");
    }
}
④在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
       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.tina.aopanno"></context:component-scan>
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
(4)配置不同类型的通知
①在增强类里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置
@Component
@Aspect
public class UserProxy {
    //前置通知
    @Before(value = "execution(* com.tina.aopanno.User.add(..))")
    public void before(){
        System.out.println("before.........");
    }
}

测试方法

@Test
public void testAop(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    User user = context.getBean("user", User.class);
    user.add();
}

结果
在这里插入图片描述

②在增强类里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置,测试不同的注解
@Component
@Aspect
public class UserProxy {
    //前置通知
    @Before(value = "execution(* com.tina.aopanno.User.add(..))")
    public void before(){
        System.out.println("before.........");
    }


    //最终通知
    @After(value = "execution(* com.tina.aopanno.User.add(..))")
    public void after(){
        System.out.println("after.........");
    }

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

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

    @Around(value = "execution(* com.tina.aopanno.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        System.out.println("之前......");
        proceedingJoinPoint.proceed() ;
        System.out.println("之后......");

    }
}

测试方法同上

测试结果:
在这里插入图片描述

(5)相同切入点抽取
@Component
@Aspect
public class UserProxy {

    @Pointcut(value = "execution(* com.tina.aopanno.User.add(..))")
    public void pointDemo(){}

    //前置通知
    @Before(value = "pointDemo()")
    public void before(){
        System.out.println("before.........");
    }


    //最终通知
    @After(value = "pointDemo()")
    public void after(){
        System.out.println("after.........");
    }

    //后置通知(返回通知)
    @AfterReturning(value = "pointDemo()")
    public void afterReturning(){
        System.out.println("afterReturning.........");
    }

    //异常通知
    @AfterThrowing(value = "pointDemo()")
    public void afterThrowing(){
        System.out.println("afterThrowing.........");
    }

    @Around(value = "pointDemo()")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        System.out.println("之前......");
        proceedingJoinPoint.proceed() ;
        System.out.println("之后......");

    }
}
(6)有多个增强类对同一方法进行增强,设置增强类优先级

在增强类上面添加注解@Order(数字类型值),数字类型值越小,优先级越高

@Component
@Aspect
@Order(4)
public class PersonProxy {
    @Before(value = "execution(* com.tina.aopanno.User.add(..))")
    public void before(){
        System.out.println("before1.........");
    }
}
@Order(1)
@Component
@Aspect
public class UserProxy {

    @Pointcut(value = "execution(* com.tina.aopanno.User.add(..))")
    public void pointDemo(){}
    //前置通知
    @Before(value = "pointDemo()")
    public void before(){
        System.out.println("before.........");
    }
    //最终通知
    @After(value = "pointDemo()")
    public void after(){
        System.out.println("after.........");
    }
    //后置通知(返回通知)
    @AfterReturning(value = "pointDemo()")
    public void afterReturning(){
        System.out.println("afterReturning.........");
    }
    //异常通知
    @AfterThrowing(value = "pointDemo()")
    public void afterThrowing(){
        System.out.println("afterThrowing.........");
    }
    @Around(value = "pointDemo()")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        System.out.println("之前......");
        proceedingJoinPoint.proceed() ;
        System.out.println("之后......");

    }
}

测试结果
在这里插入图片描述

7、AOP操作(基于AspectJ配置文件方式)

(1)创建两个类,增强类和被增强类,创建方法
public class Book {
    public void buy(){
        System.out.println("buy.........");
    }
}
public class BookProxy {
    public void before(){
        System.out.println("before.........");
    }
    public void after(){
        System.out.println("after.........");
    }
    public void afterReturning(){
        System.out.println("afterReturning.........");
    }
    public void afterThrowing(){
        System.out.println("afterThrowing.........");
    }
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        System.out.println("之前......");
        proceedingJoinPoint.proceed() ;
        System.out.println("之后......");

    }
}
(2)在Spring配置文件中创建两个类对象
<bean id="book" class="com.tina.aopxml.Book">
</bean>
<bean id="bookProxy" class="com.tina.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
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="book" class="com.tina.aopxml.Book">
    </bean>
    <bean id="bookProxy" class="com.tina.aopxml.BookProxy">
    </bean>
    <aop:config>
        <aop:pointcut id="p" expression="execution(* com.tina.aopxml.Book.buy(..))"/>
        <aop:aspect ref="bookProxy">
            <aop:before method="before" pointcut-ref="p"></aop:before>
            <aop:after method="after" pointcut-ref="p"></aop:after>
            <aop:after-returning method="afterReturning" pointcut-ref="p"></aop:after-returning>
            <aop:after-throwing method="afterThrowing" pointcut-ref="p"></aop:after-throwing>
            <aop:around method="around" pointcut-ref="p"></aop:around>
        </aop:aspect>
    </aop:config>
</beans>
(4)创建测试方法
@Test
public void testAopXml(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
    Book book = context.getBean("book", Book.class);
    book.buy();
}
(5)测试结果

在这里插入图片描述

8、完全注解开发

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

四、JdbcTemplate

1、JdbcTemplate:

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

2、使用JdbcTemplate的准备工作

(1)引入相关依赖的包
<!-- https://mvnrepository.com/artifact/springframework/spring-orm -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-orm</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>${mysql.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>${druid.version}</version>
</dependency>
(2)在spring的配置文件中配置数据库连接池
username=root
password=password
url=jdbc:mysql://localhost:3306/user?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone = GMT
driverClassName=com.mysql.cj.jdbc.Driver
initialSize=5
maxActive=10
filters=stat
maxWait=60000
timeBetweenEvictionRunsMillis=60000
minEvictableIdleTimeMillis=300000
validationQuery=SELECT 1
testWhileIdle=true
testOnBorrow=false
testOnReturn=false
poolPreparedStatements=false
maxPoolPreparedStatementPerConnectionSize=200
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${driverClassName}"></property>
    <property name="url" value="${url}"></property>
    <property name="username" value="${username}"></property>
    <property name="password" value="${password}"></property>
</bean>
(3)配置JdbcTemplate对象,注入DataSourse
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
	<!--        注入dataSourse-->
    <property name="dataSource" ref="dataSource"></property>
</bean>
(4)创建Service类,创建ServiceDao类,在ServiceDao注入JdbcTemplate对象,开启组件扫描
<context:component-scan base-package="com.tina.jdbctemplate"></context:component-scan>
@Service
public class BookService {
    @Autowired
    private BookDao bookDao;
}
public interface BookDao {
}
@Repository
public class BookDaoImpl implements BookDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
}

3、JdbcTemplate操作数据库(添加删除修改功能)

(1)创建数据库对应的实体类
public class User {
    private Integer id;
    private String name;
    private String country;

    public User() {
    }

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

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

}
(2)编写service和dao
①dao进行数据库添加删除修改操作
public interface UserDao {
    public void add(User user);
    public void delete(int id);
    public void update(User user);
}
use `user`;
SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` char(20) NOT NULL DEFAULT '' COMMENT '站点名称',
  `country` char(10) NOT NULL DEFAULT '' COMMENT '国家',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

BEGIN;
INSERT INTO `user` VALUES 
('1', 'Google',  'USA'), 
('2', '淘宝', 'CN'), 
('3', '菜鸟教程',  'CN'), 
('4', '微博', 'CN'), 
('5', 'Facebook', 'USA');
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
②调用jdbcTemplate对象里面的update方法实现添加操作

在这里插入图片描述

有两个参数

第一个参数,sql语句

第二个参数,可变参数,设置sql语句值

@Repository
public class UserDaoImpl implements UserDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void add(User user) {
        String sql = "insert into user values(?,?,?)";
        int update = jdbcTemplate.update(sql, user.getId(), user.getName(), user.getCountry());
        System.out.println(update);
    }

    public void delete(int id) {
        String sql = "delete from user where id = ?";
        int update = jdbcTemplate.update(sql, id);
        System.out.println(update);
    }

    public void update(User user) {
        String sql = "update user set name=?,country=? where id = ?";
        int update = jdbcTemplate.update(sql,  user.getName(), user.getCountry(),user.getId());
        System.out.println(update);
    }

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

    public void addUser(User user){
        userDao.add(user);
    }
    public void updateUser(User user){
        userDao.update(user);
    }
    public void deleteUser(int id){
        userDao.delete(id);
    }
}
③编写测试类和测试方法
public class UserServiceTest {

    @Test
    public void addUser() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.addUser(new User(null,"helli","CN"));
    }

    @Test
    public void updateUser() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.updateUser(new User(6,"hello","CN"));
    }

    @Test
    public void deleteUser() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.deleteUser(4);
    }
}

测试结果略

4、JdbcTemplate操作数据库(查询功能)

(1)查询返回某个值在这里插入图片描述

第一个参数:SQL语句

第二个参数:返回类型的class

public int findCount(){
    return userDao.selectCount();
}
public int selectCount() {
    String sql = "select count(*) from user";
    return jdbcTemplate.queryForObject(sql, Integer.class);
}
@Test
public void findCount() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
    UserService userService = context.getBean("userService", UserService.class);
    System.out.println(userService.findCount());
}

(2)查询返回某个对象

在这里插入图片描述

第一个参数:SQL语言

第二个参数:RowMapper接口,可以实现将返回的数据封装成对象

第三个参数:SQL语句的参数

public User findUser(int id){
    return userDao.findUserInfo(id);
}
public User findUserInfo(int id) {
    String sql = "select * from user where id = ?";
    return jdbcTemplate.queryForObject(sql,new BeanPropertyRowMapper<User>(User.class),id);
}
@Test
public void findUser() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
    UserService userService = context.getBean("userService", UserService.class);
    User user = userService.findUser(2);
    System.out.println(user);
}

(3)查询返回某个集合在这里插入图片描述

第一个参数:SQL语句

第二个参数:RowMapper是接口,针对不同的返回值类型数据,使用这个接口里面的实现类完成数据封装

第三个参数:SQL语句值

public List<User> findAll(){
    return userDao.findAllUser();
}
public List<User> findAllUser() {
    String sql = "select * from user";
    return jdbcTemplate.query(sql,new BeanPropertyRowMapper<User>(User.class));
}
@Test
public void findAll() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
    UserService userService = context.getBean("userService", UserService.class);
    List<User> users = userService.findAll();
    System.out.println(users);
}

5、JdbcTemplate操作数据库(批量操作)在这里插入图片描述

第一个参数:SQL语句

第二个参数:List集合,要添加的多条记录数据

批量添加:
public void batchAdd(List<Object[]> objects){
    userDao.batchAllUser(objects);
}
public void batchAllUser(List<Object[]> objects) {
    String sql = "insert into user values(?,?,?)";
    int[] ints = jdbcTemplate.batchUpdate(sql, objects);
    System.out.println(Arrays.toString(ints));
}
@Test
public void batchAdd() {
    ArrayList<Object[]> objects = new ArrayList<Object[]>();
    Object[] object1={6,"java1","CN"};
    Object[] object2={7,"java2","CN"};
    Object[] object3={8,"java3","CN"};
    objects.add(object1);
    objects.add(object2);
    objects.add(object3);
    ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
    UserService userService = context.getBean("userService", UserService.class);
    userService.batchAdd(objects);
}
批量修改和删除
public void batchUpdate(List<Object[]> objects){
    userDao.batchUpdateUser(objects);
}
public void batchDelete(List<Object[]> objects){
    userDao.batchdeleteUser(objects);
}
public void batchUpdateUser(List<Object[]> objects) {
    String sql = "update user set name=?,country=? where id = ?";
    int[] ints = jdbcTemplate.batchUpdate(sql, objects);
    System.out.println(Arrays.toString(ints));
}

public void batchdeleteUser(List<Object[]> objects) {
    String sql = "delete from user where id = ?";
    int[] ints = jdbcTemplate.batchUpdate(sql, objects);
    System.out.println(ints.toString());
}
@Test
public void batchUpdate() {
    ArrayList<Object[]> objects = new ArrayList<Object[]>();
    Object[] object1={"java1","CN",5};
    Object[] object2={"java2","CN",2};
    Object[] object3={"java3","CN",1};
    objects.add(object1);
    objects.add(object2);
    objects.add(object3);
    ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
    UserService userService = context.getBean("userService", UserService.class);
    userService.batchUpdate(objects);

}

@Test
public void batchDelete() {
    ArrayList<Object[]> objects = new ArrayList<Object[]>();
    Object[] object1={5};
    Object[] object2={2};
    Object[] object3={1};
    objects.add(object1);
    objects.add(object2);
    objects.add(object3);
    ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
    UserService userService = context.getBean("userService", UserService.class);
    userService.batchDelete(objects);
}

五、事务管理

1、事务:数据库操作的基本单元,逻辑上的一组操作,要么都成功,有一个失败则所有操作都失败

典型场景:银行转账

甲给乙转账100元,甲少100元,乙多100元

2、事务的特性(ACID)

(1)原子性
(2)一致性
(3)隔离型
(4)持久性

3、事务操作(模拟环境)

(1)创建数据库、创建表,在表里添加字段
DROP database IF EXISTS `bank`;
create database `bank`;
use `bank`;
SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` char(20) NOT NULL DEFAULT '' COMMENT '姓名',
  `money` decimal NOT NULL DEFAULT 0.0 COMMENT '余额',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

BEGIN;
INSERT INTO `account` VALUES 
('1', '周嘉川',  100000), 
('2', '季樱', 3000), 
('3', '朱律',20310), 
('4', '纪和琅', 10235), 
('5', '初凝', 52410);
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;

(2)创建service,搭建dao,在dao中注入jdbcTemplate模板,在模板中注入DataSource
@Service
public class AccountService {
    @Autowired
    private AccountDao accountDao;
    
}
public interface AccountDao {
}
@Deprecated
public class AccountDaoImpl implements AccountDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
}
(3)在dao里面创建两个方法,一个多钱,一个少钱,在service里创建转账的方法
public interface AccountDao {
    void addMoney(Account account, BigDecimal bigDecimal);
    void reduceMoney(Account account,BigDecimal bigDecimal);
}
@Repository
public class AccountDaoImpl implements AccountDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public Account findAccountInfo(int id) {
        String sql = "select * from account where id = ?";
        return jdbcTemplate.queryForObject(sql,new BeanPropertyRowMapper<Account>(Account.class),id);
    }
    public void update(Account account) {
        String sql = "update account set money=? where id = ?";
        int update = jdbcTemplate.update(sql,  account.getMoney());
        System.out.println(update);
    }
    public void addMoney(Account account, BigDecimal bigDecimal) {
        Account accountInfo = findAccountInfo(account.getId());
        String sql = "update account set money=? where id = ?";
        int update = jdbcTemplate.update(sql, accountInfo.getMoney().add(bigDecimal),accountInfo.getId());
        System.out.println(update);

    }

    public void reduceMoney(Account account, BigDecimal bigDecimal) {
        Account accountInfo = findAccountInfo(account.getId());
        String sql = "update account set money=? where id = ?";
        int update = jdbcTemplate.update(sql, accountInfo.getMoney().subtract(bigDecimal),accountInfo.getId());
        System.out.println(update);

    }
}
@Service
public class AccountService {
    @Autowired
    private AccountDao accountDao;

    public void account(int id1,int id2, BigDecimal money){
        accountDao.reduceMoney(new Account(id1,null,null),money);
        accountDao.addMoney(new Account(id2,null,null), money);

    }

}
(4)测试类及测试方法
public class AccountServiceTest {

    @Test
    public void account() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
        AccountService accountService = context.getBean("accountService", AccountService.class);
        accountService.account(1,2,new BigDecimal(10000));
    }
}

在这里插入图片描述

在这里插入图片描述

以上代码在正常执行时是不会有问题的,如果在执行过程中遇到断电断网等问题,那么就会出现异常状况,例如:

public void account(int id1,int id2, BigDecimal money){
    accountDao.reduceMoney(new Account(id1,null,null),money);
    int i =  1/0;
    accountDao.addMoney(new Account(id2,null,null), money);

}

在这里插入图片描述
在这里插入图片描述
再次测试发现转账人的钱少了,但是被转账人的钱并没有多

4、使用事务解决

(1)Spring事务管理介绍

①事务一般添加到spring三层结构中的Service层中
②在Spring中进行事务管理操作有两种方式:
编程式事务管理
声明式事务管理(常用)

基于注解方式

基于xml配置文件方式

③在Spring中进行声明式事务管理,底层使用Aop原理
④Spring事务管理API

Spring提供了一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类
在这里插入图片描述

Ctrl+H调出该接口的实现类

在这里插入图片描述

(2)声明式事务管理(基于注解)

①在Spring配置文件配置事务管理器
<!--创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--注入数据源-->
    <property name="dataSource" ref="dataSource"></property>
</bean>
②在Spring配置文件中开启事务注解
在Spring配置文件中引入tx名称空间
<?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
       http://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
       http://www.springframework.org/schema/aop/spring-aop.xsd">
开启事务注解
<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
在service类上面或servicelei类里面的方法上面添加事务注解
@Transactional
这个注解可以添加到类上面,也可以添加到方法上面
如果该注解添加到类上面,则daibiao代表这个类里面所有的方法都添加事务
如果该注解添加到方法上面,就是为这个方法添加事务
声明式事务属性的一些参数配置在这里插入图片描述
propagation :事务传播行为

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

事务方法:对数据库表中数据发生变化操作

传播行为:多事务方法之间调用过程中事务会如何处理

Spring框架定义了7种类传播行为

事务传播行为类型说明
PROPAGATION_REQUIRED如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

用法:

@Service
@Transactional(propagation = Propagation.REQUIRED)
public class AccountService {
    @Autowired
    private AccountDao accountDao;

    public void account(int id1,int id2, BigDecimal money){
            accountDao.reduceMoney(new Account(id1,null,null),money);
            int i =  1/0;
            accountDao.addMoney(new Account(id2,null,null), money);
    }
}

solation:事务隔离级别

事务有一个特性叫做隔离性,多事务之间不会产生影响,不考虑隔离性会产生很多问题

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

**脏读:**一个未提交事务读取到另一个未提交事务的数据

**不可重复读:**一个未提交事务读取到了另一提交事务修改的数据

**虚读(幻读):**一个未提交事务读取到了另一已提交事务添加的数据

通过设置事务的隔离级别可以解决三个读问题

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

用法:

@Transactional(propagation = Propagation.REQUIRED ,isolation = Isolation.REPEATABLE_READ)
timout:超时时间

事务需要在一定的时间内进行提交,如果超时不提交则进行回滚

Spring默认值是-1,即不超时,设置时间以秒为单位

readOnly:是否只读

读:查询操作;写:添加修改删除操作

readOnly默认值是false,表示可以查询,也可以删除修改

设置readOnly值为true,只能查询,不能删除或修改

roolbackFor:回滚

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

noRollbackFor:不回滚

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

③进行业务操作
④出现异常,让事务回滚

(2)声明式事务管理(基于xml文件)

①在Spring配置文件中进行配置
第一步:配置事务管理器
第二步:配置通知
第三步:配置切入点和切面
<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
<!--配置通知-->
<tx:advice id="txadvice">
    <tx:attributes>
        <!--指定在哪种规则的方法上面添加事务-->
        <tx:method name="account*" propagation="REQUIRED" isolation="DEFAULT"/>
    </tx:attributes>
</tx:advice>
<!--    配置切入点和切面-->
<aop:config>
    <aop:pointcut id="pt" expression="execution(* com.tina.txdemo.service.AccountService.*(..))"/>
    <aop:advisor advice-ref="txadvice" pointcut-ref="pt"></aop:advisor>
</aop:config>

5、声明式事务管理(完全注解开发)

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

@Configuration  //配置类
@ComponentScan(basePackages = "com.tina.txdemo") //开启组件扫描
@EnableTransactionManagement //开启事务
public class TxConfig {
    @Bean
    public DruidDataSource getDruidDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        druidDataSource.setUrl("jdbc:mysql://localhost:3306/user?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone = GMT");
        druidDataSource.setUsername("Tina");
        druidDataSource.setPassword("password");
        return druidDataSource;
    }
    //创建JdbcTemplate对象
    @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;

    }
}

测试方法

@Test
public void accountAnno() {
    ApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);
    AccountService accountService = context.getBean("accountService", AccountService.class);
    accountService.account(1,2,new BigDecimal(10000));
}

测试结果略

六、spring5新特性

1、Spring5框架基于Java8实现,运行时兼容JDK9,许多不建议使用的类和方法在代码库中删除

2、Spring5框架自带了通用的日志封装

(1)Spring5已经移除了Log4jConfigListener,官方建议使用Log4j2
(2)Spring5框架整合Log4j2
①引入jar包
<log4j.version>2.11.2</log4j.version>
<slf4j.version>1.7.25</slf4j.version>
<!-- 日志文件管理包 -->
        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>${log4j.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>${log4j.version}</version>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>${log4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${slf4j.version}</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="INFO">
<!--    先定义所有的Appenders-->
    <Appenders>
<!--        输出日志信息到控制台-->
        <Console name="Console" target="SYSTEM_OUT">
<!--            控制日志输出的格式-->
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
    </Appenders>
<!--    然后定义logger,只有定义了logger并引入appender,appender才会生效-->
<!--    root用于指定项目的根日志,如果没有单独指定logger,则会使用root作为默认的日志输出-->
    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>
③写日志类,并进行测试
public class UserLog {
    private static final Logger log = LoggerFactory.getLogger(UserLog.class);
    public static void main(String[] args) {
        log.info("hello log4j2 info\n");
        log.warn("hello log4j2 warn\n");
    }
}

不知道为什么,执行main方法没有结果输出,在网上查阅资料后,发现必须加上如下的名为log4j.properties的文件才可以正常输出,不知道什么原因,记录一下。

# priority  :debug<info<warn<error
#you cannot specify every priority with different file for log4j
log4j.rootLogger=debug,stdout,info,debug,warn,error 

#console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender 
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 
log4j.appender.stdout.layout.ConversionPattern= [%d{yyyy-MM-dd HH:mm:ss a}]:%p %l%m%n
#info log
log4j.logger.info=info
log4j.appender.info=org.apache.log4j.DailyRollingFileAppender 
log4j.appender.info.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.info.File=./src/main/java/cn/xx/logs/info.log
log4j.appender.info.Append=true
log4j.appender.info.Threshold=INFO
log4j.appender.info.layout=org.apache.log4j.PatternLayout 
log4j.appender.info.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
#debug log
log4j.logger.debug=debug
log4j.appender.debug=org.apache.log4j.DailyRollingFileAppender 
log4j.appender.debug.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.debug.File=./src/main/java/cn/xx/logs/debug.log
log4j.appender.debug.Append=true
log4j.appender.debug.Threshold=DEBUG
log4j.appender.debug.layout=org.apache.log4j.PatternLayout 
log4j.appender.debug.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
#warn log
log4j.logger.warn=warn
log4j.appender.warn=org.apache.log4j.DailyRollingFileAppender 
log4j.appender.warn.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.warn.File=./src/main/java/cn/xx/logs/warn.log
log4j.appender.warn.Append=true
log4j.appender.warn.Threshold=WARN
log4j.appender.warn.layout=org.apache.log4j.PatternLayout 
log4j.appender.warn.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
#error
log4j.logger.error=error
log4j.appender.error = org.apache.log4j.DailyRollingFileAppender
log4j.appender.error.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.error.File = ./src/main/java/cn/xx/logs/error.log 
log4j.appender.error.Append = true
log4j.appender.error.Threshold = ERROR 
log4j.appender.error.layout = org.apache.log4j.PatternLayout
log4j.appender.error.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n

测试结果在这里插入图片描述

3、Spring5核心容器支持@Nullable注解

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

举例:ApplicationContext类中表示方法的返回值可以为空
在这里插入图片描述

AnnotationConfigApplicationContext类的registerBean方法中表示该方法的参数可以为空
在这里插入图片描述

4、Spring5核心容器里支持函数式风格GenericApplicationContext

@Test
    public void testGenericApplicationContext(){
        //创建GenericApplicationContext对象
        GenericApplicationContext context = new GenericApplicationContext();
        //调用context的方法对对象进行注册
        context.refresh();
//        context.registerBean(User.class,() ->new User());
        context.registerBean("user",User.class,() ->new User());
        //获取spring注册的对象
//        User bean = (User) context.getBean("com.tina.jdbctemplate.pojo.User");
        User bean = (User) context.getBean("user");
        System.out.println(bean);
    }

5、Spring5支持整合JUnit5

(1)整合JUint4
①引入Spring相关针对测试依赖
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>${spring.version}</version>
</dependency>
②创建测试类,使用注解方式完成
package com.tina.test;

import com.tina.txdemo.service.AccountService;
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 java.math.BigDecimal;

/**
 * @author SongTiantian
 * @create 2021-05-14-14:51
 */

@RunWith(SpringJUnit4ClassRunner.class)  //加载单元测试框架
@ContextConfiguration("classpath:bean4.xml")  //加载配置文件
public class JTest4 {
    @Autowired
    private AccountService accountService;

    @Test
    public void test1(){
        accountService.account(3,4,new BigDecimal("10000"));
    }

}

(2)Spring5整合JUnit5
①引入JUnit5的jar包
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>RELEASE</version>
    <scope>compile</scope>
</dependency>
②创建测试类,使用Junit5的注解完成
package com.tina.test;

import com.tina.txdemo.service.AccountService;
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 java.math.BigDecimal;

/**
 * @author SongTiantian
 * @create 2021-05-14-14:51
 */

@ExtendWith(SpringExtension.class)  //加载单元测试框架
@ContextConfiguration("classpath:bean4.xml")  //加载配置文件
public class JTest5 {
    @Autowired
    private AccountService accountService;
    @Test
    public void test1(){
        accountService.account(3,4,new BigDecimal("10000"));
    }

}
③使用一个复合注解完成
package com.tina.test;

import com.tina.txdemo.service.AccountService;
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 org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import java.math.BigDecimal;

/**
 * @author SongTiantian
 * @create 2021-05-14-14:51
 */

@SpringJUnitConfig(locations = "classpath:bean4.xml")
public class JTest5 {
    @Autowired
    private AccountService accountService;
    @Test
    public void test1(){
        accountService.account(3,4,new BigDecimal("10000"));
    }

}

6、SpringWebFlux

(1)SpringWebFlux介绍
①是Spring5添加的新模块,用于web开发的,功能和SpringMVC类似,WebFlux使用当前一种比较流行的响应式编程出现的框架
②使用传统的web框架,比如SpringMVC,基于Servlet容器,WebFlux是一种异步非阻塞的框架,异步非阻塞的框架在Servlet3.1以后才出现的,核心是基于Reactor的相关API实现的。
③异步非阻塞:

异步和同步:

阻塞和非阻塞:

上面都是针对的对象不一样

异步和同步针对调用者,调用者发送请求,如果等着对方回应之后再去干其他事情就叫同步,如果发送请求之后不等着对方回应就去干其他事情就叫异步。

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

④WebFlux特点

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

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

⑤与SpringMVC进行比较

在这里插入图片描述

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

SpringMVC采用命令行式编程,WebFlux采用异步响应式编程

(2)响应式编程
①响应式编程:

响应式编程是一种面向数据流和变化传播的编程范式。这意味着在编程语言中很方便的表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。

②Java8及之前的版本

提供观察者模式的两个类:Observer和Observable

创建springBoot项目,直接使用阿里云的节点http://start.aliyun.com,我的电脑也访问不到官网,在CSDN上看了@qq_26071319 的回答之后创建成功,但是还没开始用,IDEA就卡崩了。

public class ObserverDemo extends Observable {

    public static void main(String[] args) {
        ObserverDemo observerDemo = new ObserverDemo();

        //添加观察者
        observerDemo.addObserver((o,arg)->{
            System.out.println("发生了变化");
        });
        observerDemo.addObserver((o,arg)->{
            System.out.println("收到被观察者通知,准备改变");
        });

        observerDemo.setChanged(); //数据变化
        observerDemo.notifyObservers();  //通知
    }

}
(3)Reactor实现响应式编程
①在响应式编程的操作中,Reactor是是满足Reactive规范的框架
②Reactor有两个核心类,Mono和Flux,这两个类实现接口Publisher,提供了丰富的操作符,Flux对象实现发布者,返回N个元素;Mono实现发布者,返回0或1个元素
③Mono和Flux都是数据流的发布者,使用Mono和Flux都可以发出三种数据信号,元素值,错误信号、完成信号,错误信号和完成信号都是终止信号,终止信号用于告诉订阅者数据流结束了;错误信号在终止数据流的同时把错误信息传递给订阅者。
④代码演示Mono和Flux

第一步:引入reactor依赖

<dependency>
    <groupId>io.projectreactor</groupId>
    <artifactId>reactor-core</artifactId>
    <version>3.1.5.RELEASE</version>
</dependency>

按照教程一直引用不了包,算了,后面的不看了。

(3)SpringWebFlux执行流程和核心API
(4)SpringWebFlux基于注解编程模型
(5)SpringWebFlux基于函数式编程模型
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值