Spring知识点梳理

文章预览:

一.Spring的bean与Ioc

bean标签

<!--
        每一个实现类就对应一个bean标签
            id属性: 对象的唯一标识,根据这个唯一标识,就可以从核心容器中获取对象
            class属性: 对象所属的实现类的全限定名
            scope属性: 对象的范围
                 1. singleton 单例(默认)
                 2. prototype 多例
            lazy-init: 配置懒加载,核心容器创建的时候是否创建出该类对象
            init-method: 配置类的对象初始化的时候,要调用哪个方法
            destroy-method: 配置这个类的对象销毁的时候,要调用哪个方法
            单例模式下(默认没有开启懒加载),由核心容器进行管理的对象什么时候创建什么时候销毁?
            1. 核心容器创建的时候,会创建出它所配置的所有类的对象
            2. 核心容器销毁的时候,它里面的对象才会被销毁

            多例模式下,由spring管理的对象什么时候创建什么时候销毁
            1. 当核心容器调用getBean(id)的时候,创建对象
            2. 垃圾回收机制才能销毁这个对象
    -->
    <bean class="com.wjs.service.impl.UserServiceImpl"
          id="userService"
          scope="prototype" lazy-init="false"
          init-method="init"
          destroy-method="destroy"></bean>
</beans>
1. id或者name属性

​ 用于标识bean , 其实id 和 name都必须具备唯一标识 ,两种用哪一种都可以。但是一定要唯一、 一般开发中使用id来声明.

  • class属性: 用来配置要实现化类的全限定名

  • scope属性: 用来描述bean的作用范围

    ​ singleton: 默认值,单例模式。spring创建bean对象时会以单例方式创建。(默认)

    ​ prototype: 多例模式。spring创建bean对象时会以多例模式创建。

    ​ request: 针对Web应用。spring创建对象时,会将此对象存储到request作用域。(不用管)

    ​ session: 针对Web应用。spring创建对象时,会将此对象存储到session作用域。(不用管)

  • init-method属性:spring为bean初始化提供的回调方法

  • destroy-method属性:spring为bean销毁时提供的回调方法. 销毁方法针对的都是单例bean , 如果想销毁bean , 可以关闭工厂

2.bean的作用范围和生命周期
  • 单例对象: scope=“singleton”,一个应用只有一个对象的实例。它的作用范围就是整个引用。

    1. 核心容器创建的时候,会创建出它所配置的所有类的对象
    2. 核心容器销毁的时候,它里面的对象才会被销毁
  • 多例对象: scope=“prototype”,每次访问对象时,都会重新创建对象实例。

    1. 当核心容器调用getBean(id)的时候,创建对象
    2. 垃圾回收机制才能销毁这个对象

    Spring工厂(了解)

1.ApplicationContext接口的三种实现类
- ClassPathXmlApplicationContext:它是从类的根路径下加载配置文件 
  • FileSystemXmlApplicationContext:它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
  • AnnotationConfigApplicationContext:当我们使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。
2.BeanFactory和ApplicationContext的区别(了解)
  • ApplicationContext 是现在使用的工厂

    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    
  • XmlBeanFactory是老版本使用的工厂,目前已经被废弃【了解】

    BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
    
3. 两者的区别(了解):
  • ApplicationContext加载方式是框架启动时就开始创建所有单例的bean,存到了容器里面

  • 非懒加载: 在核心容器创建的时候,创建出所有的bean对象,存到核心容器中

  • 懒加载: 第一次调用getBean()的时候,创建出bean对象,存到核心容器中

  • BeanFactory加载方式是用到bean时再加载(目前已经被废弃)

    • 懒加载: 第一次调用getBean()的时候,创建出bean对象,存到核心容器中
  • ApplicationContext是BeanFactory的子接口,子接口的功能肯定比父接口更强大

4.新旧工厂
  • 新工厂: 在工厂初始化的话的时候, 就会创建配置的所有的单例bean,存到Spring容器里面
  • 旧工厂(已经废弃了): 等用到对象的时候, 再创建

实例化Bean的三种方式(了解)

1. 无参构造方法方式
<bean class="com.wjs.service.impl.UserServiceImpl"
          id="userService"></bean>
2. 静态工厂方式

