Spring 5

目录

一、Spring 概述

二、Spring 插件下载

三、入门案例

四、IOC 容器

1、IOC 底层原理

2、IOC 接口

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

(1)基于 xml 方式创建对象

(2)基于 xml 方式 注入属性

(3)基于xml 方式 注入其他属性的值

  (4) 基于xml 方式 外部 Bean 注入

(5)基于xml 方式 内部Bean和级联使用

(6)基于xml 方式 注入集合属性

(7)FactoryBean 管理

(8)bean 的作用域

(9)bean 的生命周期

(10)xml 方式 自动装配

(11)xml 方式 引入外部属性文件

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

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

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

(3)基于注解方式 创建对象

(4)用 注解 方式 注入属性

(5)完全注解开发

五、AOP 

(1)什么是 AOP?

(2)AOP 底层原理

(3)JDK 动态代理实现

(4)AOP 的基本术语

(5)AOP操作

(6)基于注解方式完成AOP操作

(7)基于XML方式完成AOP操作

六、JDBCTemplate

1、使用 JDBCTemplate 前的准备工作

2、使用 JDBCTemplate 对数据库进行操作

七、事务

1、事务的概念

2、事务的四大特性(ACID)

3、Spring 中事务的管理

 八、Spring 5 中的新功能

整合日志框架

Nullable注解

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

Spring框架整合JUnit 5


一、Spring 概述

1、Spring 是一个 轻量级的开源的JavaEE框架

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

3、Spring 有俩个核心部分:IOC 容器 和 AOP

IOC :控制反转,把创建对象的过程交给 Spring 进行管理

AOP:面向切面,不修改源代码进行功能增强

4、Spring 的特点:

  • 方便解耦,简化开发
  • 支持AOP编程
  • 方便和其他框架进行整合
  • 方便进行事务操作
  • 降低 API 的开发程度

二、Spring 插件下载

  • 进入官网:spring.io

三、入门案例

导入以下相关的jar 包:

 使用 Spring 创建对象:

1、创建普通类

public class Test_01 {
    public void doSome(){
        //普通创建方式
        // Test_01 test_01 = new Test_01();
        System.out.println("doSome!");
    }
}

2、创建 Spring Config 

class :普通类的路径

3、创建对象

    @Test
    public void test_01(){
        //1、加载配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("test_01.xml");
        //2、创建对象
        Test_01 test_01 = context.getBean("test_01", Test_01.class);
        test_01.doSome();
    }

四、IOC 容器

1、IOC 底层原理

(1)什么是IOC ?

  • 控制反转:把对象创建和对象之间的调用过程,交给 Spring 管理
  • 使用IOC目的:为了耦合度降低

(2)IOC 底层原理 由:XML 解析,反射,工厂设计模式  组成

(3)图解 IOC 底层原理:

 我们用普通方式创建对象,就是 new 对象。这样耦合度太高了,不仅要修改 Dao ,还要修改 Service 。而且,Spring 不会管理。所以 IOC 可以大程度的降低耦合度。就是通过 XML解析,反射,工厂模式三个技术来实现的。

2、IOC 接口

 (1)IOC 思想就是基于IOC容器完成的,IOC容器底层就是个工厂对象

(2)Spring  提供 IOC 容器实现的俩种方式:【俩个接口】

  • BeanFactory :是Spring内部的使用接口,不提供开发人员使用,加载配置文件的时候不会创建对象。在获取对象的时候才会创建。
    • context.getBean("test_01", Test_01.class);
  • ApplicationContext:BeanFactory的子接口,提供更多更强大的功能,一般由开发人员使用。加载配置文件的时候就会创建对象。

(3)ApplicationContext 下的接口实现类:

 FileSystemXMLApplicationContext :配置文件在电脑上的路径。

什么是Bean 管理 ?

  • Bean管理指的是俩个操作:
    • Spring 创建对象
    • Spring 注入属性
  • 基于 Bean 管理有俩个实现方式:
    • 基于xml 配置文件实现
    • 基于 注解方法实现

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

(1)基于 xml 方式创建对象

在创建对象时,实体类中一定要有 无参构造方法,除非是通过有参构造方法注入属性。

    <!--id:自定义的别名-->
    <bean id="test_01" class="yangzhaoguang.spring5.Test_01"></bean>
在 spring 配置文件中,使用 bean 标签,标签里面添加对应属性,就可以实现对象创建。
在 bean 标签有很多属性,介绍常用的属性:
        * id 属性:唯一标识
        * class 属性:类全路径(包类路径)
