0%

Spring一览

Spring概念

  • Spring是轻量级开源JavaEE框架
  • Spring可以解决企业应用开发的复杂性
  • Spring两大核心部分:IOC和AOP
    • IOC:控制反转,将对象创建过程交给Spring管理
    • AOP:面向切面,不修改源代码进行功能增强
  • Spring特点
    • 方便解耦,简化开发
    • 支持AOP
    • 声明式事务
    • 方便程序测试
    • 方便集成其它框架
    • 降低JavaEE API的使用难度
image.png
image.png

Spring简单使用

  1. 最基本的使用,需要导入的依赖,maven对应如下,其中包含了spring-aop、spring-beans、spring-core、spring-expression

    1
    2
    3
    4
    5
    6
    <dependencies>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    </dependency>
    </dependencies>

  2. 编辑配置文件,文件名为任意值,如applicationConfig.xml

    1
    2
    3
    4
    5
    6
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="userService" class="com.zephon.test.UserService"/>
    </beans>

  3. 读取配置文件并进行测试

    1
    2
    3
    4
    5
    6
    7
    8
    @Test
    public void test(){
    // 1. 加载Spring配置文件
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationConfig.xml");
    // 2. 获取配置创建的对象
    UserService userService = (UserService) context.getBean("userService");
    userService.add();
    }
    # IOC

IOC的作用与原理

作用:

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

底层原理、BeanFactory:

  • xml解析+工厂模式+反射
  • BeanFactory:IOC容器的基本实现,是Spring内部的使用接口,不提供开发人员使用
    • 加载配置文件时创建对象(版本Spring 5.3.22)
  • ApplicationContext:BeanFactory接口的子接口,提供更多理强大的功能,一般由开发人员使用
    • 加载配置文件时就会将配置文件中的对象创建(版本Spring 5.3.22)
  • 详细可见之前的自己简单完成依赖注入功能

IOC管理Bean(基于XML)

创建对象

  • 在Spring的xml配置文件中使用标签并使用对应属性,常用的一些属性:

    • id:唯一标识
    • class:对象对应的全限定类名
    • name:与id类似,但可以使用特殊符号(使用较少)
  • 默认会执行无参构造器创建对象

    1
    <bean id="userService" class="com.zephon.test.UserService"/>
    ### Spring注入属性

  • DI:依赖注入,就是注入属性,注入方法包括:

    • 使用set方法注入,对应的类中必须实现对应属性的set方法,然后在xml配置文件中使用标签的子标签,内部属性包括:
      • name:属性名
      • value:属性值
      • 子标签:
        • :表示给属性设置空值
        • :可用来设置特殊符号,如<>就需要写成<[CDATA[<>]]>
          1
          2
          3
          4
          <bean id="user" class="com.zephon.test.User">
          <property name="username" value="root"/>
          <property name="password" value="123456"/>
          </bean>
          然后再使用测试方法测试获取Bean即可
    • 使用有参数构造器注入,使用标签下的子标签
      • name:属性名
      • value:属性值
        1
        2
        3
        4
        <bean id="user" class="com.zephon.test.User">
        <constructor-arg name="username" value="root"/>
        <constructor-arg name="password" value="123456"/>
        </bean>

一种不常用简化方式:p名称空间注入

  1. 添加p名称空间,在beans标签属性中添加:xmlns:p="http://www.springframework.org/schema/p"
  2. 进行属性注入:
    1
    2
    3
    4
    5
    6
    7
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="user" class="com.zephon.test.User" p:username="root" p:password="123456"/>
    </beans>

注入其它类型属性

  1. 注入外部bean

例:service类调用dao类,必须在Service类中写有Dao对象对应的set方法

1
2
3
4
<bean id="userService" class="com.zephon.test.UserService">
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" class="com.zephon.test.dao.UserDaoImpl"/>

  1. 注入内部bean和级联赋值
    1. 一对多关系:如部门和员工
      1
      2
      3
      4
      5
      6
      7
      8
      9
      <bean id="employee" class="com.zephon.test.Employee">
      <property name="ename" value="张三"/>
      <property name="dept">
      <!-- 内部bean -->
      <bean id="dept" class="com.zephon.test.Dept">
      <property name="name" value="策划部"/>
      </bean>
      </property>
      </bean>
  2. 级联赋值
    1. 第一种写法

      1
      2
      3
      4
      5
      6
      7
      8
      <bean id="employee" class="com.zephon.test.Employee">
      <property name="ename" value="张三"/>
      <!-- 级联赋值 -->
      <property name="dept" ref="dept"/>
      </bean>
      <bean id="dept" class="com.zephon.test.Dept">
      <property name="name" value="财务部"/>
      </bean>

    2. 第二种写法

      1
      2
      3
      4
      5
      6
      7
      8
      <bean id="employee" class="com.zephon.test.Employee">
      <property name="ename" value="张三"/>
      <!-- 级联赋值 -->
      <property name="dept" ref="dept"/>
      <!-- 需要在Employee中写dept的get方法 -->
      <property name="dept.name" value="财务部"/>
      </bean>
      <bean id="dept" class="com.zephon.test.Dept"/>