需要额外提供工厂类 , 工厂方法是静态方法

<bean id="dataSource" class="com.wjs.service.utils.DruidUtil"
      factory-method="getDataSource"></bean>
3. 实例工厂实例化的方法
  配置实例工厂实例化Bean
  第一步: 配置实例工厂对象
  第二步: 配置bean对象
  factory-bean 表示要调用哪个实例工厂对象的方法
  factory-method 表示要调用实例工厂对象的哪个方法
-->
<bean id="druidUtil" class="com.wjs.service.utils.DruidUtil"></bean>
<bean id="dataSource" factory-bean="druidUtil" factory-method="getDataSource"></bean>

二.Spring的DI

1.概念

依赖注入全称是 dependency Injection 翻译过来是依赖注入.其实就是如果spring核心容器管理的某一个类中存在属性,需要spring核心容器在创建该类实例的时候,顺便给这个对象里面的属性进行赋值。 这就是依赖注入。

​ 现在, Bean的创建交给Spring了, 需要在xml里面进行注册

​ 我们交给Spring创建的Bean里面可能有一些属性(字段), Spring帮我创建的同时也把Bean的一些属性(字段)给赋值, 这个赋值就是注入.

2.构造方法注入
<?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">
      <!--
          它里面的userDao属性要进行赋值
      -->
      <bean id="userService" class="com.wjs.service.impl.UserServiceImpl">
          <constructor-arg name="userDao" ref="userDao"></constructor-arg>
      </bean>
  
      <bean id="userDao" class="com.wjs.dao.impl.UserDaoImpl"></bean>
  </beans>
3.set方法注入(注入)
<?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">
      <!--
          它里面的UserService属性要进行赋值
              第一种:使用有参构造进行属性的注入,使用<constructor-arg>标签注入
              第二种:使用set方法进行属性的注入
              第三种:p命名空间的方式注入
          要注入的属性类型:
              1. 对象(spring核心容器中的)类型: ref="对象的id"
              2. 简单类型: String、int、Integer: value="要注入的数据"
  
      -->
      <bean id="userController" class="com.wjs.controller.UserController">
          <!--set方法注入-->
          <property name="userService" ref="userService"></property>
      </bean>
  
      <!--
          它里面的userDao属性要进行赋值
      -->
      <bean id="userService" class="com.wjs.service.impl.UserServiceImpl">
          <!--set方法注入-->
          <property name="userDao" ref="userDao"></property>
      </bean>
  
  
      <bean id="userDao" class="com.wjs.dao.impl.UserDaoImpl"></bean>
  </beans>

P名称空间注入[了解] 其实底层也是调用set方法,只是写法有点不同

4 p名称空间注入的方式
  • 提供属性的set方法

  • 在spring.xml引入p命名空间xmlns:p="http://www.springframework.org/schema/p"

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
  • 使用
<!--
        它里面的userDao属性要进行赋值
    -->
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl" p:userDao-ref="userDao">
    <!--构造函数方式注入-->
    <!--<constructor-arg name="userDao" ref="userDao"></constructor-arg>-->
    <!--set方法注入-->
    <!--<property name="userDao" ref="userDao"></property>-->
</bean>


<bean id="userDao" class="com.wjs.dao.impl.UserDaoImpl"
      p:age="28"
      p:username="奥巴马">
</bean>

三.Spring的注解

一.使用注解创建对象

1.注解开发与配置开发区别

学习基于注解的 IoC 配置,即注解配置和 xml 配置要实现的功能都是一样的,都是要降低程序间的耦合。只是配置的形式不一样。

2.包扫描(第一步)

在beans标签内部,使用context:component-scan ,让spring扫描该基础包下的所有子包注解
<context:component-scan base-package="com.wjs"></context:component-scan>

在applicationContext.xml 开启包扫描​

3.@Component

作用:
​ 把资源让 spring 来管理。相当于在 xml 中配置一个 bean。
属性:
​ value:指定 bean 的 id。如果不指定 value 属性,默认 bean 的 id 是当前类的类名。首字母小写。

4. @Controller @Service @Repository

​ web里面的三层结构中的所有类,在spring里面都称之为 Component (组件) , 但是它也提供了更详细的注解来针对不同的层级声明 。

