Spring笔记

文章目录

  • 1. Spring
    • 搭建Spring框架
    • Spring中getBean()的三种方式
    • bean标签详解
  • 2. Spring IOC
  • 3. Spring中依赖注入的数值问题
    • 字面量数值(value)
    • CDATA区
    • 外部已声明bean及级联属性赋值问题
    • 内部bean
    • 集合
  • 4. Sping依赖注入方式
  • 5. Spring 管理第三方bean
  • 6. Spring中的FactoryBean(了解)
        • Spring中两种bean
        • FactoryBean使用步骤
  • 7. Spring中bean的作用域
        • 语法
        • 四个作用域
  • 8.Spring中bean的生命周期
        • bean的生命周期
        • bean的后置处理器
  • 9. Spring中的自动装配
        • Spring中提供两种装配方式
        • Spring自动装配语法及规则
        • 总结
  • 10.Spring中注解(重要)
    • 使用注解装配对象到IOC
    • 使用注解管理对象之间的依赖关系
  • 11. Spring中组件扫描
    • 包含扫描/排除扫描
  • 12. Spring中完全注解开发
    • 完全注解开发步骤
  • 13.Spring 集成 Junit4
    • 集成步骤
  • 14. Spring AOP前奏
    • 代理模式
    • 为什么需要代理?
    • 手动实现动态代理步骤
  • 15. Spring中的AOP(重要)
    • AspectJ框架【AOP框架】
    • 使用AspectJ步骤
    • Spring中AOP概述
    • Spring中AOP相关术语
  • 16. AspectJ详解(重要)
    • AspectJ切入点表达式
    • AspectJ中JoinPoint对象
    • AspectJ中通知
    • 定义切面优先级
    • 基于XML方式配置AOP
  • 17. Spring中的JdbcTemplate
    • JdbcTemplate简介
    • JdbcTemplate使用步骤
    • JdbcTemplate常用API
    • 使用JdbcTemplate搭建Service&Dao层
  • 18. Spring声明式事务管理(重要)
    • Spring中事务管理
    • 使用声明式事务管理
    • Spring声明式事务管理属性
      • 事务传播行为【Propagation】
      • 事务隔离级别【Isolation】
      • 其他属性
    • 基于XML方式配置声明式事务管理
  • 19. Spring5新特性
    • 添加了新注解
    • Spring5整合Log4j2
    • Spring5整合Junit5

1. Spring

Spring是一个为了简化企业级开发的开源框架。

Spring的特性:

  • Spring是一个IOC和AOP容器框架。
    • Spring最受认同的是控制反转的**依赖注入(DI)**风格。 Inversion of Control (IoC) 控制反转是一个通用概念,可以用多种不同的方式表达。 依赖注入只是控制反转的一个具体例子。
    • AOP:Aspect-Oriented Programming,面向切面编程
  • 非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的API。
  • 容器:Spring是一个容器,因为它包含并且管理应用对象的生命周期。
  • 组件化:Spring实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用XML和Java注解组合这些对象。
  • 一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上Spring 自身也提供了SpringMVC和持久层的JDBCTemplate)。

搭建Spring框架

  • 导入jar包

    
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-contextartifactId>
        <version>5.3.1version>
    dependency>
    
    <dependency>
        <groupId>junitgroupId>
        <artifactId>junitartifactId>
        <version>4.12version>
        <scope>testscope>
    dependency>
    
  • 编写核心配置文件【applicationContext.xml/beans.xml/spring.xml】

    配置文件路径:src/main/resources

    • 示例代码

      
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
      
          
          <bean id="Stu" class="Spring.pojo.Student">
              <property name="stuId" value="1">property>
              <property name="stuName" value="Sivan">property>
          bean>
      
      beans>
      
  • 使用核心类库

    public void test(){
        //使用Spring之前
        Student student = new Student();
        //使用Spring之后
        //创建ioc容器
        ApplicationContext iocObj =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        //通过容器对象id获取需要对象。
        Student Stu = (Student)iocObj.getBean("Stu",Student.class);
    
        System.out.println(Stu);
    
    }
    

Spring中getBean()的三种方式

  • getBean(String beanId):通过beanId获取对象。【但是需要类型转换】
  • getBean(Class clazz):通过反射获取对象。【但是容器中有多个相同bean,会报错】
  • getBean(String beanId,Class clazz):整合前两种方法。

bean标签详解

  • 属性
    • id:bean的唯一标识
    • class:定义bean的类型【Class全类名】
  • 子标签
    • property:为对象中的属性赋值【set注入方式】
      • name:设置属性名称
      • value:设置属性数值
      • ref:ref = "dataSource"就是引用dataSource的bean
        spring容器会在引用后进行验证,验证当前的xml是否存在引用的bean

2. Spring IOC

IOC:将对象的控制权反转给Spring

Spring笔记_第1张图片

  • BeanFactory接口

    IOC容器的基本实现,是Spring的内部使用接口,面向Spring本身。

    • ApplicationContext接口

      BeanFactory的子接口,提供了更多高级特性。面向Spring的使用者。

      • ConfigurableApplicationContext接口

        ​ 提供关闭或刷新容器对象的方法。

        • ClassPathXmlApplicationContext与FileSystemXmlApplicationContext实现类

          基于类路径检索xml文件/基于文件系统检索xml文件。

3. Spring中依赖注入的数值问题

字面量数值(value)

  • 数据类型:基本数据类型或包装类
  • 语法:value属性或value标签

CDATA区

<bean id="Stu2" class="Spring.pojo.Student">
        <property name="stuId" value="2">property>
        <property name="stuName">

            <value>>]]>value>
        property>
    bean>