对象中仅有  有参构造 方法,是会出错的。
 

(2)基于 xml 方式 注入属性

DI:依赖注入属性
第一种注入方式:set 注入

*******属性需要有自己的 set 方法。

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

    public User() {
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
}
注入属性时必须在 bean 标签内部完成。
    <!--1、创建对象-->
    <bean id="user" class="yangzhaoguang.spring5.User">
        <!--2、set 方法注入属性-->
        <!--name:属性名称  value:注入的属性值-->
        <property name="name" value="张三"></property>
        <property name="age" value="20"></property>
    </bean>

第二种注入方式:通过有参构造注入

    <!--第二种方式:通过有参构造方法注入-->
    <bean id="student" class="yangzhaoguang.spring5.Student">
        <constructor-arg name="name" value="李四"></constructor-arg>
        <constructor-arg name="id" value="2"></constructor-arg>
    </bean>

第三种方式:p 名称空间注入【不常用】

<!--增加p名称在属性配置中。在beans标签里面配置-->
xmlns:p="http://www.springframework.org/schema/p"


<bean id="user" class="yangzhaoguang.spring5.User" p:name="王五" p:age="18">
</bean>

(3)基于xml 方式 注入其他属性的值

 设置 null 值

name:设置null值的属性名

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

设置特殊符号

使用 : <![CDATA[ ]]> 

        <!--设置特殊符号-->
        <property name="name"> <value><![CDATA[大佐ぁ次]]></value>  </property>

(4) 基于xml 方式 外部 Bean 注入

【属性值是一个Bean对象,并且这个Bean对象在外部】

原始方式:

在 Service 层调用 Dao 层中的方法:原始方式 new 对象在调用。 

使用 xml 中的 set 注入:

在 Service 层 创建 UserDao 属性,并生成set方法。

    //使用 xml 文件中的 set 注入,为 userDao 属性赋值
    private UserDao userDao ;
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

xml 配置文件:


    <!--创建 UserServiceImpl 对象-->
    <bean id="userService" class="yangzhaoguang.service.UserServiceImpl">
        <!--为userDao属性赋值
            由于 userDao 属性是一个对象,所以需要使用 ref
            ref:对象的id值
        -->
        <property name="userDao" ref="userDao"></property>
    </bean>
    <!--创建 UserDaoImpl 对象-->
    <bean id="userDao" class="yangzhaoguang.dao.UserDaoImpl"></bean>
</beans>

测试:

    /*测试外部 Bean*/
    @Test
    public void add() {
        //加载配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("test_02.xml");
        //创建对象
        UserServiceImpl userService = context.getBean("userService", UserServiceImpl.class);
        userService.add();
    }

(5)基于xml 方式 内部Bean和级联使用

【Bean对象在内部】

实体类:

【内部Bean注入】

赋值的对象在被赋值对象的内部 

    <bean id="emp" class="yangzhaoguang.pojo.Emp">
        <!--设置普通属性-->
        <property name="id" value="1"></property>
        <property name="ename" value="jackson"></property>
        <!--设置对象属性-->
        <property name="dept">
            <!--内部Bean注入-->
            <bean id="dept" class="yangzhaoguang.pojo.Dept">
                <property name="dname" value="销售部"></property>
            </bean>
        </property>
    </bean>

测试:

    //内部注入
    @Test
    public void test_03(){
        //加载配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("test_03.xml");
        //创建对象
        Emp emp = context.getBean("emp", Emp.class);
        emp.print(); //id : 1,姓名 :jackson, 部门 :销售部
    }

【级联使用】

在 外部 注入的基础上多了一个 属性赋值。

第一种写法:

    <bean id="emp" class="yangzhaoguang.pojo.Emp">
        <!--设置普通属性-->
        <property name="id" value="1"></property>
        <property name="ename" value="大壮"></property>
        <!--设置对象属性-->
        <property name="dept" ref="dept"> </property>
    </bean>

    <!--级联赋值
        将 dept 对象中的属性也会赋值过去
    -->
    <bean id="dept" class="yangzhaoguang.pojo.Dept">
        <property name="dname" value="保安部"></property>
    </bean>

第二种写法:

需要为 Dept 对象 提供一个 get 方法:

    <bean id="emp" class="yangzhaoguang.pojo.Emp">
        <!--设置普通属性-->
        <property name="id" value="1"></property>
        <property name="ename" value="大壮"></property>
        <!--设置对象属性-->
        <property name="dept" ref="dept"> </property>
        <property name="dept.dname" value="传销部"> </property>

    </bean>

    <!--级联赋值
        将 dept 对象中的属性也会赋值过去
    -->
    <bean id="dept" class="yangzhaoguang.pojo.Dept"></bean>

测试:

    //级联赋值
    @Test
    public void test_04(){
        //加载配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("test_04.xml");
        //创建对象
        Emp emp = context.getBean("emp", Emp.class);
        emp.print();
    }

(6)基于xml 方式 注入集合属性

** 注入数组类型属性

** 注入List类型属性

** 注入Map类型属性

实体类:

public class Stu {
    //数组属性
    private String[] arr ;

    //List集合属性
    private List<String> list ;

    // Map集合
    private Map<String,String> map ;

    public Stu(String[] arr, List<String> list, Map<String, String> map) {
        this.arr = arr;
        this.list = list;
        this.map = map;
    }

    public Stu() {
    }

    public void setArr(String[] arr) {
        this.arr = arr;
    }

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

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

    /*遍历*/
    public void print(){
        System.out.println(Arrays.toString(arr));
        System.out.println(list);
        System.out.println(map);

    }
}

xml 配置文件:

   <!--注入集合类型-->
        <bean id="stu" class="pojo.Stu">
            <!--注入数组类型-->
            <property name="arr">
                <array>
                    <value>数据结构</value>
                    <value>算法</value>
                </array>
            </property>

            <!--注入List类型-->
            <property name="list">
                <list>
                    <value>Java</value>
                    <value>Python</value>
                </list>
            </property>

            <!--注入Map类型-->
            <property name="map">
                <map>
                    <entry key="1" value="java从入门到入土"></entry>
                    <entry key="2" value="javaWeb"></entry>
                </map>
            </property>
        </bean>

当集合中存储不是基本数据类型,而是对象:

准备一个实体类:

public class Course {
    private String cname ;

    public void setCname(String cname) {
        this.cname = cname;
    }

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

在Stu 实体类中准备一个List,能存储 Course 类型数据:

    //List集合 存的是 Course 类型
    List<Course> courseList ;

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

xml 配置文件: 

           <!--注入List类型,存储类型是:Course-->
            <property name="courseList">
                <list>
                    <!--存入多个对象-->
                    <ref bean="course1"></ref>
                    <ref bean="course2"></ref>
                </list>
            </property>
        </bean>
    
    <!--创建多个对象-->
    <bean id="course1" class="pojo.Course">
        <!--为对象的属性注入值-->
        <property name="cname" value="Java"></property>
    </bean>
    <bean id="course2" class="pojo.Course">
        <property name="cname" value="Python"></property>
    </bean>

目前的集合只能由一个对象使用,想要将集合变成公共的怎么办?

把集合的注入部分提取出来:

  • 在配置文件中引入 util 名称空间
  • 使用 util 标签提取使用
    <!--提取list类型属性注入-->
        <util:list id="bookList">
            <value>Java</value>
            <value>Python</value>
            <value>C++</value>
        </util:list>

    <!--创建对象,注入使用-->
    <bean id="book" class="pojo.Book">
        <!--ref 与 上面的id是一样的。-->
        <property name="list" ref="bookList"></property>
    </bean>
public class Book {
    private List<String> list ;

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

    @Override
    public String toString() {
        return "Book{" +
                "list=" + list +
                '}';
    }
}

(7)FactoryBean 管理

Spring 中 有俩种类型的Bean,一种是 普通的 bean,一种是 工厂bean【FactoryBean】

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

比如:以下定义的就是 Book 类,返回的就是 Book 类型

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

实现工厂bean 的步骤:

1、创建类实现 FactoryBean 接口

2、实现方法,在方法中设置返回值类型。

 这样他的返回值类型时 Course 类

(8)bean 的作用域

在 spring 里面,默认情况下 bean 是单实例对象,但是可以设置为多实例对象

《1》如何设置单实例还是多实例
1 )在 spring 配置文件 bean 标签里面有属性( scope )用于设置单实例还是多实例
(2)scope 属性值
        第一个值  singleton ,表示是单实例对象  默认就是 singleton
        第二个值 prototype ,表示是多实例对象

 《2》singleton 和 prototype 的区别:

singleton,表示是单实例对象,prototype,表示是多实例对象

设置 scope 属性值是 singleton 时,会在加载配置文件的时候就创建对象