注入集合类型属性

  1. 注入数组

    1
    2
    3
    4
    5
    6
    7
    8
    <bean id="stu" class="com.zephon.test.Student">
    <property name="courses">
    <array>
    <value>语文</value>
    <value>数学</value>
    </array>
    </property>
    </bean>

  2. 注入List

    1
    2
    3
    4
    5
    6
    <property name="list">
    <list>
    <value>1</value>
    <value>2</value>
    </list>
    </property>

  3. 注入Map

    1
    2
    3
    4
    5
    6
    <property name="map">
    <map>
    <entry key="JAVA" value="java"/>
    <entry key="PHP" value="php"/>
    </map>
    </property>

  4. 集合中的数据是对象情况,则将上述value换成ref,然后与之前的bean一样处理即可

    1
    2
    3
    4
    5
    6
    7
    <property name="list">
    <list>
    <ref bean="course1"/>
    </list>
    </property>
    ...
    <bean id="course1" class="..."/>

  5. 提取公共部分

    1. 在配置文件中引入名称空间util:xmlns:util="http://www.springframework.org/schema/util"
    2. 修改xsi:schemaLocation,添加utils对应部分
    3. 使用utils标签完成提取
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      <?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:util="http://www.springframework.org/schema/util"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
      <util:list id="aList">
      <!-- 如果是对象则用ref -->
      <value>1</value>
      <value>2</value>
      </util:list>

      <bean id="student" class="com.zephon.test.Student">
      <property name="list" ref="aList"/>
      </bean>
      </beans>
      ## IOC管理Bean(基于注解)

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

创建对象相关注解

四个常用注解功能一样,都可以用来创建bean实例

  1. @Component:普通注解
    • value:和XML中的id一样,表示bean的唯一标识,可省略不写,默认值是类名首字母变小写后的结果
  2. @Service:通常用在业务层
  3. @Controller:通常用在控制层
  4. @Repository:通常用在持久层

使用方法:

  1. 如果没有aop依赖需要先引入aop依赖

  2. 开启组件扫描功能(同样需要引入context名称空间)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 开启组件扫描 -->
    <!-- 如果需要扫描多个包,多个包之间用逗号隔开;或者直接扫描包上层目录 -->
    <context:component-scan base-package="com.zephon.test.dao,com.zephon.test.service"/>

    </beans>

  3. 创建类并在对应的类上面添加注解

    1
    2
    3
    4
    @Repository(value="userService")
    public class UserService {
    ...
    }

  • 上述配置文件中,还可以自定义包扫描内容,如:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <!-- use-default-filters:是否使用默认filter -->
    <context:component-scan base-package="com.zephon.test" use-default-filters="false">
    <!-- 表示只扫描注解为Controller的类 -->
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!-- 或 -->
    <context:component-scan base-package="com.zephon.test" use-default-filters="false">
    <!-- 表示排除注解为Controller的类,扫描其它的类 -->
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

属性注入相关的注解

  1. @Autowired:根据属性类型自动注入(装配)
  2. @Qualifier:根据属性名称自动注入(装配),需要和@Autowired一起使用(有时一个接口对应多个实现类,就需要再加上对应的名称)
    • value:指定为创建对象是使用的id值
  3. @Resource:可以根据类型注入也可以根据名称注入
    • name:设定值后则根据设定的名称进行注入,否则根据类型进行注入
  4. @Value:注入普通类型属性
    • value:表示属性对应的值,如@Value(value = "root")表示给属性赋值为root

使用流程:

  1. 编写类,如UserService和UserDao
  2. 在被使用的各个类上加上注解,如在UserDao上加@Repository,在UserService上加@Service
  3. 在Service类上添加对应的属性并在属性上加注解,如@Autowired,不需要写对应的set方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Service
    public class UserService {
    @Autowired
    UserDao userDao;

    public void add(){
    userDao.add();
    }
    }
    或:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Service
    public class UserService {
    @Autowired
    @Qualifier(value = "userDao1") // -->对应的是UserDaoImpl上的@Repository(value = "userDao1")
    UserDao userDao;

    public void add(){
    userDao.add();
    }
    }

完全注解开发

  1. 创建配置类,替代XML配置文件

    1
    2
    3
    4
    @Configuration
    @ComponentScan(basePackages = {"com.zephon.test"})
    public class SpringConfig {
    }

  2. 编写测试类

    1
    2
    3
    ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
    UserService userService = context.getBean("userService", UserService.class);
    userService.add();
    ## Bean管理

FactoryBean

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

如:

  1. 创建一个类实现FactoryBean接口,作为工程Bean
  2. 实现接口里面的方法,在里面可以定义返回的Bean类型
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class MyBean implements FactoryBean<User> {
    // 定义返回的Bean
    @Override
    public User getObject() throws Exception {
    User user = new User();
    user.setUsername("root");
    return user;
    }
    // 定义返回的Bean类型
    @Override
    public Class<?> getObjectType() {
    return User.class;
    }
    }
    1
    <bean id="myBean" class="com.zephon.test.MyBean"/>
    1
    2
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationConfig.xml");
    User user = context.getBean("myBean", User.class);