外部已声明bean及级联属性赋值问题

使用外部已声明bean的语法:ref

级联属性更改数值之后会影响外部声明的bean。(ref中使用的是引用)

示例代码:


    <bean id="Dept01" class="Spring.pojo.Dept">
        <property name="deptId" value="1">property>
        <property name="deptName" value="财务部">property>
     bean>

    <bean id="Emp01" class="Spring.pojo.Employee">
        <property name="id" value="1">property>
        <property name="email" value="[email protected]">property>
        <property name="lastName" value="WWW">property>
        <property name="salary" value="100.00">property>
        <property name="dept" ref="Dept01">property>

        <property name="dept.deptName" value="研发部">property>
    bean>

内部bean

概念:在bean标签中定义另一个完整的bean

内部bean不会直接装配到IOC容器中。所以无法从容器中直接获取。


    <bean id="Emp02" class="Spring.pojo.Employee">
        <property name="id" value="2">property>
        <property name="email" value="[email protected]">property>
        <property name="lastName" value="XXX">property>
        <property name="salary" value="110.00">property>
        <property name="dept">
            <bean class="Spring.pojo.Dept">
                <property name="deptName" value="XXX部门">property>
                <property name="deptId" value="1">property>
            bean>
        property>
    bean>

集合

如果传入bean中的属性是集合,就不能简单的用来使用。

应该用以下方式:

  • List集合

    
        <utils:list id="list01">
            <ref bean="Emp01">ref>
            <ref bean="Emp02">ref>
        utils:list>
        <bean id="Dept02" class="Spring.pojo.Dept">
            <property name="deptId" value="1">property>
            <property name="deptName" value="研发部">property>
            <property name="empList" ref="list01">property>
         bean>
    
  • Map集合

    
        <utils:map id="map01">
            <entry key="101" value-ref="Emp01">entry>
    
            <entry >
                <key><value>102value>key>
                <ref bean="Emp02">ref>
            entry>
        utils:map>
        <bean id="EmpMap" class="Spring.pojo.Dept">
            <property name="deptId" value="101">property>
            <property name="deptName" value="研发部门">property>
            <property name="empMap" ref="map01">property>
        bean>
    

4. Sping依赖注入方式

  • 调用set方法注入

    语法:标签

  • 使用构造器注入

    语法:

  • 使用p名称空间注入

    需要先导入:xmlns:p=“http://www.springframework.org/schema/p”

    语法:在bean标签中,使用p:XXX。也是使用set方法注入。

示例代码:

<bean id="Stu3" class="Spring.pojo.Student">
        <constructor-arg name="stuId" value="3">constructor-arg>
        <constructor-arg name="stuName" value="WWW">constructor-arg>
    bean>

    <bean id="Stu4" class="Spring.pojo.Student" p:stuId="4" p:stuName="XXX" >bean>

5. Spring 管理第三方bean

以Spring管理Druid数据源为例。

  1. 导入jar包

    
            <dependency>
                <groupId>com.alibabagroupId>
                <artifactId>druidartifactId>
                <version>1.1.10version>
            dependency>
            
            <dependency>
                <groupId>mysqlgroupId>
                <artifactId>mysql-connector-javaartifactId>
                <version>8.0.28version>
            dependency>
    
  2. 编写db.properties配置文件

    #这里名字最好使用一个前缀,防止与Mysql冲突
    db.driverClassName=com.mysql.cj.jdbc.Driver
    db.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8
    db.username=root
    db.password=root
    
  3. 编写applicationContext.xml

    //加载外部属性文件db.properties
       <context:property-placeholder location="classpath:db.properties">context:property-placeholder>
    
    //装配数据源
        <bean id="Druid" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="${db.driverClassName}">property>
            <property name="url" value="${db.url}">property>
            <property name="username" value="${db.username}">property>
            <property name="password" value="${db.password}">property>
        bean>
    beans>
    
  4. 测试

        ApplicationContext ioc = new 	ClassPathXmlApplicationContext("ApplicationContext_druid.xml");
        DruidDataSource druid = ioc.getBean("Druid", DruidDataSource.class);
        System.out.println("druid = " + druid);
        DruidPooledConnection connection = druid.getConnection();
        System.out.println("connection = " + connection);
    

6. Spring中的FactoryBean(了解)

Spring中两种bean

  • 一种是普通bean
  • 另一种是工厂bean【FactoryBean】
    • 作用:如需我们程序员参与到bean的创建时,使用FactoryBean。bean返回的类型由getObject()方法决定

FactoryBean使用步骤

  • 实现FactoryBean接口

  • 重写方法【三个】

    package factory;
    
    import pojo.Dept;
    import org.springframework.beans.factory.FactoryBean;
    
    public class MyFactoryBean implements FactoryBean<Dept> {
    
        /**
         * getObject():参数对象创建的方法
         * @return
         * @throws Exception
         */
        @Override
        public Dept getObject() throws Exception {
            Dept dept = new Dept(101,"研发部门");
            //.....
            return dept;
        }
    
        /**
         * 设置参数对象Class
         * @return
         */
        @Override
        public Class<?> getObjectType() {
            return Dept.class;
        }
    
        /**
         * 设置当前对象是否为单例
         * @return
         */
        @Override
        public boolean isSingleton() {
            return true;
        }
    
    }
    
  • 装配工厂bean

    在applicationContext.xml中使用bean标签装配工厂bean。

  • 测试使用

7. Spring中bean的作用域

语法

  • 在bean标签中添加scope属性即可