设置 scope 属性值是 prototype 时,执行 getBeans 才会创建对象。

《3》scope 中海油俩个值,session,request。他会把创建的对象放到 对应的域中

(9)bean 的生命周期

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

    public Book() {
        System.out.println("第一步: 无参构造方法执行,表示创建了对象");
    }

    public void setList(List<String> list) {
        this.list = list;
        System.out.println("第二步:set 方法执行,表示为属性注入了值");
    }

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

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

    @Override
    public String toString() {
        return "Book{" +
                "list=" + list +
                '}';
    }
}

xml:

初始化和销毁方法需要在 xml 中配置,并且销毁方法需要手动调用,不然不会执行。

    <!--设置初始化和 销毁方法。
        init-method 初始化
        destroy-method 销毁
    -->
    <bean id="book" class="pojo.Book" init-method="init" destroy-method="destroy">
        <property name="list" ref="bookList"></property>
    </bean>

(10)xml 方式 自动装配

1、什么是自动装配
1 )根据指定装配规则(属性名称或者属性类型), Spring 自动将匹配的属性值进行注入
2、自动装配方法
        bean 标签属性 autowire,配置自动装配
        autowire 属性常用两个值:
1 )根据属性名称自动注入
        byName 根据属性名称注入 ,注入值 bean 的 id 值和类属性名称一样
(2)根据属性类型自动注入
        byType 根据属性类型注入

(11)xml 方式 引入外部属性文件

有一些固定属性,可能会通过 Properties 文件进行引入。比如:连接数据库的url,username等等一些属性。

《1》先通过德鲁伊连接池 创建 Properties 配置文件

p.driverClassName=com.mysql.jdbc.Driver
p.url=jdbc:mysql://localhost:3306/test
p.username=root
p.password=root

《2》将 Properties  配置文件引入 xml 配置文件中

    <!--使用 xml 文件引入外部文件-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <!--配置数据库连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${p.driverClassName}"></property>
        <property name="url" value="${p.url}"></property>
        <property name="username" value="${p.username}"></property>
        <property name="password" value="${p.password}"></property>
    </bean>

在写Properties配置文件时,使用前缀将名字区别开。不然会报错。我也不知道为啥。。。。。

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

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

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

其实就是向IOC里面增加组件,组件可以理解为对象。被标注的类保存在IOC容器中。通过getBean方法取出来。

        《1》@Component

        《2》@Service

        《3》@Controller

        《4》@Repository

(3)基于注解方式 创建对象

第一步:引入jar包:

 第二步:在xml配置文件中开启扫描

    <!--
        开启组件扫描:
            如果扫描多个包,用逗号隔开
            或者直接写上层的包
    -->
    <context:component-scan base-package="spring5.dao,lib,spring5.service,spring5.web"></context:component-scan>
 <!--   <context:component-scan base-package="spring5"></context:component-scan>-->

第三步:创建 类 使用注解创建对象

/**使用以下四个注解都可以
 * @Repository
 * @Controller
 * @Service
 * value = "userServiceImpl"  相当于我们使用 xml 配置:<bean id="userServiceImpl"></> 
 * 这个value值就是bean标签中的id
 * 
 * value值也可以不写,他的默认值就是你的类名,首字母小写:userServiceImpl
 */
// @Component(value = "userServiceImpl")
    @Component
public class UserServiceImpl {
    public void add(){
        System.out.println("UserServiceImpl 中的 add 方法.....");
    }
}

第四步:测试

    @Test
    public void add() {
        //加载配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("test_01.xml");
        //创建对象
        UserServiceImpl userServiceImpl = context.getBean("userServiceImpl", UserServiceImpl.class);
        System.out.println(userServiceImpl);
        userServiceImpl.add();

        /*
            输出结果
            spring5.service.UserServiceImpl@78047b92
            UserServiceImpl 中的 add 方法.....
        */
    }

《1》关于组件扫描配置

    <!--组件扫描配置-->
    
    <!--
        实例一:
        use-default-filters="false":表示不使用默认 filter ,自己配置filter 
    -->
    <context:component-scan base-package="spring5" use-default-filters="false">
        <!--include-filter:表示只扫描 Controller 注解 -->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    
    <!--实例二-->
    <context:component-scan base-package="spring5">
        <!--exclude-filter:表示不扫描 Repository 注解-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>