Bean的作用域

  • 在Spring中可以设置创建的bean实例是单例还是多例

    1
    2
    <!-- singleton:单实例, prototype:多实例 -->
    <bean id="user" class="com.zephon.test.User" scope="prototype"/>

  • Spring中默认情况下创建的bean是单实例对象

  • singleton和prototype区别:

    • singleton表示单实例,prototype表示多实例
    • 设置scope时为singleton时,加载Spring配置文件时就会创建单实例对象;而设置为prototype则是在调用getBean()方法是创建对象
  • 如果是在web工程则还可设置request和session ### Bean的生命周期

  1. 通过构造器创建Bean实例(无参构造)
  2. 为Bean的属性赋值(调用set方法)
  3. 将Bean实例传递给Bean后置处理器的postProcessBeforeInitialization方法(需要自定义类,实现BeanPostProcessor)
  4. 调用Bean的初始化方法(需要进行配置初始化的方法)
  5. 将Bean实例传递给Bean后置处理器postProcessAfterInitialization方法
  6. 使用bean(可获取状态)
  7. 当容器关闭时,调用bean的销毁方法(需要进行配置销毁的方法)(((ClassPathXmlApplicationContext)context).close();)
    1
    2
    3
    4
    5
    <!-- 需要在User类中自己定义initMethod方法和destroyMethod方法 -->
    <bean id="user" class="com.zephon.test.User" init-method="initMethod" destroy-method="destroyMethod"/>

    <!-- 配置所有Bean的后置处理器 -->
    <bean id="myBeanPost" class="com.zephon.test.Post"/>
    > 测试输出: > 构造器调用 > 调用set方法 > 初始化前的后置处理器 > 初始化方法 > 初始化后的后置处理器 > 使用bean > 销毁方法

XML自动装配(使用较少)

  • 根据指定装配规则(属性名或属性类型),Spring自动将匹配的属性值进行注入,如:
    1
    2
    3
    <!-- 也可以根据类型注入 byType -->
    <bean id="user" class="com.zephon.test.User" autowire="byName"/>
    <bean id="book" class="com.zephon.test.Book"/>

引入外部的属性文件

  1. 创建外部属性文件,properties格式文件(可以写数据库相关信息等),如:

    1
    2
    3
    4
    prop.driverClass=com.mysql.cj.jdbc.Driver
    prop.url=jdbc:mysql://localhost:3306/userDB
    prop.userName=root
    prop.password=123456

  2. 引入到Spring的xml配置文件

    1. 引入context名称空间,xmlns:context="http://www.springframework.org/schema/context"
    2. 修改xsi:schemaLoaction
    3. 导入properties文件
    4. 配置属性
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:context="http://www.springframework.org/schema/context"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
      <context:property-placeholder location="classpath:jdbc.properties"/>
      <bean id="..." class="...">
      <property name="driverClassName" value="${prop.driverClass}"/>
      ......
      </bean>
      </beans>

AOP

概念:面向切面编程(Aspect Oriented Programming),可能用来对业务逻辑的各个部分进行隔离,从而使得业务逻辑各个部分之间的耦合度降低,提高程序的可重用性,提高开发效率。

AOP底层原理:使用动态代理

  1. 有接口的情况,使用JDK动态代理(源于接口实现)
    1. 被代理类实现了某个接口A
    2. 创建接口A实现类代理对象,然后通过代理对象增强被代理类的功能
    3. 具体实现:使用Proxy类中对应的方法
      1
      2
      3
      4
      5
      6
      7
      8
      Object obj = new Object(); // 被代理类
      Object proxyInstance = Proxy.newProxyInstance(obj.getClass().getClassLoader(), new Class[]{A.class}, new InvocationHandler() {
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      System.out.println("增强方法");
      return method.invoke(obj, args);
      }
      }); // 代理实例
  2. 没有接口的情况,使用CGLIB动态代理(源于继承)
    1. 被代理类为B
    2. 创建类B的子类的代理对象,增强被代理类的功能

AOP常用词

  • 连接点:被增强的部分(方法),类中哪些方法能被增强,这些方法就是连接点
  • 切入点:实际被增强的方法被称为切入点
  • 通知(增强):实际增强的逻辑部分称为通知(原代码之外自己新增的部分)
    • 通知有多种类型:
      • 前置通知:在方法执行之前执行
      • 后置通知:在方法执行之后执行
      • 环绕通知:在方法执行前后均执行
      • 异常通知:在方法抛出异常时执行
      • 最终通知:无论如何都执行
  • 切面:将通知应用到切入点的过程称为切面