四个作用域

  • singleton【默认值】:单例【在容器中只有一个对象】
    • 对象创建时机:创建容器对象时,创建对象。
  • prototype:多例【在容器中有多个对象】
    • 对象创建时机:getBean()方法被调用时,创建对象。
  • request:请求域
    • 当前请求有效,离开请求域失效
    • 当前请求:URL不变即为当前请求
  • session:会话域
    • 当前会话有效,离开当前会话失效
    • 当前会话:当前浏览不关闭不更换即为当前会话

8.Spring中bean的生命周期

bean的生命周期

① 通过构造器或工厂方法创建bean实例

<bean id="Student" class="Spring.pojo.Student"
        init-method="initMethod"
        destroy-method="destroyMethod">
        <property name="id" value="1">property>
        <property name="name" value="Xin">property>
    bean>

② 为bean的属性设置值(比如setid方法)和对其他bean的引用

③ 调用bean的初始化方法

④ bean可以使用了

当容器关闭时,若使用ConfigurableApplicationContext初始化容器,则可以调用bean的销毁方法context.close

bean的后置处理器

  • 作用:在调用初始化方法前后对bean进行额外的处理

  • 实现:

    • 实现BeanPostProcessor接口

    • 重写方法

      • postProcessBeforeInitialization(Object, String):在bean的初始化之前执行
      • postProcessAfterInitialization(Object, String):在bean的初始化之后执行
    • 在容器中装配后置处理器

      
          <bean class="Spring.pojo.processor.Myprocessor">bean>
      
  • 注意:装配后置处理器会为当前容器中每个bean均装配,不能为局部bean装配后置处理器

9. Spring中的自动装配

Spring中提供两种装配方式

  • 手动装配:自己使用bean中的property属性进行装配。
  • 自动装配:使用Autowire属性。

Spring自动装配语法及规则

  • 在bean标签中添加属性:Autowire即可

    • byName:对象中属性名称与该属性容器中的beanid进行匹配,如果一致,则自动装配成功

    • byType:对象中属性类型与该属性容器中的beanclass进行匹配,如果唯一,则自动装配成功

      • 匹配0个:未装配

      • 匹配多个,会报错

        expected single matching bean but found 2: deptDao,deptDao2(期望匹配单个bean,但找到两个bean)

          <!--将deptDao装配到容器中-->
          <bean id="deptDao" class="Spring.dao.impl.DeptImpl"></bean>
          <!--自动装配——byName,属性名与该属性的beanid进行匹配。-->
          <bean id="deptService" class="Spring.service.impl.DeptServiceImpl" autowire="byName"></bean>
          <!--    自动装配——byType,属性类型与class一致-->
          <bean id="deptService2" class="Spring.service.impl.DeptServiceImpl" autowire="byType"></bean>
      
  • 注意:基于XML方式的自动装配,只能装配非字面量数值

总结

  • 基于xml自动装配,底层使用set注入
  • 最终:不建议使用byName、byType,建议使用注解方式自动装配

10.Spring中注解(重要)

使用注解装配对象到IOC

位置:在类的上面标识。

注意:

  1. Spring本身不区分四个注解【注解本质一样,都是Component】,目的是提高代码可读性。
  2. Java中的约定:约束(Maven包结构、Java命名规则等)>注解>XML>代码
  3. 关于beanid:不使用value,默认将类名首字母小写作为beanid。也可以使用value属性/省略value从而设置beanid。
  • 装配对象的四个注解:

    • @Component(组件):装配普通组件到IOC中。

    • @Repository(仓库):装配持久化层到IOC中。

    • @Service(业务):装配业务逻辑层到IOC中。

    • @Controller(控制):装配控制层/表示层组件到IOC中。

  • 使用注解装配对象的步骤:

    1. 使用组件扫描(目的是扫描到注解)

      //注意命名空间。
      
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:context="http://www.springframework.org/schema/context"
             xsi:schemaLocation="http://www.springframework.org/schema/beans
                 http://www.springframework.org/schema/beans/spring-beans.xsd
                 http://www.springframework.org/schema/context
                 http://www.springframework.org/schema/context/spring-context-3.0.xsd">
      
      
          <context:component-scan base-package="Spring">context:component-scan>
      
      beans>
      
    2. 使用注解标识组件(标识的是实现类Impl,因为只有实现类才可以创建对象)

      ApplicationContext context =
                      new ClassPathXmlApplicationContext("applicationContext.xml");
              //使用Component注解
              Dept dept = context.getBean("dept", Dept.class);
              System.out.println("dept = " + dept);
              //使用Repository注解
              DeptDaoImpl deptdaoimpl = context.getBean("deptdaoimpl", DeptDaoImpl.class);
              System.out.println("deptdaoimpl = " + deptdaoimpl);
              //使用Service注解
              DeptServiceImpl deptserviceimpl = context.getBean("deptserviceimpl", DeptServiceImpl.class);
              System.out.println("deptserviceimpl = " + deptserviceimpl);
              //使用Controller注解
              DeptController deptController = context.getBean("deptController", DeptController.class);
              System.out.println("deptController = " + deptController);
      

使用注解管理对象之间的依赖关系

依赖关系:比如Service层依赖Dao层的对象。