​ 三个衍生注解如下:

   		 	 @Controller               :修饰WEB层类	 --->web | SpringMVC 

  		  	 @Service                    :修饰业务层类	 --->service

  		  	 @Repository             :修饰DAO层类	 --->dao

​ 在需要spring创建对象的类上面使用注解 @Component(“us”) 即可.Spring看类上是否有该注解,如果有该注解,生成这个类的实例。如果不指定value值, 那么默认的id值就是类名的名字, 第一个字母小写.

5. 用于改变作用范围的@Scope

@Scope

​ singleton: 单例(默认)

​ prototype:多例

​ @Scope注解用来描述类的作用范围的,默认值singleton。如同xml中bean标签的属性scope <bean scope=""/>.如果配置成多例的使用prototype。

@Scope("prototype")
@Component("accountService")
public class AccountServiceImpl implements AccountService {}
6.和生命周期相关的【了解】
  • 初始化和销毁回调方法对应的注解

    ​ @PostConstrut:如同xml中bean标签的属性init-method <bean init-method=""/>,用来设置spring框架初始化此类实例时调用的初始化方法,标注在此类的初始化方法上

    ​ @PreDestroy:如同xml中bean标签的属性destroy-method <bean destroy-method=""/>,用来设置spring框架销毁此类实例时调用的销毁方法,标注在此类的销毁方法上

    注意:这两个注解都是配在方法上的

    二.使用注解注入属性

1.@Value : 注入简单类型的数据
  • 作用:

    注入基本数据类型和 String 类型数据的

  • 属性:

    value:用于指定值 , 可以通过表达式动态获得内容再赋值

  • 实例:

@Value("奥巴马")
private String
2.@Autowired
  • 作用:

    ​ 自动按照类型注入。当使用注解注入属性时, set 方法可以省略。它只能注入其他 bean 类型。

    ​ 如果只有一个实现类, 可以自动注入成功

    ​ 如果有两个或者两个以上的实现类, 找到变量名一致的id对象给注入进去, 如果找不到,就报错

  • 实例:

@Component("accountService")
public class AccountServiceImpl implements AccountService {
    @Autowired
    //当有多个AccountDao实现类时候, @Autowired会在在Spring容器里面找id为accountDao的对象注入,找不到就报错
    private AccountDao accountDao;
    @Override
    public void save() {
        System.out.println("AccountServiceImpl---save()");
        accountDao.save();
    }
}
3.@Qualifier
  • 作用

    在自动按照类型注入(@Autowired)的基础之上,再按照Bean的id注入。
    

    ​ 它在给字段注入时不能独立使用,必须和@Autowire一起使用;

    ​ 但是给方法参数注入时,可以独立使用。

  • 属性

    ​ value:指定bean的id。

  • 实例

package com.wjs.controller;

import com.wjs.service.UserService;
import org.springframework.stereotype.Controller;

import javax.annotation.Resource;

/**
 * 包名:com.wjs.controller
 *
 * 注入对象类型的属性:
 *    1. Autowired: 自动装配, 如果spring的核心容器中,只有一个该类型的对象,则自动把那个对象注入给当前属性;
 *       如果spring的核心容器中,不止有一个该类型的对象,那么就会根据属性名匹配对象的id,匹配上哪个就注入哪个;
 *       如果一个都匹配不上,那么我们还可以通过Qualifier指定要注入的对象的id
 */
@Controller
public class UserController {
    @Autowired
    @Qualifier("userServiceImplAnother")
    private UserService userService;
    public String getName(){
        //调用业务层的getName()方法
        return userService.getName();
    }
}
4.@Resource

如果上面一个接口有多种实现,那么现在需要指定找具体的某一个实现,那么可以使用@Resource

package com.wjs.controller;

import com.wjs.service.UserService;
import org.springframework.stereotype.Controller;

import javax.annotation.Resource;

/**
 * 包名:com.wjs.controller
 *
 * 注入对象类型的属性:
 *    1. Autowired: 自动装配, 如果spring的核心容器中,只有一个该类型的对象,则自动把那个对象注入给当前属性;
 *       如果spring的核心容器中,不止有一个该类型的对象,那么就会根据属性名匹配对象的id,匹配上哪个就注入哪个;
 *       如果一个都匹配不上,那么我们还可以通过Qualifier指定要注入的对象的id
 *
 *   2. Resource: 能够进行自动装配以及手动装配
 */