AOP使用

  • Spring框架一般基于AspectJ实现AOP操作

    • AspectJ本身不是Spring组成部分,而是独立的AOP框架,只是一般为方便起见将AspectJ和Spring框架一起使用进行AOP操作
  • 导入包

    1
    2
    3
    4
    5
    6
    <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>2.7.2</version>
    </dependency>

  • 切入点表达式:

    • 作用:表示对哪个类的哪个方法进行增强
    • 语句结构:execution([权限修饰符] [返回类型] [全限定类名].[方法名称] ([参数列表]))
      • 可使用*****表示任意类型
      • 方法返回类型可省略
      • 参数列表可以用..表示 ### 基于XML配置文件实现
  1. 创建被增强类和增强类和方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // User类文件
    public class User {
    public void add(){
    System.out.println("被增强方法");
    }
    }

    // UserProxy类文件
    public class UserProxy {
    public void before(){
    System.out.println("before");
    }
    }

  2. 在Spring配置文件中创建两个类对象()

  3. 在Spring配置文件中配置切入点

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <?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="user" class="com.zephon.test.aop.User"/>
    <bean id="userProxy" class="com.zephon.test.aop.UserProxy"/>

    <!-- 配置AOP增强 -->
    <aop:config>
    <!-- 切入点 -->
    <aop:pointcut id="point" expression="execution(* com.zephon.test.aop.User.add(..))"/>
    <!-- 配置切面 -->
    <aop:aspect ref="userProxy">
    <!-- 增强作用的方法 -->
    <aop:before method="before" pointcut-ref="point"/>
    </aop:aspect>
    </aop:config>
    </beans>
    ### 基于注解方式实现(常用)

  4. 创建被增强类,在类里定义方法

    1
    2
    3
    4
    5
    6
    @Component
    public class User {
    public void add(){
    System.out.println("被增强方法");
    }
    }

  5. 创建增强类,编写增强逻辑

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    @Component
    @Aspect
    // 表示增强类优先级,当有多个增强类时,
    // 按优先级执行,数字越小,优先级越高
    @Order(1)
    public class UserProxy {

    // 相同切入点抽取
    @Pointcut(value = "execution(* com.zephon.test.aop.User.add(..))")
    public void point(){

    }

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

    @After(value = "point()")
    public void after(){
    System.out.println("after");
    }
    @AfterReturning(value = "execution(* com.zephon.test.aop.User.add(..))")
    public void afterReturning(){
    System.out.println("after returning");
    }
    @AfterThrowing(value = "execution(* com.zephon.test.aop.User.add(..))")
    public void afterThrowing(){
    System.out.println("after throwing");
    }

    @Around(value = "execution(* com.zephon.test.aop.User.add(..))")
    public void around(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println("环绕之前");
    pjp.proceed();
    System.out.println("环绕之后");
    }
    }

  6. 在增强类中使用不同的方法代表不同的通知类型

  7. 进行通知的配置

    1. 在Spring配置文件中开启注解扫描或通过配置文件开启注解扫描
    2. 使用注解创建对象(即在两个类上添加注解@Component或其它)
    3. 在增强类上添加注解@Aspect
    4. 在Spring配置文件中开启生成代理对象(需要引入aop名称空间)
    5. 配置不同类型的通知
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      <?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.zephon.test.aop"/>
      <!-- 开启AspectJ生成代理对象 -->
      <aop:aspectj-autoproxy/>
      </beans>

JDBCTemplate

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