使用**@Autowired注解**来管理对象之间的依赖关系(装配对象中的属性)。

  • 使用方式:直接注解到属性上。

  • 装配原理:反射机制

  • 装配方式

    • 先按照byType进行匹配(寻找接口的实现类)

      • 匹配1个:匹配成功,正常使用

      • 匹配0个:

        • 默认【@Autowired(required=true)】报错

          /*expected at least 1 bean which qualifies as autowire candidate. 	Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
          */
          
        • @Autowired(required=false),不会报错

      • 匹配多个

        • 再按照byName进行唯一筛选

          • 筛选成功【对象中属性名称==beanId】,正常使用

          • 筛选失败【对象中属性名称!=beanId】,报如下错误:

            //expected single matching bean but found 2: deptDao,deptDao2
            
  • @Autowired中required属性

    • true:表示被标识的属性必须装配数值,如未装配,会报错

    • false:表示被标识的属性不必须装配数值,如未装配,不会报错

  • @Qualifier注解

    • 作用:配合@Autowired使用,可以设置装配属性的beanid,而不是必须与属性名相同。
    • 注意:不能单独使用,需要与@Autowired一起使用。
  • @Value注解

    • 作用:装配对象中的属性(针对字面量数值)

11. Spring中组件扫描

组件扫描:扫描自己写的注解,可以让程序识别。

默认情况下会扫描包下面的所有包。如果想要扫描100个包中个个别几个,可以使用包含扫描/排除扫描。

包含扫描/排除扫描

  • 注意:
    • 使用包含/排除扫描之前,必须设置use-default-filters="false"【关闭当前包及其子包的扫描】
    • type
      • annotation:设置被扫描注解的全类名
      • assignable:设置被扫描实现类的全类名

<context:component-scan base-package="Spring" use-default-filters="false">
    <context:include-filter type="annotation" 			 expression="org.springframework.stereotype.Repository"/>
    <context:include-filter type="assignable" expression="Spring.service.impl.DeptServiceImpl"/>
    
    <context:exclude-filter type="assignable" expression="Spring.controller.DeptController"/>
context:component-scan>

12. Spring中完全注解开发

完全取消xml配置过程。

完全注解开发步骤

  1. 创建配置类

  2. 在配置类上添加注解

    • @Configuration:标识当前的类是一个配置类,用来代替xml。
    • @ComponentScan:设置组件扫描的当前包以及子包。

    示例代码:

    @ComponentScan(basePackages = "Spring")
    @Configuration
    public class SpringConfig {
    }
    
  3. 使用 AnnotationConfigApplicationContext 创建容器对象

    示例代码:

    @Test
        public void test(){
            ApplicationContext context =
                    new AnnotationConfigApplicationContext(SpringConfig.class);
            Object deptdaoimpl = context.getBean("deptdaoimpl");
            System.out.println("deptdaoimpl = " + deptdaoimpl);
        }
    

13.Spring 集成 Junit4

Spring提供了对Junit的集成,所以可以在测试类中直接注入IOC容器中的对象。

集成步骤

  1. 导入jar包支持

    
            <dependency>
                <groupId>org.springframeworkgroupId>
                <artifactId>spring-testartifactId>
                <version>5.3.1version>
                <scope>testscope>
            dependency>
    
  2. 指定Spring配置文件路径/Config类【使用@ContextConfiguration】

  3. 指定Spring环境下的Junit4 的运行器【使用@RunWith】

示例代码:

@ContextConfiguration(classes = SpringConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class TextJunit {
    @Autowired
    DeptDao deptdao;
    @Test
    public void test(){
        deptdao.addDept(new Dept());
    }
}

14. Spring AOP前奏

代理模式

  • 代理模式:比如房屋中介、房屋App等都是代理。

  • 我们(目标对象)与中介(代理对象)不能相互转换,因为是平级关系。

Spring笔记_第2张图片

为什么需要代理?

  • 需求:实现【加减乘除】计算器类

    • 在加减乘除方法中,添加日志功能【在计算之前,记录日志。在计算之后,显示结果。】
  • 实现后发现问题如下

    • 日志代码比较分散,可以将代码提取到日志类中。
    • 日志代码比较混乱,非核心代码与核心业务代码书写一处。
  • 总结:我们不期望在核心业务代码中书写日志代码

    • 此时:使用代理模式解决问题:

      先将日志代码横向提取到日志类中,再动态织入回到业务代码中

手动实现动态代理步骤

注意:代理对象与实现类【目标对象】是“兄弟”关系,不能相互转换

  • 实现方式

    • 基于接口实现动态代理: JDK动态代理
    • 基于继承实现动态代理: Cglib、Javassist动态代理
  • 基于接口实现动态代理的具体步骤:

    • 创建类(比如MyProxy)【为了创建代理对象的工具类】

    • 提供属性【目标对象,也就是实现类】

    • 提供方法(比如getProxyObject())【为目标对象创建代理对象】

      实现动态代理关键步骤

      • 一个类:Proxy
        • 概述:Proxy代理类的基类【类似Object】
        • 作用:newProxyInstance():创建代理对象
      • 一个接口:InvocationHandler
        • 概述:实现【动态织入效果】关键接口
        • 作用:invoke(),执行invoke()实现动态织入效果
    • 提供有参构造器【避免目标对象为空】

package com.atguigu.beforeaop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * MyProxy为获取代理对象的工具类。
 */
public class MyProxy {

    /**
     * 目标对象【目标客户】
     */
    private Object target;

    public MyProxy(Object target){
        this.target = target;
    }

    /**
     * 获取目标对象的,代理对象
     * @return
     */
    public Object getProxyObject(){
        Object proxyObj = null;

        /**
            第一个参数:类加载器【ClassLoader loader】,目标对象的类加载器
            第二个参数:目标对象实现接口:Class[] interfaces,目标对象实现所有接口
            第三个参数:InvocationHandler (动态织入的关键方法)
         */
        
        //创建类加载器
        ClassLoader classLoader = target.getClass().getClassLoader();
        //代理对象要知道目标对象实现了什么样的接口
        Class<?>[] interfaces = target.getClass().getInterfaces();
        //创建代理对象
        proxyObj = Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
            //执行invoke()实现动态织入效果
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //获取方法名【目标对象】
                String methodName = method.getName();
                //执行目标方法之前,添加日志
 MyLogging.beforeMethod(methodName,args);
                //获取目标对象的目标方法。
                Object rs = method.invoke(target, args);
                //执行目标方法之后,添加日志
                MyLogging.afterMethod(methodName,rs);
                return rs;
            }
        });
        return proxyObj;
    }