(4)用 注解 方式 注入属性

        《1》@AutoWired    根据属性类型自动装配

        《2》@Qualifier        根据属性名称进行注入

        《3》@Resource        可以根据类型注入,也可以根据名称注入

        《4》@Value               注入普通属性【以上三个都是注入对象属性的】

@AutoWired 演示:在 Service 层注入 Dao 对象:

我们之前使用 xml 方式:

1、先创建 UserDaoImpl 和 UserServiceImpl 的对象

2、在 Service 层提供一个 UserDao属性,并提供一个 set 方法。

3、在 xml 文件中进行配置

使用注解的方式:

1、先创建 UserDaoImpl 和 UserServiceImpl 的对象

2、在 Service 层提供一个 UserDao属性,但是并不需要提供set 方法

@Qualifier 演示:他需要和 @AutoWired  一起使用:如果不一起使用就会报空指针

什么时候用 @Qualifier ? 当一个接口有多个实现类的时候,根据类型注入已经找不到哪个实现类了。就使用  @Qualifier

  @Resource   注入属性演示:这个   @Resource  并不是 Spring 中的,而是 JDK 中的

@Value  注解演示:

    //普通属性注入
    @Value("张三")
    private String name ;

(5)完全注解开发

使用配置类代替 xml 文件,完成注解开发

@Component :  [kəmˈpəʊnənt] 

//注解类,代替xml
@Configuration //表示 当前这个类为注解类
@ComponentScan(basePackages = {"spring5"}) // 扫描的范围---spring5包下
public class AnnotationConfig {

}

除了测试的方法和以前不一样,剩下都一样。

五、AOP 

1、AOP 概念 

 (1)什么是 AOP?

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

(2)AOP 底层原理

AOP 底层使用的是 动态代理【  代理模式 为其他对象提供一个 代理以控制对某个对象的访问】
第一种情况:有接口的情况,使用 JDK 中的动态代理
 创建接口实现类的代理对象,增强类的方法
        

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

(3)JDK 动态代理实现

底层代码实现,Spring 里已经进行封装了!

JDK 中实现动态代理,在java.lang包下的 Proxy 类中:

 使用这个方法为接口的实现类创建代理对象:

方法有三个参数:
第一参数,类加载器
第二参数,增强方法所在的类,这个类实现的接口,支持多个接口
第三参数,实现这个接口 InvocationHandler,创建代理对象,写增强的部分
public class UserDaoProxy {
    public static void main(String[] args) {

        Class[] userDaoClass = {UserDao.class};
        //获取动态代理对象
        //参数分别是:类加载器,增强方法所在的类的接口数组,InvocationHandler 增强实现类,写增强的代码
        //匿名内部类的形式。
        // Proxy.newProxyInstance(UserDaoProxy.class.getClassLoader(), userDao, new InvocationHandler() {
        //     @Override
        //     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //         return null;
        //     }
        // });
        UserDao userDao = new UserDaoImpl();
        UserDao userDao1 = (UserDao) Proxy.newProxyInstance(UserDao.class.getClassLoader(),
                userDaoClass, new UserDaoImplProxy(userDao));

        int res = userDao1.add(1, 2);
        System.out.println("增强完的结果:" + res);
    }
}

//除了使用匿名内部类的方式,还可以创建一个类实现 InvocationHandler 接口
class UserDaoImplProxy implements InvocationHandler {

    //一般使用有参构造方法来传递调用方法时所需要的对象
    private Object obj;

    public UserDaoImplProxy(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //在这里实现增强的部分
        System.out.println("执行方法之前执行的代码。。。。。");
        //invoke参数:方法所在类的对象,参数
        //返回的是方法结果
        Object res = method.invoke(obj, args);

        //还可以通过方法名对具体的方法做修改
        if ("add".equals(method.getName())) {
            //对 add 方法进行修改
        } else if ("update".equals(method.getName())) {
            // 对update 方法进行修改
        }

        System.out.println("执行方法之后执行的代码。。。。。。");

        return res;

    }
}

(4)AOP 的基本术语

《1》连接点

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

《2》切入点

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

《3》通知(增强)

实际上增强的部分,叫做通知。

通知又分为:

  • @Before 前置通知:在增强方法的前面执行
  • @AfterReturning 后置通知,又叫返回通知:在增强方法的后面执行
  • @Around 环绕通知:在增强方法的前面和后面都会执行
  • @AfterThrowing 异常通知:出现异常执行
  • @After 最终通知:

《4》切面

是一个动作,将通知应用到切入点的过程,就是切面

比如:我们在刚才的例子中 if。。else 的动作就是切面。