导包

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${srping.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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.zephon.test"/>

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

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

一个例子:

1
2
3
4
5
6
7
8
9
10
11
@Repository
public class UserDao {
@Autowired
JdbcTemplate jdbcTemplate;

public void addUser(User user) {
String sql = "insert into user (name,password,address,phone) values(?,?,?,?)";
int update = jdbcTemplate.update(sql, user.getName(), user.getPassword(), user.getAddress(), user.getPhone());
System.out.println(update);
}
}
## 增删改查

  • 增:
    • update(String sql, Object... args)
      • sql:SQL语句
      • args:可变参数,设置SQL语句值
      • 返回值:影响的行数
    • batchUpdate(String sql, List<Object[]> batchArgs):批量添加
  • 删:
    • update(String sql, Object... args)
      • sql:SQL语句
      • args:可变参数,设置SQL语句值
      • 返回值:影响的行数
    • batchUpdate(String sql, List<Object[]> batchArgs):批量删除
  • 改:
    • update(String sql, Object... args)
      • sql:SQL语句
      • args:可变参数,设置SQL语句值
      • 返回值:影响的行数
    • batchUpdate(String sql, List<Object[]> batchArgs):批量修改
  • 查询:
    • queryForObject(String sql, Class requiredType)
      • sql:SQL语句
      • requiredType:返回类型
        • 为Integer.class时
          • 返回某一个值(如查询数量(select count(*)))
    • queryForObject(String sql, RowMapper rowMapper, Object... args)
      • sql:SQL语句
      • rowMapper:RowMapper接口,针对SQL查询返回不同类型的数据,RowMapper接口里面实现完成数据封装,如:new BeanPropertyRowMapper(User.class)
      • args:SQL语句的填充参数
      • 返回指定类型的单个对象
    • query(String sql, RowMapper rowMapper, Object... args)
      • sql:SQL语句
      • rowMapper:针对不同类型进行封装
      • args:SQL语句的填充参数
      • 返回List类型的集合数据

事务管理

  • Spring使用的是声明式事务管理,底层使用AOP原理

Spring事务管理API

  • 接口PlatformTransactionManager:表示事务管理器,这个接口针对不同的框架提供不同的实现类,如JDBC使用的DataSourceTransactionManager

声明式事务管理使用

基于注解方式

  1. 在Spring配置文件中配置事务管理器
  2. 在Spring配置文件中开启事务注解(需要先引入tx名称空间)
  3. 在Service类其中的需要的方法上面添加事务注解@Transactional,表示这个类中所有方法或指定的方法添加事务,包含可选参数:
    • propagation:事务传播行为
      • REQUIRED:如果有事务在运行,当前方法就在这个事务内运行,否则就启动一个新的事务,并在自己的事务内运行
      • REQUIRED_NEW:当前的方法必须启动新事务,并在它自己的事务内运行,如果有事务正在运行,应该将它挂起
      • SUPPORTS:如果有事务在运行,当前的方法就在这个事务内运行,否则它可以不运行在事务中
      • NOT_SUPPORTES:当前方法不应该运行在事务中,如果有运行的事务,将它挂起
      • MANDATORY:当前的方法必须运行在事务内部,如果没有正在运行的事务,就抛出异常
      • NEVER:当前的方法不应该运行在事务中,如果有运行的事务,就抛出异常
      • NESTED:如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则就启动一个新的事务,并在它自己的事务内运行
    • isolation:事务隔离级别
      • READ UNCOMMITTED:读未提交
      • READ COMMITTED:读已提交
      • REPEATABLE:可重复读
      • SERIALIZABLE:可串行化
    • timeout:超时时间(单位是秒)
      • 设置事务必须在指定时间内进行提交,超时了则进行回滚
      • 默认值是-1,表示不设超时
    • readOnly:是否只读
      • 默认是false,设置为true则只能进行查询,不能进行修改、删除、插入
    • rollbackFor:回滚
      • 设置出现哪些异常时进行事务回滚
    • noRollbackFor:不回滚
      • 设置出现哪些异常时不进行事务回滚
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        <?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"
        xmlns:tx="http://www.springframework.org/schema/tx"
        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
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
        </bean>
        <!-- 开启事务注解 -->
        <tx:annotation-driven transaction-manager="transactionManager"/>
        ### 基于XML配置文件方式
  4. 配置事务管理器
  5. 配置通知
  6. 配置切入点和切面
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
    </bean>
    <!-- 配置通知 -->
    <tx:advice id="txAdvice">
    <!-- 配置事务参数 -->
    <tx:attributes>
    <!-- 指定在哪种规则的方法上添加事务:方法名或通配符 -->
    <tx:method name="account*" isolation="READ_COMMITTED" propagation="REQUIRED"/>
    </tx:attributes>
    </tx:advice>
    <!-- 配置切入点和切面 -->
    <aop:config>
    <!-- 配置切入点 -->
    <aop:pointcut id="point" expression="execution(* com.zephon.test.UserService(..))"/>
    <!-- 配置切面 -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="point"/>
    </aop:config>

完全注解

  1. 创建配置类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    @Configuration
    @ComponentScan(basePackages = {"com.zephon.test"})
    @EnableTransactionManagement // 开启事务
    public class SpringConfig {
    // 创建数据库连接池
    @Bean
    public DruidDataSource getDruidDataSource(){
    DruidDataSource druidDataSource = new DruidDataSource();
    druidDataSource.setDriverClassName("com.jdbc.cj.jdbc.driver");
    druidDataSource.setUrl("jdbc:mysql:///jdbc_learn");
    druidDataSource.setUsername("root");
    druidDataSource.setPassword("123456");
    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;
    }
    }

Spring5新功能

  1. 整个Spring5框架代码基于JDK8,运行时兼容JDK9,将许多不建议的类和方法在代码库中删除

  2. Spring5框架自带了通用的日志封装(可整合log4j2)

  3. @Nullable注解

    • 用在方法上,表示方法返回可以为空
    • 用在属性或参数值上,表示属性或参数值可以为空
  4. 函数式注册对象GenericApplicationContext,可以用来将自己new的对象注册到Spring中,例:

    1
    2
    3
    4
    5
    // 1. 创建GenericApplicationContext对象
    GenericApplicationContext genericApplicationContext = new GenericApplicationContext();
    // 2. 调用registerBean注册,注册之前清空
    genericApplicationContext.refresh();
    genericApplicationContext.registerBean("user", User.class, () -> new User());

  5. 整合JUnit5单元测试框架

  6. SpringWebFlux

SpringWebFlux(简单介绍)

介绍

  • 是Spring5添加的新模块,用于web开发的,功能与Spring MVC类似的,Webflux是因为当前一种比较流行的响应式编程而出现的框架
  • 使用传统web框架,如Spring MVC,是基于Servlet的,而Webflux是一种异步非阻塞框架,而异步非阻塞是在Servlet3.1以后才支持的,核心是基于Reactor的相关API实现的
  • 什么是异步非阻塞?(解释正确性待验证)
    • 异步和同步:针对调用者
      • 同步:调用者发送请求后需要等到回应才做其它事情
      • 异步:调用者改善请求后不用等到回应就能做其它事情
    • 阻塞和非阻塞:针对被调用者
      • 阻塞:被调用者收到请求后完成请求任务才给出反馈
      • 非阻塞:被调用者收到请求后马上给出反馈再完成请求任务
  • Webflux特点:
    • 异步非阻塞:在有限资源下提高系统吞吐量和伸缩性,以Reactor为基础实现响应式编程
    • 函数式编程:Spring5就是基于Java8,Webflux使用Java8函数式编程实现路由请求
  • 和SpringMVC比较:
    • 两个构架都可以使用注解方式,都运行在Tomacat等容器中
    • SpringMVC采用命令式编程,WebFlux采用异步响应式编程

响应式编程

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

  • Java中提供了观察者模式相关的类(Observer和Observerable)

  • 例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class ObserverDemo extends Observable {
    public static void main(String[] args) {
    // 被观察者
    ObserverDemo observable = new ObserverDemo();
    // 添加观察者
    observable.addObserver(new Observer() {
    @Override
    public void update(Observable o, Object arg) {
    System.out.println("发生了变化");
    }
    });
    observable.addObserver((o, arg)->{
    System.out.println("收到被观察者通知,准备改变");
    });
    // 被观察者数据变化
    observable.setChanged();
    // 通知观察者
    observable.notifyObservers();
    }
    }

  • Reactor中使用的实际上是JDK9中新出的Flow.java文件对应的发布订阅函数式接口

  • Reactor实现响应式编程:

    • 两个核心类:
      • Mono:实现Publisher接口,实现发布者,返回0个或1个元素
      • Flux:实现Publisher接口,实现发布者,返回N个元素
    • Flux和Mono都是数据流的发布者,可以发出三种数据信号:
      • 元素值
      • 错误信号,是终止信号,终止数据流同时把错误信息传递给订阅者
      • 完成信号,是终止信号,用于告诉订阅者数据流已经结束
    • API
      • just(Object... args):直接声明
      • fromArray(Object[] objArray):通过数组声明
      • fromIterable(List<> list):通过列表声明
      • fromStream(Stream<> stream):通过流声明
      • 这些方法都只是声明数据流,但没有发出数据流,只有进行订阅之后都会触发数据流,如:

Flux.just(1, 2, 3, 4).subscribe(System.out::println);

  • 三种信号特点:
    • 错误信号和完成信号都是终止信号,不能共存
    • 如果没有发送任何元素,而是直接发送错误信号或完成信号,表示是空数据流
    • 如果没有错误信号,也没有完成信号,表示是无限数据流
  • 操作符:
    • 对数据流进行一个个操作,就是操作符,就和工厂流水线类似
    • 常用操作符:
      • map:元素映射为新元素
      • flatMap :元素映射为流

Webflux执行流程和核心API

  • 核心控制器DispatchHandler,实现接口WebHandler
    • HandlerMapping:请求查询到处理的方法
    • HandlerAdapter:真正负责请求处理
    • HandlerResultHandler:响应结果处理
  • 执行流程暂无
  • SpringWebflux实现函数式编程,两个接口
    • RouterFunction:路由功能,将请求转发给对应的Handler
    • HandlerFunction:处理请求并做出响应

基于注解编程模型

  1. 创建SpringBoot工程,引入webflux依赖

    1
    2
    3
    4
    5
    6
    7
    8
    <!-- <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    </dependency> -->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>

  2. 创建实体类、服务层和控制层

    • 创建接口:

      1
      2
      3
      4
      5
      public interface UserService {
      Mono<User> getUserById(Integer id);
      Flux<User> getAllUsers();
      Mono<Void> saveUserInfo(Mono<User> user);
      }

    • 创建实现类:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      @Service
      public class UserServiceImpl implements UserService{
      // 创建Map集合模拟数据库
      private final Map<Integer, User> users = new HashMap<>();

      public UserServiceImpl(){
      this.users.put(1, new User(1, "张三", "男", 20));
      this.users.put(2, new User(2, "李四", "女", 22));
      this.users.put(3, new User(3, "王明", "男", 18));
      }

      @Override
      public Mono<User> getUserById(Integer id) {
      return Mono.justOrEmpty(this.users.get(id));
      }

      @Override
      public Flux<User> getAllUsers() {
      return Flux.fromIterable(this.users.values());
      }

      @Override
      public Mono<Void> saveUserInfo(Mono<User> userMono) {
      return userMono.doOnNext(user -> {
      this.users.put(user.getId(), user);
      }).thenEmpty(Mono.empty());
      }
      }

    • 创建Controller

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      @RestController
      public class UserController {

      @Autowired
      private UserService userService;

      @GetMapping("/user/{id}")
      public Mono<User> getUserById(@PathVariable int id){
      return userService.getUserById(id);
      }

      @GetMapping("/user")
      public Flux<User> getAllUsers(){
      return userService.getAllUsers();
      }

      @PostMapping("/save")
      public Mono<Void> saveUser(@RequestBody User user){
      Mono<User> userMono = Mono.just(user);
      return userService.saveUserInfo(userMono);
      }

      }

  • 说明:
    • SpringMVC,基于SpringMVC+Servlet+Tomcat
    • SpringWebflux,基于SpringWebflux+Reactor+Netty

基于函数式编程模型

  1. 需要自己初始化服务器

  2. SpringWebflux请求和响应不再是ServletRequest和ServletResponse,而是ServerRequest和ServerResponse

  3. 创建Handler

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    public class UserHandler {
    private final UserService userService;

    public UserHandler(UserService userService) {
    this.userService = userService;
    }

    public Mono<ServerResponse> getUserById(ServerRequest request) {
    Integer id = Integer.valueOf(request.pathVariable("id"));
    // 空值处理
    Mono<ServerResponse> notFound = ServerResponse.notFound().build();
    Mono<User> userMono = userService.getUserById(id);
    // 将userMono转换返回
    return userMono.flatMap(user -> ServerResponse.ok()
    .contentType(MediaType.APPLICATION_JSON).
    bodyValue(user)).switchIfEmpty(notFound);
    }
    public Mono<ServerResponse> getAllUsers(ServerRequest request){
    Flux<User> allUsers = this.userService.getAllUsers();
    return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
    .body(allUsers, User.class);
    }

    public Mono<ServerResponse> save(ServerRequest request){
    Mono<User> userMono = request.bodyToMono(User.class);
    return ServerResponse.ok().build(this.userService.saveUserInfo(userMono));
    }
    }

  4. 编写Router

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    public class Server {
    // 创建路由
    public RouterFunction<ServerResponse> routingFunction(){
    UserHandler userHandler = new UserHandler(new UserServiceImpl());

    return RouterFunctions.route(
    GET("/user/{id}").and(accept(MediaType.APPLICATION_JSON)), userHandler::getUserById)
    .andRoute(GET("/user").and(accept(MediaType.APPLICATION_JSON)), userHandler::getAllUsers)
    .andRoute(POST("/save").and(accept(MediaType.APPLICATION_JSON)), userHandler::save);
    }

    // 创建服务器
    public void createReactorServer(){
    RouterFunction<ServerResponse> routerFunction = routingFunction();
    HttpHandler httpHandler = toHttpHandler(routerFunction);
    ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);

    // 创建服务器
    HttpServer httpServer = HttpServer.create();
    httpServer.handle(adapter).bindNow();
    }

    public static void main(String[] args) {
    // 测试
    Server server = new Server();
    server.createReactorServer();
    System.out.println("Enter to exit");
    System.in.read();
    }
    }

  5. 使用WebClient调用替换浏览器测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class Client {
    public static void main(String[] args) {
    // 传入调用服务器地址
    WebClient webClient = WebClient.create("http://localhost:56284");
    // 根据id查询
    int id = 1;
    User userResult = webClient.get().uri("/user/{id}", id)
    .accept(MediaType.APPLICATION_JSON)
    .retrieve().bodyToMono(User.class)
    .block();
    System.out.println(userResult);

    Flux<User> userFlux = webClient.get().uri("/user")
    .accept(MediaType.APPLICATION_JSON)
    .retrieve().bodyToFlux(User.class);
    userFlux.map(User::getUsername)
    .buffer().doOnNext(System.out::println).blockFirst();
    }
    }
    # Spring配置常用