@Controller
public class UserController {
    @Resource(name = "userServiceImpl")
    private UserService userService;
    public String getName(){
        //调用业务层的getName()方法
        return userService.getName();
    }
}

二.小结

  1. 注入简单类型 @Value("值/表达式")
  2. 注入对象类型
    • 如果只有一个实现类, 建议使用@Autowired
    • 如果有多个实现类, 建议使用@Resource("name=“id”)

    三.混合开发

1.XML与注解的优劣势
  • xml
    • 优点: 方便维护, 改xml文件
    • 缺点: 相对注解而言, 麻烦一点
  • 注解
    • 优点: 开发简洁方便
    • 缺点: 维护没有xml那么方便, 需要改源码
2.混合开发特点

​ IOC: 自己写的类使用注解进行IOC,非自己写的类使用配置文件进行IOC

​ DI: 如果一个类是使用注解进行IOC的,那么它的属性使用注解进行注入;如果一个类是使用配置文件进行IOC,那么它的属性使用配置文件进行注入

四.spring整合mybatis(非常重要)

1.步骤
  1. 引入mybatis以及整合的相关依赖
  2. 在spring的IOC容器中创建DataSource对象(可以使用spring内置的DataSource)
  3. 在spring的IOC容器中创建SqlSessionFactoryBean对象,并且可以指定要配置别名的包或者是加载核心配置文件
  4. 在spring的IOC容器中创建MapperScannerConfigurer对象,用于扫描Dao接口创建代理对象
  5. 通过注解的方式注入Dao代理对象
2.实现步骤
2.1. 引入依赖
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <!--spring-->
    <spring.version>5.0.2.RELEASE</spring.version>
    <!--日志打印框架-->
    <slf4j.version>1.6.6</slf4j.version>
    <log4j.version>1.2.12</log4j.version>
    <!--mysql-->
    <mysql.version>5.1.6</mysql.version>
    <!--mybatis-->
    <mybatis.version>3.5.3</mybatis.version>
</properties>

<dependencies>
    <!--引入依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <!--mysql依赖-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>${mysql.version}</version>
    </dependency>
    <!-- log start -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>${log4j.version}</version>
    </dependency>

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>${slf4j.version}</version>
    </dependency>

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>${slf4j.version}</version>
    </dependency>
    <!-- log end -->

    <!--mybatis的依赖-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>${mybatis.version}</version>
    </dependency>

    <!--
          mybatis整合spring的依赖
        -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>1.3.0</version>
    </dependency>

    <!--lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.10</version>
    </dependency>

    <!--junit-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>
2.2. spring.xml文件配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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">
    <!--1. 包扫描-->
    <context:component-scan base-package="com.wjs"/>

    <!--
        2. spring整合mybatis
    -->
    <!--
        1. 配置jdbc的环境:
            1.1 配置dataSource(既可以使用spring内置的DataSource,又可以使用第三方的DataSource)
            1.2 配置事务管理者(现在不做),后面笔记声明式事务的时候有
        2. 将SqlSessionFactoryBean 对象进行IOC配置到spring的核心容器中,并且将dataSource注入进去
        3. 扫描dao包,创建出dao的代理对象,交给spring的核心容器管理
           各种dao代理对象的id,就是接口名首字母改小写
    -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="username" value="root"></property>
        <property name="password" value=""></property>
        <property name="url" value="jdbc:mysql:///数据库?characterEncoding=utf8"></property>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    </bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <!--加载mybatis的主配置文件-->
        <!--<property name="configLocation" value="classpath:SqlMapConfig.xml"></property>-->
        <!--别名配置的包扫描-->
        <property name="typeAliasesPackage" value="com.wjs.pojo"></property>
    </bean>

    <bean id="scannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.wjs.dao"></property>
    </bean>
</beans>
3.纯注解改造增删改查(了解)
3.1实现
3.2 @Configuration
  • 作用:

    用于指定当前类是一个spring配置类,当创建容器时会从该类上加载注解。

    获取容器时需要使用AnnotationApplicationContext(类.class)。

  • 属性:

    value:用于指定配置类的字节码

  • 示例代码:

@Configuration
public class SpringConfig {

}

3.3 @ComponentScan
  • 作用:

    用于指定spring在初始化容器时要扫描的包。作用和在spring的xml配置文件中的: <context:component-scan base-package="com.wjs"/>是一样的。

  • 属性:

    basePackages:用于指定要扫描的包。和该注解中的value属性作用一样。

  • 示例代码:

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

}
3.4@Bean
  • 作用:

    该注解只能写在方法上,表明使用此方法创建一个对象,并且放入spring容器。

  • 属性:

    name:给当前@Bean注解方法创建的对象指定一个名称(即bean的id)。

  • 示例代码:

@Configuration
@ComponentScan(basePackages = "com.wjs")
public class SpringConfig {
	@Bean
    public DataSource getDataSource(){
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setUsername("root");
        dataSource.setPassword("");
        dataSource.setUrl("jdbc:mysql:///数据库?characterEncoding=utf8");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        return dataSource;
    }

    @Bean
    public SqlSessionFactoryBean getSqlSessionFactoryBean(DataSource dataSource){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        //设置dataSource属性
        sqlSessionFactoryBean.setDataSource(dataSource);
        //设置typeAliasesPackage
        sqlSessionFactoryBean.setTypeAliasesPackage("com.wjs.pojo");
        return sqlSessionFactoryBean;
    }

    @Bean
    public MapperScannerConfigurer getMapperScannerConfigurer(){
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        mapperScannerConfigurer.setBasePackage("com.wjs.dao");
        return mapperScannerConfigurer;
    }
}
3.5通过注解获取容器
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
4.Spring整合junit(了解)
4.1目标

​ 在测试类中,每个测试方法都有以下两行代码:

​ ApplicationContext ac = new ClassPathXmlApplicationContext(“bean.xml”);

   AccountService as= ac.getBean("accountService",AccountService.**class**);

​ 这两行代码的作用是获取容器,如果不写的话,直接会提示空指针异常。所以又不能轻易删掉。

4.2分析

​ 早前我们测试业务逻辑,一般都使用Junit 框架来测试, 其实在spring框架里面它也做出来了自己的一套测试逻辑, 里面是对junit进行了整合包装。让我们在执行spring的单元测试上面,可以少写一些代码.

  1. 导入Spring整合单元测试的坐标
  2. 在测试类上面添加注解(指定运行的环境, 加载配置文件或者配置类)