//不推荐以下方法:使用匿名内部类
//    class invocationImpl implements InvocationHandler{
//    }

}
@Test
    public void testBeforeAop(){

//        int add = calc.add(1, 2);
//        System.out.println("add = " + add);

        //目标对象
        Calc calc = new CalcImpl();
        //代理工具类
        MyProxy myProxy = new MyProxy(calc);
        //获取代理对象,与目标对象CalcImpl不能相互转换,两者是兄弟关系。
        Calc calcProxy = (Calc)myProxy.getProxyObject();
        //使用代理对象执行方法
//        int add = calcProxy.add(1, 2);
        int div = calcProxy.div(2, 1);
    }

15. Spring中的AOP(重要)

AspectJ框架【AOP框架】

  • AspectJ框架是Java社区中最完整最流行的AOP框架
  • 在Spring2.0以上的版本中,可以使用基于AspectJ注解或基于XML配置的AOP。

使用AspectJ步骤

  1. 导入相关jar包

    
    
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-aspectsartifactId>
        <version>5.3.1version>
    dependency>
    
  2. 配置文件

    • 开启组件扫描
    • 开启AspectJ注解支持
    
        <context:component-scan base-package="com.atguigu">context:component-scan>
    
        <aop:aspectj-autoproxy>aop:aspectj-autoproxy>
    
  3. 切面类(MyLogging类)添加注解

    @Component //标识当前类为一个组件。
    @Aspect     //将当前类标识为切面类(非核心业务提取类)。
    
  4. 在切面类中添加通知注解

    @Component 
    @Aspect     
    public class MyLogging {
        @Before(value = "execution(public int Spring.AOP.CalcImpl.add(int ,int))")
        public void beforeMethod(JoinPoint joinPoint){
            //获取当前方法名称
            String name = joinPoint.getSignature().getName();
            //获取方法形参
            Object[] args = joinPoint.getArgs();
            System.out.println("当前调用"+ name + "方法,参数是"+ Arrays.toString(args));
        }
    }
    
  5. 测试

    @Test
        public void test1(){
            ApplicationContext context =
                    new ClassPathXmlApplicationContext("applicationContext.xml");
            Calc calc = context.getBean("calc", Calc.class);
            //错误的,代理对象不能转换为目标对象【代理对象与目标对象是兄弟关系】
    	 //CalcImpl calc = context.getBean("calc", CalcImpl.class);
            calc.add(1,2);
        }
    

Spring中AOP概述

  • AOP:Aspect-Oriented Programming,面向切面编程【面向对象一种补充】
    • 优势:
      • 解决代码分散问题
      • 解决代码混乱问题
  • OOP:Object-Oriented Programming,面向对象编程

AOP更针对某一个方法,进行横向的扩展。比如添加日志等。

Spring笔记_第3张图片

Spring中AOP相关术语

  1. 横切关注点:非核心业务代码【日志】,称之为横切关注点
  2. 切面(Aspect):将横切关注点提取到类中,这个类称之为切面类
  3. 通知(Advice):将横切关注点提取到类中之后,横切关注点更名为:通知
  4. 目标(Target):目标对象,指的是需要被代理的对象【实现类(CalcImpl)】
  5. 代理(Proxy):代理对象可以理解为:中介
  6. 连接点(Joinpoint):通知方法需要指定通知位置,这个位置称之为:连接点【此时在通知之前】
  7. 切入点(pointcut):通知方法需要指定通知位置,这个位置称之为:切入点【此时在通知之后】

16. AspectJ详解(重要)

AspectJ切入点表达式

  • 语法:@Before(value = "execution(权限修饰符 返回值类型 包名.类名.方法名(参数类型))")

  • 通配符

    [ * ]:代表任意权限修饰符+返回值类型 和 任意包名+类名+方法名。

    [ … ] :代表任意参数类型及参数个数。

通配符常用的方法是:

    @Before(value = "execution(* Spring.AOP.CalcImpl.* (..))")

  • 重用切入点表达式

    @Pointcut("execution(* Spring.AOP.CalcImpl.* (..))")
        public void myPoint(){
        }
    
    @After("myPoint()")
        public void AfterMethod(JoinPoint joinPoint){
            System.out.println("后置");
        }
    

AspectJ中JoinPoint对象

  • JoinPont【切入点对象】

  • 作用:

    • 获取方法名称

      //获取方法签名【方法签名=方法名+参数列表】
      joinPoint.getSignature();
      //获取方法名称
      String methodName = joinPoint.getSignature().getName();
      
    • 获取参数

      Object[] args = joinPoint.getArgs();
      

AspectJ中通知