(5)AOP操作

Spring 框架一般都是基于 AspectJ 实现 AOP 操作
《1》什么是 AspectJ  ?
AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使 用,进行 AOP 操作
《2》基于 AspectJ 进行 AOP 操作
        基于 xml 方式
        基于注解方式(经常使用)
《3》引入相关的 jar 包
《4》切入点表达式
切入点表达式的作用: 知道哪个类里面的哪个方法进行增强
语法结构:
execution ([ 权限修饰符 ]  [ 返回类型 ] [ 类全路径 ]  [ 方法名称 ]  [ 参数列表 ] )

 (6)基于注解方式完成AOP操作

《1》创建普通类:被增强的类

//被增强的类
public class UserServiceImpl {
    public void add(){
        System.out.println("UserServiceImpl中的add 方法、、、、、");
    }
}

《2》创建增强类

//增强类
public class UserServiceImplProxy {

    //将这个方法作为 前置通知 增加到UserServiceImpl中的add方法前执行。
    public void before(){
        System.out.println("前置通知.....");
    }
}

《3》进行通知配置

  • 开启注解扫描【注解类或者xml方式都可以】
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <!--开启注解扫描-->
    <context:component-scan base-package="aopByAnnotation"></context:component-scan>
    

</beans>
  • 使用@Component(四个注解都可以)创建 增强类 与 被增强类的对象

  • 在配置文件中开启 AspectJ 生成代理对象
    <!--开启 aspectj 生成代码对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
  • 用 @Aspect 在增强类中生成代理对象使

《4》配置不同类型的通知 

在增强类里面,通知上面增加通知类型注解,使用切入点表达式配置

@Component
//增强类
@Aspect
public class UserServiceImplProxy {

    //前置通知类型注解。
    // * 表示所有的修饰符类型
    @Before(value = "execution(* aopByAnnotation.UserServiceImpl.add(..))")
    //将这个方法作为 前置通知 增加到UserServiceImpl中的.add方法前执行。
    public void before(){
        System.out.println("前置通知.....");
    }

    //后置通知(返回通知)没有异常的时候执行
    @AfterReturning(value = "execution(* aopByAnnotation.UserServiceImpl.add(..))")
    public void afterReturning(){
        System.out.println("后置通知.....");
    }

    //环绕通知
    //会在增强方法前面和后面都要执行。
    @Around(value = "execution(* aopByAnnotation.UserServiceImpl.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕之前.....");
        //被增强的方法执行
        proceedingJoinPoint.proceed();
        System.out.println("环绕之后.....");
    }

    //异常通知。只有异常的时候会执行
    @AfterThrowing(value = "execution(* aopByAnnotation.UserServiceImpl.add(..))")
    public void afterThrowing(){
        System.out.println("异常通知.....");
    }

    //最终通知,不管有没有异常都会执行
    @After(value = "execution(* aopByAnnotation.UserServiceImpl.add(..))")
    public void after(){
        System.out.println("最终通知.....");
    }
}

《5》抽取切入点表达式

上面五种类型的通知,它们的切入点表达式都是一样的,所以我们可以抽取出来。

    //抽取切入点表达式
    @Pointcut(value = "execution(* aopByAnnotation.UserServiceImpl.add(..))")
    public void pointDemo(){
        
    }

在切入点表达式的位置上调用这个方法:

《6》有多个增强类对同一个方法增强,设置增强类优先级

再增强类上,使用 @Order(数值类型) 注解,数值越小优先级越高。

《7》完全注解开发

@Configuration //注解类
@ComponentScan(basePackages = {"aopByAnnotation","aopByXML"}) //设置扫描范围
//开启Aspect 生成代理对象 相当于 <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAOP {

}

(7)基于XML方式完成AOP操作

1 、创建两个类,增强类和被增强类,创建方法
2 、在 spring 配置文件中创建两个类对象
    <!--创建增强类与被增强类的对象-->
    <bean id="book" class="aopByXML.Book"></bean>
    <bean id="bookProxy" class="aopByXML.BookProxy"></bean>
3 、在 spring 配置文件中配置切入点
    <!--配置增强方法-->
    <aop:config>
        <!--设置切入点-->
        <aop:pointcut id="point" expression="execution(* aopByXML.Book.add(..))"/>
        <!--设置切面-->
        <aop:aspect ref="bookProxy">
            <!--增强作用在具体的方法上-->
            <aop:before method="before" pointcut-ref="point"></aop:before>
        </aop:aspect>

    </aop:config>