4.3实现
  • 导入spring整合Junit的坐标

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    
  • 在测试类上面标记注解

    package com.itheima.test;
    
    import com.wjs.controller.AccountController;
    import com.wjs.pojo.Account;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    import java.sql.SQLException;
    import java.util.List;
    
    /**
     * 包名:com.wjs.test
     *
     * 直接在测试用例中,注入要使用的对象AccountController
     * 1. 我们自己不创建核心容器,那么我们就应该交给别人(Junit)去创建核心容器
     *    1. 引入spring整合Junit的依赖  spring-test
     *    2. 保证Junit的版本是4.12以及以上的版本
     *    3. 在单元测试类上添加RunWith注解
     *       @RunWith(SpringJUnit4ClassRunner.class)
     *    4. 指定有Junit创建核心容器的时候,要加载的配置文件/配置类
     *       @ContextConfiguration(locations = "classpath:applicationContext.xml") 混合开发
     *       @ContextConfiguration(classes = 配置类名.class) 纯注解开发
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = {"classpath:applicationContext.xml"})
    public class TestSpring {
        @Autowired
        private AccountController accountController;
        @Test
        public void testFindAll() throws SQLException {
            List<Account> accountList = accountController.findAll();
            System.out.println(accountList);
        }
    }
    
4.3小结
  1. 添加Spring整合单元测试的坐标
  2. 在测试类上面添加注解
    • @RunWith(SpringJUnit4ClassRunner.class) 指定运行的环境
    • @ContextConfiguration() 加载配置文件或者配置类的
    5.混合开发方式补充

5.1知识点-import标签

使用import标签导入外部的xml配置文件

实现
<import resource="classpath:spring-mybatis.xml"/>
知识点-引入外部properties文件

引入外部的properties属性文件

实现
<context:property-placeholder location="classpath:jdbc.properties"/>

<!--1. 创建DataSource对象-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="username" value="${jdbc.username}"></property>
    <property name="password" value="${jdbc.password}"></property>
    <property name="url" value="${jdbc.url}"></property>
    <property name="driverClassName" value="${jdbc.driver}"></property>
</bean>

五.AOP

1.基本名词解释

连接点: 所有可以被增强的方法。
切入点: 已经在项目中真正配置的方法。(例如一个项目有增删改查,但是只对增加方法进行配置增强)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

通知: 用于增强切入点的方法。
通知有----->1.前置通知,2.后置通知,3.环绕通知,4.异常通知,5.最终通知。
切面: 存放通知的类。
execution表达式: expression是切入点表达式,它的作用是用一个表达式描述切入点
在这里插入图片描述
如图:
第一个 * 表示所有返回值。如果写void,表达找到所有返回值是void的方法。
第二个表示找到一个类。
第三个 .* 表达所有方法名
第四个 (…) 表达方法里面可以有参数或则没有参数都可以,有参数可以是任意类型。(*)则表示方法里面必须有参数。这里的方法都是切入点的方法
在这里插入图片描述

2.Aop几个通知的介绍
2.1 后置通知

<aop:after-returning method="" pointcut-ref="" returning=""/>如果配置的方法中形参有值则,在所有切入点的方法中,只有有返回值的方法才能被用到后置通知,否则都可以用到后置通知。

2.2 Aop异常通知

切入点出现异常时候执行的操作。常用的是把异常信息写入到本地文件中,但是后来被很多的日志工具代替了。
切入点:

 public void error(Throwable errorMsg){
        //使用流将异常信息写入到本地磁盘文件中
        System.out.println("将"+errorMsg.getMessage()+"写入到磁盘");//此处省略了写入到磁盘操作,相当于日志打印
    }

异常配置:

<aop:after-throwing method="error" pointcut-ref="p1" throwing="errorMsg"/>
2.3 最终通知

在执行所有方法之后,无论是否出现异常,均执行的代码片段。
切面类中代码如下:

 public void finalM(){
        System.out.println("hi");
    }

配置如下:

<aop:after method="finalM" pointcut-ref="p1"/>
2.4环绕通知

环绕通知是在通知方法的中间进行执行。需要在方法中传入ProceedingJoinPoint形式参数,然后.proceed执行切入点
切入点:

   @Override
    public String query() {
        try {
            Thread.sleep(3000);
            System.out.println("执行查询...");
            return "张三";
        } catch (InterruptedException e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
    }

通知
在这里插入图片描述
配置切入点

 <aop:pointcut id="p2" expression="execution(String com.wjs.service.impl.UserServiceImpl.query())"/>

配置通知:

<!--配置环绕通知-->
            <aop:around method="totalTime" pointcut-ref="p2"/>
3.Aop注解配置
3.1 在spring.xml中加入
<aop:aspectj-autoproxy/>
3.2 在切面中加入注解,并在其中配置切入点以及通知。

以下面的代码为例

@Component
@Aspect
public class MyAspect {
    @Pointcut("execution(* com.wjs.service.impl.UserServiceImpl.*(..))")
    public void p1(){
    }
    @Pointcut("execution(String com.wjs.service.impl.UserServiceImpl.query()))")
    public void p2(){
    }
    @Before("p1()")
    public void checkPermission(){
        System.out.println("进行权限校验...");
    }
    @AfterReturning(value = "p1()",returning = "reback")
    public void afterMehod(String reback){
        System.out.println("后置方法执行");
    }
    public void getBack(){
        System.out.println("执行完毕,返回值为...");
    }
    public void error(Throwable errorMsg){
        //使用流将异常信息写入到本地磁盘文件中
        System.out.println("将"+errorMsg.getMessage()+"写入到磁盘");//此处省略了写入到磁盘操作,相当于日志打印
    }
    @After("p1()")
    public void finalM(){
        System.out.println("hi");
    }
    //环绕通知,统计代码执行时间
    @Around("p2()")
    public Object totalTime(ProceedingJoinPoint joinPoint){
    //    1. 获取当前时间
        long startTime = System.currentTimeMillis();
    //    2.执行切入点
        try {
            Object proceed = joinPoint.proceed();
            //    3.获取执行完切入点之后的时间
            long endTime = System.currentTimeMillis();
            System.out.println("方法的总执行时间是" + (endTime - startTime));
            return proceed+"sb";
        } catch (Throwable e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
    }
3.3 删除配置文件中的Aop配置信息

六. Spring中的JdbcTemplate 【了解】

   <!-- 配置HikariCP数据库连接池 -->
  <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
  	<property name="jdbcUrl" value="jdbc:mysql:///spring_jdbc" />
  	<property name="driverClassName" value="com.mysql.jdbc.Driver" />
  	<property name="username" value="root" />
  	<property name="password" value="" />
  </bean>

七. Spring中的事务

1.编程式(通过代码)(硬编码)事务【了解】
  • 持久层不同的框架有不同的事务控制方案:
    1. 原始的JDBC:
  • 1.1 开启事务: connection.setAutoCommit(false);
  • 1.2 提交事务: connection.commit();
  • 1.3 回滚事务: connection.rollback();
    1. DBUtils框架: 和原始的JDBC是一样的
    1. mybatis框架: 底层也是使用的JDBC的事务
  • 3.1 开启事务: sqlSessionFactory.openSession(); 开启事务
  • 3.2 提交事务: sqlSession.commit();
  • 3.3 回滚事务: sqlSession.rollback();
    1. 只要某个持久层框架和spring整合了,那么事务相关的操作就都交给spring控制
  • 4.1 编程式事务(了解)
  • 4.2 声明式事务(重点掌握)
    1.创建事务管理者
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();

2.给事务管理者注入dataSource

transactionManager .setDataSource(dataSource);

3.创建事务模板

TransactionTemplate transactionTemplate = new TransactionTemplate();

4.给事务模板配置事务管理者

transactionTemplate.setTransactionManager(transactionManager);

6.事务模板执行方法,需要被事务执行的方法放入

 transactionTemplate.execute(new TransactionCallback<Object>() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
                //1. 转出账户扣款
                accountDao.updateAccount(new Account(null,fromName,-money));

                //出现异常
                int num = 10/0;

                //2. 转入账户收款
                accountDao.updateAccount(new Account(null,toName,money));
                return null;
            }
        });
    }

上面这种配置方式把代码写在业务层不利于业务代码的书写

2.Spring声明式事务-xml配置方式【重点】

Spring的声明式事务的作用是: 无论spring集成什么dao框架,事务代码都不需要我们再编写了

声明式的事务管理的思想就是AOP的思想。面向切面的方式完成事务的管理。声明式事务有两种,xml配置方式和注解方式.

2.1 步骤
  1. 注册事务管理器, 配置数据源
  2. 配置事务建议(事务规则: 事务隔离级别, 遇到什么异常回滚,是否只读…)
  3. 配置AOP
    • 配置切入点
    • 配置切面
  • 配置事务管理器
<bean id="transactionManager" 		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
</bean>
  • 配置事务建议(事务的规则)

    <!--配置事务规则-->
    <tx:advice id="transferAdvice" transaction-manager="transactionManager">
        <!--配置事务的属性-->
        <tx:attributes>
            <!--
                    哪个方法需要使用什么样的事务配置
                        rollback-for="java.lang.Exception" 什么时候回滚:遇到所有的Exception都会回滚
                        no-rollback-for="java.lang.NullPointerException" 什么时候不回滚:遇到NullPointerException不回滚
                        timeout="-1" 事务的超时时间,默认不超时
                        read-only="false" 是否是只读事务,默认不是只读事务,只读事务只会针对于查询方法,如果是增删改一定不能设置为只读
                        isolation="" 事务的隔离级别: 目的是为了防止不同事务之间相互影响
                            1. Read Uncommitted  读取到未提交的事务,在这种隔离级别下,可能发生脏读、不可重复读、幻读
                            2. Read Committed(Oracle的默认隔离级别) 读取到已提交的事务,在这种隔离级别下,不可能发生脏读,但是可能发生不可重复读和幻读
                            3. Repeatable Read(mysql的默认隔离级别) 可重复读,在这种隔离级别下不会发生脏读、不可重复读,但是有可能发生幻读
                            4. Serializable 串行化的,在这种隔离级别下不会发生脏读、不可重复读、幻读
    
                        propagation="" 事务的传播行为
                                - PROPAGATION_REQUIRED:默认值,也是最常用的场景.
                                  如果当前没有事务,就新建一个事务,
                                  如果已经存在一个事务中,加入到这个事务中。
    
                                - PROPAGATION_SUPPORTS:
                                  如果当前没有事务,就以非事务方式执行。
                                  如果已经存在一个事务中,加入到这个事务中。
    
                                - PROPAGATION_MANDATORY
                                  如果当前没有有事务,就抛出异常;
                                  如果已经存在一个事务中,加入到这个事务中。
    
                                保证不在同一个事务里:
                                - PROPAGATION_REQUIRES_NEW
                                  如果当前有事务,把当前事务挂起,创建新的事务但独自执行
    
                                - PROPAGATION_NOT_SUPPORTED
                                  如果当前存在事务,就把当前事务挂起。不创建事务
    
                                - PROPAGATION_NEVER
                                  如果当前存在事务,抛出异常
    
                -->
            <tx:method name="transfer"
                       rollback-for="java.lang.Exception"/>
        </tx:attributes>
    </tx:advice>
    
  • 配置事务的AOP

    <aop:config>
        <!--声明切入点-->
        <aop:pointcut id="pt1" expression="execution(* com.wjs.service.impl.AccountServiceImpl.transfer(..))"/>
        <!--绑定切入点和事务-->
        <aop:advisor advice-ref="transferAdvice" pointcut-ref="pt1"></aop:advisor>
    </aop:config>
    
    3 事务的传播行为
3.1事务的传播行为的作用

​ 我们一般都是将事务设置在Service层,那么当我们调用Service层的一个方法的时候, 它能够保证我们的这个方法中,执行的所有的对数据库的更新操作保持在一个事务中, 在事务层里面调用的这些方法要么全部成功,要么全部失败。

​ 如果你的Service层的这个方法中,除了调用了Dao层的方法之外, 还调用了本类的其他的Service方法,那么在调用其他的Service方法的时候, 我必须保证两个service处在同一个事务中,确保事物的一致性。

​ 事务的传播特性就是解决这个问题的。

3.2 事务的传播行为的取值

保证在同一个事务里面:

  • PROPAGATION_REQUIRED:默认值,也是最常用的场景.

    如果当前没有事务,就新建一个事务,
    如果已经存在一个事务中,加入到这个事务中。

  • PROPAGATION_SUPPORTS:

    如果当前没有事务,就以非事务方式执行。

    如果已经存在一个事务中,加入到这个事务中。

  • PROPAGATION_MANDATORY

    如果当前没有有事务,就抛出异常;

    如果已经存在一个事务中,加入到这个事务中。

保证不在同一个事物里:

  • PROPAGATION_REQUIRES_NEW

    如果当前有事务,把当前事务挂起,创建新的事务但独自执行

  • PROPAGATION_NOT_SUPPORTED

    如果当前存在事务,就把当前事务挂起。不创建事务

  • PROPAGATION_NEVER

    如果当前存在事务,抛出异常

4 spring声明式事务-注解方式【重点】
4.1 步骤
  1. 注册事务管理器

  2. 开启事务注解支持

  3. 在业务类上面添加@Transactional

  • 在applicationContext里面打开注解驱动

    <?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: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/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">
        <import resource="classpath:application-mybatis.xml"></import>
        <!--配置申明式事务-->
        <!--
            注解方式配置声明式事务
                1. 配置事务管理者
                2. 加载事务注解驱动
                3. 在代码中使用Transactional注解来标注,哪个方法需要使用事务
        -->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
        <tx:annotation-driven transaction-manager="transactionManager"/>
    </beans>
    
  • 在业务逻辑类上面使用注解

    @Transactional : 如果在类上声明,那么标记着该类中的所有方法都使用事务管理。也可以作用于方法上, 那么这就表示只有具体的方法才会使用事务。

4.2两种方式比较
  • xml方式
    • 优点: 只需要配一次, 全局生效, 容易维护
    • 缺点: 相对注解而言 要麻烦一点
  • 注解方式
    • 优点: 配置简单
    • 缺点: 写一个业务类就配置一次, 事务细节不好处理, 不好维护
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值