指定方法:切入点表达式设置位置

  • 前置通知 @Before

    • 执行时机:指定方法执行之前【如目标方法中有异常,会执行】
  • 后置通知 @After

    • 执行时机:指定方法所有通知执行之后【如目标方法中有异常,会执行】
  • 返回通知 @AfterReturning

    • 执行时机:指定方法返回结果时执行,【如目标方法中有异常,不执行】

    • 注意事项:@AfterReturning中returning属性与入参中参数名一致

    示例代码:

     @AfterReturning(value = "myPoint()",returning = "rs")
        public void AfterReturning(JoinPoint joinPoint,Object rs){
            //获取当前方法名称
            String name = joinPoint.getSignature().getName();
            //获取方法形参
            Object[] args = joinPoint.getArgs();
            System.out.println("【返回通知】当前调用"+ name + "方法,返回结果是"+ rs);
        }
    
  • 异常通知 @AfterThrowing

    • 执行时机:指定方法出现异常时执行,【如目标方法中没出现异常,不执行】

    • 注意事项:@AfterThrowing中throwing属性与入参中参数名一致

    示例代码

    @AfterThrowing(value = "myPoint()",throwing = "ex")
        public void AfterThrowing(JoinPoint joinPoint , Exception ex){
            //获取当前方法名称
            String name = joinPoint.getSignature().getName();
            //获取方法形参
            Object[] args = joinPoint.getArgs();
            System.out.println("【异常通知】当前调用"+ name + "方法,出现的异常是"+ ex);
        }
    

    总结

    • 有异常:前置通知=》异常通知=》后置通知
    • 无异常:前置通知=》返回通知=》后置通知
  • 环绕通知 @Around

    该通知可以整合前四个通知。

    注意:

    • 参数中必须使用ProceedingJoinPoint,目的是使用proceed()方法
    • 环绕通知必须将目标对象的方法返回结果作为返回值。

    示例代码

    @Around("myPoint()")
        public Object AroundMethod(ProceedingJoinPoint pjp){
            String name = pjp.getSignature().getName();
            Object[] args = pjp.getArgs();
            //定义方法返回值
            Object rs = null;
            try {
                //前置通知
                System.out.println("【前置通知】当前调用"+ name + "方法,参数是"+ Arrays.toString(args));
                //触发目标对象的方法
                rs = pjp.proceed();
                //返回通知
                System.out.println("【返回通知】当前调用"+ name + "方法,返回结果是"+ rs);
            } catch (Throwable ex) {
                //异常通知
                System.out.println("【异常通知】当前调用"+ name + "方法,出现的异常是"+ ex);
            }finally {
                //后置通知
                System.out.println("【后置通知】当前调用"+ name + "方法,参数是"+ Arrays.toString(args));
            }
            return rs;
        }
    

定义切面优先级

两个切面类中如果都存在前置等切面,则可以使用@Order来定义切面类的优先级。

@Order(value = index),其中index值越小,优先级越高

@Component
@Aspect
@Order(value = 1)
public class MyValidate {}
============================
@Component      
@Aspect         
@Order(2)
public class MyLogging {}

基于XML方式配置AOP

在一些老的项目中,可能没有使用基于注解配置,还在使用XML的方式进行配置切面类。


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    
    <bean id="calculator" class="com.atguigu.spring.aop.xml.CalculatorImpl">bean>

    
    <bean id="loggingAspect" class="com.atguigu.spring.aop.xml.LoggingAspect">bean>

    
    <aop:config>
        
        <aop:pointcut id="pointCut"
                      expression="execution(* com.atguigu.spring.aop.xml.Calculator.*(..))"/>
        
        <aop:aspect ref="loggingAspect">
            
            <aop:before method="beforeAdvice" pointcut-ref="pointCut">aop:before>
            
            <aop:after-returning method="returningAdvice" pointcut-ref="pointCut" returning="result">aop:after-returning>
            
            <aop:after-throwing method="throwingAdvice" pointcut-ref="pointCut" throwing="e">aop:after-throwing>
            
            <aop:after method="afterAdvice" pointcut-ref="pointCut">aop:after>
            
            <aop:around method="aroundAdvice" pointcut-ref="pointCut">aop:around>
        aop:aspect>
    aop:config>
beans>

17. Spring中的JdbcTemplate

JdbcTemplate简介

Spring提供的jdbcTemplate是一个小型的持久化层框架,可以简化jdbc代码。

JdbcTemplate使用步骤

  1. 导入jar包
//重要的是导入该jar包。
<dependency>
  <groupId>org.springframeworkgroupId>
            <artifactId>spring-ormartifactId>
            <version>5.3.1version>
dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-contextartifactId>
            <version>5.3.1version>
        dependency>
        
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.12version>
            <scope>testscope>
        dependency>
        
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druidartifactId>
            <version>1.1.10version>
        dependency>
        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>8.0.28version>
        dependency>
  1. 编写配置文件

    • db.properties:设置连接数据库属性

    • applicationContext.xml:spring配置文件

      • 加载外部属性文件【db.properties】
      • 装配数据源【DruidDataSources】
      • 装配持久化层框架【JdbcTemplate】
    • 示例代码

      db.properties

      db.driver=com.mysql.cj.jdbc.Driver
      db.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8
      db.username=root
      db.password=root
      

      applicationContext_JdbcTemplate.xml

      
          <context:property-placeholder location="classpath:db.properties">context:property-placeholder>
          
          <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
              <property name="driverClassName" value="${db.driver}">property>
              <property name="url" value="${db.url}">property>
              <property name="password" value="${db.password}">property>
              <property name="username" value="${db.username}">property>
          bean>
          
          <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
              <property name="dataSource" ref="druidDataSource">property>
          bean>
      

    装配自己内部的bean可以使用注解,但是第三方的bean要使用xml去装配,比如jdbcTemplate。因为装配类需要在类上面去写注解。

  2. 使用核心类库(API调用)

JdbcTemplate常用API