六、JDBCTemplate

Spring 对 JDBC 进行了封装,使用 JDBCTemplate 可以很方便的操作数据库。

1、使用 JDBCTemplate 前的准备工作

 (1)引入相关 jar 包:

 (2)在xml 文件中配置好数据库连接池并创建好数据库

 (3)创建 JDBCTemplate对象,注入DataSource对象属性。

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

(4)创建 service 层和 dao层,service 注入 dao 对象,在dao层注入 JDBCTemplate对象。

2、使用 JDBCTemplate 对数据库进行操作

《1》实现数据库增删改查 

BookDao :


public interface BookDao {
    int add(Book book);

    int update(Book book);

    int  del(Integer id);

    //查询数据库记录
    int findCount();

    //根据id,查询返回对象
    Book findForOne(Integer id);

    //查询多条记录
    List<Book> list();
}

 BookDaoImpl:

@Repository //注解创建对象
public class BookDaoImpl implements BookDao {

    //注入JDBCTemplate对象。不需要我们手动写连接数据库,使用Spring里面的 JDBCTemplate对象
    @Autowired //根据数据类型注入属性
    private JdbcTemplate jdbcTemplate;

    //实现增加功能
    @Override
    public int add(Book book) {
        String sql = "insert into book(name,price) values(?,?)";
        return jdbcTemplate.update(sql, book.getName(), book.getPrice());
    }

    //修改
    @Override
    public int update(Book book) {
        String sql = "update book set name=? ,price =? where id=?";
        return jdbcTemplate.update(sql, book.getName(), book.getPrice(), book.getId());
    }

    //删除
    @Override
    public int del(Integer id) {
        String sql = "delete from book where id= ?";
        return jdbcTemplate.update(sql, id);
    }

    @Override
    public int findCount() {
        String sql = "select count(*) from book";
        /*返回类型的class*/
        return jdbcTemplate.queryForObject(sql, int.class);
    }

    @Override
    public Book findForOne(Integer id) {
        String sql = "select * from book where id=?";
        return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), id);
    }

    @Override
    public List<Book> list() {
        String sql = "select * from book";
        return jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));
    }
}

BookService与BookDao代码一样。

BookServiceImpl:

@Service //注解创建对象
public class BookServiceImpl implements BookService {

    //使用注解注入属性
    @Autowired
    private BookDao bookDao;

    @Override
    public int add(Book book) {
        return bookDao.add(book);
    }

    @Override
    public int update(Book book) {
        return bookDao.update(book);
    }

    @Override
    public int del(Integer id) {
        return bookDao.del(id);
    }

    @Override
    public int findCount() {
        return bookDao.findCount();
    }

    @Override
    public Book findForOne(Integer id) {
        return bookDao.findForOne(id);
    }

    @Override
    public List<Book> list() {
        return bookDao.list();
    }
}

测试:

  @Test
    public void add() {
        ApplicationContext context = new ClassPathXmlApplicationContext("test_01.xml");
        BookServiceImpl bookServiceImpl = context.getBean("bookServiceImpl", BookServiceImpl.class);
        Book book = new Book(null,"钢铁是怎样炼成的",new BigDecimal(233));

         //增加
         // bookServiceImpl.add(book);
        //修改
        // bookServiceImpl.update(new Book(2,"废物一生",new BigDecimal(999)));
        //删除
        // bookServiceImpl.del(1);

        //查询数据库记录条数
        int count = bookServiceImpl.findCount();
        System.out.println(count);

        //查询对象
        Book book1 = bookServiceImpl.findForOne(2);
        System.out.println(book1);

        //查询返回集合
        List<Book> list = bookServiceImpl.list();
        list.forEach(System.out::println);
    }

《2》批量增加,修改,删除操作:

 Dao层:

 Service 层:

测试:

七、事务

1、事务的概念

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

最经典的案例:就是银行转账。

2、事务的四大特性(ACID)

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

3、Spring 中事务的管理

一般事务的处理都放在 service 层。

(1)在Spring事务管理操作:

      有俩种方式:编程式事务管理和声明式管理

(2)声明式管理包括:

        基于注解管理

        基于xml管理

(3)在 Spring 进行声明式事务管理,底层使用 AOP 原理
(4)Spring 事务管理 API
        《1》提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类

 (5)注解声明式管理事务

《1》在 Spring 开启事务管理器

    <!--配置事务管理器,并注入DataSource-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