XML中的bean标签

:表示一个Java对象,可设置属性:

  • id:唯一标识
  • class:对象对应的全限定类名
  • 子标签:
    • :对象通过setter方法设置内部的属性,可设置属性

      • name:属性名
      • value:属性值
      • ref:引用的其它bean的id值
      • scope:作用域,默认为singleton表示单实例,设置为prototype表示多实例
      • init-method:配置初始化方法,值为要执行的方法名
      • destroy-method:配置销毁方法,值为要执行的方法名
      • autowire:自动装配,常用的两个值:
        • byName:根据属性名称注入,bean的Id值要和类中定义的属性名一样
        • byType:根据属性类型注入,但此时相同类型的bean不能有多个
      • 子标签:
        • :表示给属性设置空值
        • :可用来设置特殊符号,如<>就需要写成<[CDATA[<>]]>
        • :内部bean属性
        • :用于注入数组属性,内部可以有多个子标签或子标签
        • :用于注入List类型属性,内部可以有多个子标签或子标签
        • :用于注入Map类型属性,具体还可以配置key和value属性
    • :对象通过构造器参数设置对象内部属性,可设置属性:

      • name:属性名,对应的是构造器上的形参名
      • value:属性值,和中的一样
      • ref:引用的其它bean的id值
      • index:表示有参构造器中的第几个参数(从0开始),不常用