JdbcTemplate默认:自动提交事务,不像Mybatis一样手动提交事务。

  • jdbcTemplate.update(String sql,Object… args):通用的增删改方法
  • jdbcTemplate.batchUpdate(String sql,List args):通用批处理增删改方法
  • jdbcTemplate.queryForObject(String sql,Class clazz,Object… args):查询单个数值
    • String sql = “select count(1) from tbl_xxx”;
  • jdbcTemplate.queryForObject(String sql,RowMapper rm,Object… args):查询单个对象
    • String sql = “select col1,col2… from tbl_xxx”;
  • jdbcTemplate.query(String sql,RowMapper rm,Obejct… args):查询多个对象
ApplicationContext context =
                new ClassPathXmlApplicationContext("applicationContext_Jdbc.xml");
        JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
        String sql = "insert into tbl_employee(last_name,email,salary,dept_id) values(?,?,?,?)";
        List<Object[]> list = new ArrayList<>();
        list.add(new Object[]{"qq0","qq.com",123,123});
        list.add(new Object[]{"qq1","qq.com",123,123});
        list.add(new Object[]{"qq2","qq.com",123,123});
        list.add(new Object[]{"qq3","qq.com",123,123});

        //演示批量添加操作。
        jdbcTemplate.batchUpdate(sql,list);

	//查询单个数值
        String sql = "select count(1) from tbl_employee";
        Integer integer = jdbcTemplate.queryForObject(sql, Integer.class);
        System.out.println("integer = " + integer);

        //查询单个对象
        String sql = "select id,last_name,email,salary from tbl_employee where id = ?";
        //创建RowMapper
        RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class);
        Employee employee = jdbcTemplate.queryForObject(sql, rowMapper, 1);
        System.out.println("employee = " + employee);

        //查询多个对象
        String sql = "select id,last_name,email,salary from tbl_employee";
        RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class);
        List<Employee> employeeList = jdbcTemplate.query(sql, rowMapper);

使用JdbcTemplate搭建Service&Dao层

  • Service层依赖Dao层

  • Dao层依赖JdbcTemplate

    示例代码:

    //text:
    /**
     * @Author 不知名网友鑫
     * @Date 2022/10/2
     **/
    @ContextConfiguration(locations = "classpath:applicationContext_Jdbc.xml")
    @RunWith(SpringJUnit4ClassRunner.class)
    public class TestDaoService {
        @Autowired
        DeptService deptService;
    
        @Test
        public void test(){
            List<Dept> depts = deptService.selectDeptS();
            System.out.println("depts = " + depts);
        }
    }
    //DeptService
    /**
     * @Author 不知名网友鑫
     * @Date 2022/10/2
     **/
    @Service("deptService")
    public class DeptServiceImpl implements DeptService{
        @Autowired
        DeptDao deptDao;
        @Override
        public List<Dept> selectDeptS() {
            List<Dept> depts = deptDao.selectDept();
            return depts;
        }
    }
    //DeptDao
    /**
     * @Author 不知名网友鑫
     * @Date 2022/10/2
     **/
    @Repository("deptDao")
    public class DeptDaoImpl implements DeptDao{
        @Autowired
        JdbcTemplate jdbcTemplate;
    
        @Override
        public List<Dept> selectDept() {
            String sql = "select dept_id,dept_name from tbl_dept";
            RowMapper<Dept> rowMapper = new BeanPropertyRowMapper<>(Dept.class);
            List<Dept> query = jdbcTemplate.query(sql, rowMapper);
            return query;
        }
    }
    

18. Spring声明式事务管理(重要)

回顾事务:

  1. 事务四大特征【ACID】
    • 原子性:事务是最小单元,不可分割。
    • 一致性:事务要么同时提交,要么同时回滚。
    • 隔离性:事务与事务彼此隔离,不能有关联。多个事务并发执行不会互相干扰。
    • 持久性:事务对数据1的修改应该被写在持久化容器中。
  2. 事务三种行为
    • 开启事务:connection.setAutoCommit(fasle);
    • 提交事务:connection.commit();
    • 回滚事务:connection.rollback();

Spring中事务管理

案例:去结账。

具体步骤:1. 生成订单 2. 生成订单详情 3. 更改库存&销量 4. 清空购物车

如果其中一步出错,我们希望整个流程一起回滚,而不是提交部分,此时我们需要进行事务管理。

  • 编程式事务管理【传统事务管理】

    1. 获取数据库连接Connection对象
    2. 取消事务自动提交【开启事务】
    3. 执行操作【业务代码】
    4. 正常完成操作时手动提交事务
    5. 失败完成操作时回滚事务
    6. 关闭相关资源

    问题:事务管理的代码与核心业务代码相耦合。

    • 事务管理代码分散
    • 事务管理代码混乱
  • 声明式事务管理【使用AOP管理事务】

    方式:先横向提取事务管理代码,再动态织入。

使用声明式事务管理

不用事务管理代码,发现:同一个业务中,会出现局部成功及局部失败的现象【不正常】

  1. 导入Jar包,添加AspectJ的jar包。

    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-aspectsartifactId>
        <version>5.3.1version>
    dependency>
    
  2. 编写配置文件

    • 配置事务管理器
    • 开启事务注解支持
    
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="druidDataSource">property>
        bean>
        
        <tx:annotation-driven>tx:annotation-driven>
    
  3. 在需要事务管理的业务方法上,添加注解**@Transactional**

    @Transactional(propagation = Propagation.REQUIRES_NEW)
        public void purchase(String username, String isbn) {
            //查询book价格
            Integer price = bookshopDao.findBookPriceByIsbn(isbn);
            //修改库存
            bookshopDao.updateBookStock(isbn);
            //修改余额
            bookshopDao.updateUserAccount(username, price);
        }
    

Spring声明式事务管理属性