《2》开启事务注解

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

    需要引入命名空间:
http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd
 xmlns:tx="http://www.springframework.org/schema/tx"

《3》在 service 层加上事务注解@Transactional

@Transactional可以增加到类上,也可以增加到方法上。增加到类上表示所有的方法都能使用事务,增加到方法上只有该标注的方法能使用

(6)声明式事务管理参数配置

@Transactional 这个注解可以配置的参数:

 《1》propagation 事务传播行为事务方法直接进行调用,这个过程中事务 是如何进行管理的

事务方法:对数据库的增删改,查不算因为查没有涉及到数据变化。

多事务方法:在事务方法里调用其他方法。

 

 设置传播行为:

 《2》isolation 隔离级别:多事务操作之间不产生影响。不考虑隔离性会产生三个问题:

        脏读,不可重复读,幻读(虚读)

脏读:一个未提交的事务读到另一个未提交的事务(读未提交)

不可重复度:一个未提交的事务读到另一个提交事务修改的数据(读已提交)

幻读:一个未提交的事务读到另一个提交事务增加的数据

设置隔离级别可解决读问题:

 设置隔离级别:

 《3》timeout 超时时间 : 事务需要在一定时间内进行提交,如果超过 超时时间 自动回滚。默认时间 -1 ,表示不超时。设置时间以秒为单位。

《4》readOnly 是否只读:读:表示查询操作   写:添加修改删除操作 

默认就是 false,可以进行增删改查

《5》rollbackFor 回滚 :设置出现哪些异常进行事务回滚

《6》noRollbackFor 不回滚:设置出现哪些异常不进行事务回滚

(7)完全注解方式

//使用完全注解方式使用事务
@Configuration //注解类
@ComponentScan(basePackages = {"dao","service"}) //设置注解扫描范围
@EnableTransactionManagement //开启事务注解
public class ConfigTx {

    @Bean
    //1、创建数据库连接池
    public DataSource getDataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }
    @Bean
    //2、创建JDBCTemplate对象,并注入 DataSource
    //DataSource 经过上面已经创建好放到 IOC 容器当中。
    //会自动根据类型注入属性
    public JdbcTemplate getJdbcTemplate(DataSource dataSource){
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        //注入属性
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    @Bean //表示创建对象
    //3、创建 事务管理器对象,并注入 DataSource
    public DataSourceTransactionManager  getTransactionManager(DataSource dataSource){
        DataSourceTransactionManager  transactionManager = new DataSourceTransactionManager();
        //注入 DataSource 对象。
        transactionManager.setDataSource(dataSource);
        return transactionManager ;
    }
}

 八、Spring 5 中的新功能

1、整个 SPring5 框架的diamante基于Java8 ,运行时兼容 Java9,许多不建议使用的类和方法在代码库中删除。

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

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

(2)Spring5 框架整合 Log4j2

整合日志框架

1、导入相关jar包:

2、创建 log4j2.xml 文件 ,名字不能错; 

Nullable注解

@Nullable 注解可以出现在方法、属性、参数上,表示:方法返回值可以为空,属性可以为空,参数可以为空。

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

//函数式风格创建对象,交给 spring 进行管理
@Test
public void testGenericApplicationContext() {
 //1 创建 GenericApplicationContext 对象
 GenericApplicationContext context = new GenericApplicationContext();
 //2 调用 context 的方法对象注册
 context.refresh();
 // "name":可写可不写,不写就通过类的全路径创建对象。写就可以通过name创建对象
 context.registerBean("user1",User.class,() -> new User());
 //3 获取在 spring 注册的对象
 // User user = (User)context.getBean("com.atguigu.spring5.test.User");
 User user = (User)context.getBean("user1");
 System.out.println(user);
}

前面说过,我们自己new的对象,Spring是不会进行管理的。但是我们可以通过 GenericApplicationContext 来进行注册。让Spring进行管理我们自己new'出来的对象。

Spring框架整合JUnit 5

第一步:引入 JUnit 5 相关 jar 包:

 第二步:测试

// @ExtendWith(SpringExtension.class)
// @ContextConfiguration("classpath:test_01.xml") //加载配置文件,自动创建好了对象
    
//    也可用以下一行代码代替上面俩行:
@SpringJUnitConfig(locations = "classpath:test_01.xml")
public class Test_Junit5 {
    @Autowired
    private UserService userService ;
    @Test
    public void test(){
        userService.account(200.0);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鲨瓜2号

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值