XML中的context命名空间

  • 引入context命名空间的方法

    1
    2
    3
    4
    5
    6
    7
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    </beans>

  • context命名空间常用的标签

    • <context:property-placeholder>:引入外部属性文件
      • 属性location:表示外部属性文件对应的位置,如"classpath:jdbc.properties"表示类路径下的jdbc.properties文件,引入的属性可以通过${属性名}进行调用
    • <context:component-scan>:用于开启组件扫描,通常在使用注解管理Bean时使用
      • 属性base-package:表示要扫描的包,如有多个值则可以用逗号隔开
      • 属性use-default-filters:表示是否使用默认Filter,默认值是true,如果需要自定义filter规则时需要改为false
      • 子标签<context:include-filter>:设置要扫描哪些内容
        • type:表示针对的类型
        • expression:表示具体类型对应的全限定类名
      • 子标签<context:exclude-filter>:设置不扫描的内容
        • type:表示类型
        • expression:表达式

XML中的aop命名空间

  • 引入aop命名空间

    1
    2
    3
    4
    5
    6
    7
    8
    <?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">

  • <aop:aspectj-autoproxy>:开启AspectJ生成代理对象

  • <aop:config>:配置aop增强

    • 子标签<aop:pointcut>:配置切入点
      • 属性id:唯一标识
      • 属性expression:切入点表达式
    • 子标签<aop:aspect>:配置切面
      • 属性ref:增强类对应的bean
      • 子标签<aop:before>:配置前置通知
        • 属性method:前置通知对应在增强类中的方法名
        • 属性pointcut-ref:切点引用
      • 子标签<aop:after>等,同上