@Transactional注解属性

事务传播行为【Propagation】

  • 当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。

  • Propagation常用属性:

    REQUIRED传播行为:

    当 bookService 的 purchase()方法被另一个事务方法 checkout()调用时,它默认会在现有的事务内运行。因此在 checkout()方法的开始和终止边界内只有一个事务。

    如果余额不足,就一本书也买不了。

Spring笔记_第4张图片

REQUIRES_NEW传播行为:

表示该方法必须启动一个新事务,并在自己的事务内运行。如果有事务在运行,就应该先挂起它。

Spring笔记_第5张图片

事务隔离级别【Isolation】

假设现在有两个并发的事务:Transaction01和Transaction02。

脏读【读取到了未提交的事务】:

①Transaction01 将某条记录的 AGE 值从 20 修改为 30,但是没有提交。

②Transaction02 读取了 Transaction01 更新后的值:30。

③随后Transaction01 回滚,AGE 值恢复到了 20。

④Transaction02 读取到的 30 就是一个无效的值。

不可重复读 【多次从一个字段中读取到的数据不相同】

①Transaction01 读取了 AGE 值为 20。

②Transaction02 将 AGE 值修改为 30,并且提交数据。

③Transaction01 再次读取 AGE 值为 30,和第一次读取不一致。

幻读【多次从一个表中读取的行不相同】

①Transaction01 读取了 STUDENT 表中的一部分数据。

②Transaction02 向 STUDENT 表中插入了新的行。

③Transaction01 读取了 STUDENT 表时,多出了一些行。

数据库系统必须具有隔离并发运行各个事务的能力,使它们不会互相影响。

隔离级别越高,数据一致性就越好,但并发性越弱。一个事务与其他事务之间的隔离等级【1,2,4,8】

隔离等级

  • 读未提交【1】:READ UNCOMMITED
    • 事务1可以读取事务2未提交的数据。存在问题:脏读。
  • 读已提交【2】:READ COMMITTED
    • 存在问题:可能出现不可重复读
  • 可重读【4】:REPEATABLE READ(字段锁)
    • 存在问题:可能出现幻读
  • 串行化【8】SERIALIZABLE(表的锁)

Spring笔记_第6张图片

其他属性

  • 事务超时【timeout】

类型:int,单位:second。

默认值:-1【未设置强制回滚】

设置超时时间,到达指定时间后,会强制事务回滚。

  • 事务只读【readonly】

    一般事务方法中,只有查询操作时,才将事务设置为只读。

  • 事务回滚【rollbackFor/noRollbackFor】

    遇见回滚/不回滚的异常类。

示例代码:

//当前事务传播行为是自己属于一个事务。
    @Transactional(propagation = Propagation.REQUIRES_NEW,
                    timeout = 1,
                    noRollbackFor = ArithmeticException.class)
    public void purchase(String username, String isbn) {
        //查询book价格
        Integer price = bookshopDao.findBookPriceByIsbn(isbn);

        //测试事务超时
//        try {
//            Thread.sleep(5000);
//        } catch (InterruptedException e) {
//            throw new RuntimeException(e);
//        }

        //修改库存
        bookshopDao.updateBookStock(isbn);
        //修改余额
        bookshopDao.updateUserAccount(username, price);

        //演示遇到算数异常时,事务不回滚
        int i = 1/0;
    }

基于XML方式配置声明式事务管理

 
<aop:config>
<aop:pointcut expression="execution(* com.atguigu.tx.component.service.BookShopServiceImpl.purchase(..))" id="txPointCut"/>  
<aop:advisor advice-ref="myTx" pointcut-ref="txPointCut"/> 
aop:config> 
 
<tx:advice id="myTx" transaction-manager="transactionManager"> 
    <tx:attributes> 
     
    <tx:method name="find*" read-only="true"/> 
    <tx:method name="get*" read-only="true"/> 
    <tx:method name="purchase" 
               isolation="READ_COMMITTED" 
               no-rollback-for="java.lang.ArithmeticException,java.lang.NullPointerException" propagation="REQUIRES_NEW" 
               read-only="false" 
               timeout="10"/> 
    tx:attributes> 
tx:advice>

19. Spring5新特性

添加了新注解

  • 以@Nullable 为例

    位置:可以书写在方法&属性&参数前面。

    作用:表示当前方法或属性可以为空,消除了空指针异常。

Spring5整合Log4j2

  • 导入jar包

    
    <dependency>
        <groupId>org.apache.logging.log4jgroupId>
        <artifactId>log4j-slf4j-implartifactId>
        <version>2.11.2version>
        <scope>testscope>
    dependency>
    
  • 编写配置文件(不需要自己编写)

    • log4j2.xml

      日志级别以及优先级排序:OFF > FATAL > WHAR > INFO > DEBUG > TRACE > ALL

      高级别会打印低级别的内容。

Spring5整合Junit5

  • 导入jar包【注意:将Junit4的jar包删除】

    
    <dependency>
        <groupId>org.junit.jupitergroupId>
        <artifactId>junit-jupiter-apiartifactId>
        <version>5.7.2version>
        <scope>testscope>
    dependency>
    
    <dependency>
                <groupId>org.springframeworkgroupId>
                <artifactId>spring-testartifactId>
                <version>5.3.1version>
            dependency>
    
  • 使用注解进行整合。

    //方式一:
    @ContextConfiguration(locations = "classpath:applicationContext.xml")
    @ExtendWith(SpringExtension.class)
    //方式二:
    @SpringJUnitConfig(locations = "classpath:applicationContext.xml")
    

你可能感兴趣的:(个人笔记,spring,笔记,junit)