XML中的tx命名空间

  • 引入tx命名空间

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?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"
    xmlns:tx="http://www.springframework.org/schema/tx"
    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
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

  • <tx:annotation-driven>:开启事务注解

    • 属性transaction-manager:指定配置的事务管理器id

常用注解

创建对象

四者的使用方法是一样的

  • @Component:普通组件Bean
    • value:和XML中bean的id一样,表示类的唯一标识,默认值为首字母小写后的类名
  • @Service:常用于服务层的类上
  • @Repository:常用于持久层
  • @Controller:学用于控制层

属性注入

  • @Autowired:根据属性类型自动注入(装配)
  • @Qualifier:根据属性名称自动注入(装配),需要和@Autowired一起使用
    • value:值为创建对象的指定的value,表示属性名称
  • @Resource:可以根据类型注入也可以根据名称注入
    • name:指定具体值后,则表示根据对应名称进行注入
  • @Value:注入普通类型属性

配置相关

  • @Configuration:用在类上表示这是一个配置类,用于替代XML配置文件
  • @ComponentScan:表示开启注解扫描
    • basePackages={"包名"}:指定要开启的包
  • @Bean:用在方法上表示一个Bean对象,方法的返回值为对象的类型,需要设置的值作为参数传入方法中
  • 例:
    1
    2
    3
    4
    5
    6
    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
    DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
    dataSourceTransactionManager.setDataSource(dataSource);
    return dataSourceTransactionManager;
    }

AOP相关

  • @Aspect:用在类上表示该类是一个用于增强的类
  • @Order:用在类上表示优先级,当有多个增强类时,按优先级执行,数字越小,优先级越高
  • @EnableAspectJAutoProxy:用在配置类上表示开启对注解AOP的支持
    • proxyTargetClass:为true时表示强制使用cglib代理方式
  • @Before:用在方法上表示该方法是前置通知
    • value:切入点表达式表示要增强的方法,切入点表达式:
      • 语句结构:execution([权限修饰符] [返回类型] [全限定类名].[方法名称] ([参数列表]))
        • 可使用*****表示任意类型
        • 方法返回类型可省略
        • 参数列表可以用..表示
  • @After:用在方法上表示该方法是后置通知,发生异常的情况下也会执行
  • @AfterReturning:用在方法上表示该方法是在被增强方法返回结果之后才执行,即必须没有发生异常
  • @AfterThrowing:用在方法上表示该方法是异常通知
  • @Around:用在方法上表示该方法是环绕通知,对应的方法上可以添加形参ProceddingJoinPoint pjp,然后在方法中调用pjp.proceed()表示执行被增强的方法,当发生异常时不执行环绕之后的内容
  • @Pointcut:用在方法上将相同切入点进行抽取,后续再需要对应的表达式时直接使用对应的方法名即可
  • 执行的顺序(Spring 5.3.22版本) > 环绕之前 > before > 被增强方法 > after returning > after > 环绕之后

事务管理

  • @Transactional:用在类上表示类中所有方法都支持事务;用在方法上表示指定方法支持事务
    • 参数propagation:事务传播行为
      • REQUIRED:如果有事务在运行,当前方法就在这个事务内运行,否则就启动一个新的事务,并在自己的事务内运行
      • REQUIRED_NEW:当前的方法必须启动新事务,并在它自己的事务内运行,如果有事务正在运行,应该将它挂起
      • SUPPORTS:如果有事务在运行,当前的方法就在这个事务内运行,否则它可以不运行在事务中
      • NOT_SUPPORTES:当前方法不应该运行在事务中,如果有运行的事务,将它挂起
      • MANDATORY:当前的方法必须运行在事务内部,如果没有正在运行的事务,就抛出异常
      • NEVER:当前的方法不应该运行在事务中,如果有运行的事务,就抛出异常
      • NESTED:如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则就启动一个新的事务,并在它自己的事务内运行
    • 参数isolation:事务隔离级别
      • READ UNCOMMITTED:读未提交
      • READ COMMITTED:读已提交
      • REPEATABLE:可重复读
      • SERIALIZABLE:可串行化
    • 参数timeout:超时时间(单位是秒)
      • 设置事务必须在指定时间内进行提交,超时了则进行回滚
      • 默认值是-1,表示不设超时
    • 参数readOnly:是否只读
      • 默认是false,设置为true则只能进行查询,不能进行修改、删除、插入
    • 参数rollbackFor:回滚
      • 设置出现哪些异常时进行事务回滚
    • 参数noRollbackFor:不回滚
      • 设置出现哪些异常时不进行事务回滚

测试相关

  • @RunWith(SpringJUnit4ClassRunner.class):Junit4单元测试框架
  • @ContextConfiguration("classpath:bean.xml"):加载配置文件
  • @ExtendWith(SpringExtension.class):Junit5单元测试框架
  • @SpringJunitConfig:复合注解,即:@SpringJunitConfig(locations="classpath:bean.xml")相当于@ExtendWith注解和@ContextConfiguration一起使用