Srping复习总结

本文素材来自动力结点王妈妈的课程

什么是Spring

Spring 是于 2003 年兴起的一个轻量级的 Java 开发框架,它是为了解 决企业应用开发的复杂性而创建的。Spring的核心是控制反转(IoC)和面向切面编程(AOP)。Spring 根据代码的功能特点,使用 Ioc 降低业务对象之间耦合度。通过 Spring 提供的 AOP 功能,方便进行面向切面的编程。

Spring官网

IoC控制反转

控制反转(IoC,Inversion of Control),是一种思想。指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。Spring 框架使用依赖注入(DI)实现 IoC。

初识Spring

1、创建maven项目
在这里插入图片描述
2、引入 maven 依赖

	<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>

  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>

3、定义接口与实体类

public interface SomeService {
    void doSome();
}

public class SomeServiceImpl implements SomeService {
    public SomeServiceImpl() {
        super();
        System.out.println("SomeServiceImpl无参数构造方法");
    }
    @Override
    public void doSome() {
        System.out.println("doSome业务方法");
    }
}

4、创建 Spring 配置文件
在 src/main/resources/目录现创建一个 xml 文件,文件名可以随意,但Spring 建议的名称为applicationContext.xml。注意要将resources文件将指定为资源文件夹(这个文件夹中的文件编译之后都会放在类路径下),如何指定?
右键文件夹 → Mark Directory as → Resources Root

<?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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!--
        注册bean对象
            id:自定义对象的名称
            class:类的全限定名称,不能是接口
    -->
    <bean id="someService" class="com.why.SomeServiceImpl"></bean>
</beans>

5、定义测试类

public class MyTest {
    @Test
    public void test01(){
        //指定spring配置文件的位置和名称
        String resource = "applicationContext.xml";
        //创建spring容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
        //使用id从spring容器中获取对象
        SomeService someService = (SomeService) ac.getBean("someService");
        //执行对象的方法
        someService.doSome();
    }
}

基于XML依赖注入(DI)

set注入

public class School {
    private String schoolName;
    private String addr;
	/*setter*/
	/*toString*/
}

public class Student {
    private String name;
    private int age;
    private School school;
	/*setter*/
	/*toString*/
}

	<!--使用set方法注入参数-->
    <bean id="mySchool" class="com.why.School">
        <!--简单类型-->
        <property name="schoolName" value="浙江中医药大学"></property>
        <property name="addr" value="浙江杭州"></property>
    </bean>

    <bean id="zs" class="com.why.Student">
        <property name="name" value="张三"></property>
        <property name="age" value="20"></property>
        <!--引用类型-->
        <property name="school" ref="mySchool"></property>
    </bean>

构造注入

//Student构造函数
public Student(String stuName, int stuAge, School stuSchool) {
	this.name = stuName;
    this.age = stuAge;
    this.school = stuSchool;
}
	<!--构造注入-->
    <bean id="ls" class="com.why.Student">
        <constructor-arg name="stuName" value="李四"></constructor-arg>
        <constructor-arg name="stuAge" value="20"></constructor-arg>
        <constructor-arg name="stuSchool" ref="mySchool"></constructor-arg>
    </bean>
    <!--index:指明该参数对应着构造器的第几个参数,从0开始(少用)-->
	<bean id="ww" class="com.why.Student">
        <constructor-arg index="0" value="王五"></constructor-arg>
        <constructor-arg index="1" value="20"></constructor-arg>
        <constructor-arg index="2" ref="mySchool"></constructor-arg>
    </bean>
    <!--index属性不要也行,但要注意:若参数类型相同或之间有包含关系,则需要保证赋值顺序要与构造器中的参数顺序一致-->
    <bean id="zl" class="com.why.Student">
        <constructor-arg value="赵六"></constructor-arg>
        <constructor-arg value="20"></constructor-arg>
        <constructor-arg ref="mySchool"></constructor-arg>
    </bean>

引用类型属性自动注入

byName方式自动注入

容器是通过调用者的 bean 类的属性名与配置文件的被调用者 bean 的id 进行比较而实现自动注入的

public class Student {
    private String name;
    private int age;
    private School schoolXXX;
	/*setter*/
	/*toString*/
}

byName方式自动注入时Spring会根据用于类型的变量名去调用相应的setter方法
(比如这里我们要注入zy对象 Spring根据autowire的属性发现是根据byName方式自动注入。Spring会找到Student中的Schoo引用类型,根据schoolXXX属性名在容器中找到名为schoolXXX的对象(如果在找不到schoolXXX对象Spring就会去找引用类型名首字母小写的对象本例中为school 如果两个都找不到就会报错)然后去调用setSchoolXXX方法将引用类型注入)

	<bean id="schoolXXX" class="com.why.School">
        <property name="schoolName" value="温州大学"></property>
        <property name="addr" value="浙江温州"></property>
    </bean>

    <bean id="zy" class="com.why.Student" autowire="byName">
        <property name="name" value="张扬"></property>
        <property name="age" value="20"></property>
    </bean>

byType方式自动注入

使用 byType 方式自动注入,要求:配置文件中被调用者 bean 的 class 属性指定的类,要与代码中调用者 bean 类的某引用类型属性类型同源。即要么相同,要么有 is-a 关系(子类或是实现类)。

	<bean id="cb" class="com.why.Student" autowire="byType">
        <property name="name" value="陈八"></property>
        <property name="age" value="20"></property>
    </bean>

如果使用byType方式自动注入要求同源的被调用 bean只能有一个否则会报以下错误
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type ‘com.why.School’ available: expected single matching bean but found 2: mySchool,school

为应用指定多个 Spring 配置文

在实际应用里,随着应用规模的增加,系统中 Bean 数量也大量增加,导致配置文件变得非常庞大、臃肿。为了避免这种情况的产生,提高配置文件的可读性与可维护性,可以将 Spring 配置文件分解成多个配置文件。
在这里插入图片描述
spring-school.xml

    <bean id="middleSchool" class="com.why.School">
        <property name="schoolName" value="温州中学"></property>
        <property name="addr" value="浙江温州"></property>
    </bean>

spring-student.xml

	<bean id="lh" class="com.why.Student">
        <property name="name" value="林浩"></property>
        <property name="age" value="20"></property>
        <!--引用类型-->
        <property name="school" ref="middleSchool"></property>
    </bean>
    <!--引入spring-school.xml-->
    <import resource="classpath:spring-school.xml"/>

applicationContext.xml(主配置文件)

    <import resource="classpath:spring-school.xml"/>
    <import resource="classpath:spring-student.xml"/>
    <!--也可使用通配符*-->
    <!-- <import resource="classpath*:spring-*.xml"/> -->

基于注解的 DI

对于 DI 使用注解,将不再需要在 Spring 配置文件中声明 bean 实例。需要在 Spring 配置文件中配置组件扫描器,用于在指定的基本包中扫描注解。

	<!--声明组件扫描器:指定注解所在的包-->
    <context:component-scan base-package="com.why"/>
    <!-- 指定多个包
    <context:component-scan base-package="com.why,com.ehy"/> 第1种方式:逗号分隔
    <context:component-scan base-package="com.why;com.ehy"/> 第2种方式:分号分隔
    <context:component-scan base-package="com.why com.ehy"/> 第3种方式:空格分隔
    <context:component-scan base-package="com"/> 第4种方式:指定到父包,也会扫描到子包下级的子包-->

定义bean的注解@Component

需要在类上使用注解@Component,该注解的 value 属性用于指定该bean 的 id 值

//@Component 不指定value属性,bean的id是类名的首字母小写school
@Component("collage")//类似xml中的<bean id="collage" class="com.why.School"></bean>
public class School {
    private String schoolName;
    private String addr;
}
  • @Repository 用于对 DAO 实现类进行注解
  • @Service 用于对 Service 实现类进行注解
  • @Controller 用于对 Controller 实现类进行注解
    这三个注解与@Component 都可以创建对象,但这三个注解还有其他的含义,@Service 创建业务层对象,业务层对象可以加入事务功能,@Controller 注解创建的对象可以作为处理器接收用户的请求。

简单类型属性注入@Value

使用该注解完成属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加到 setter 上。

@Component("collage")
public class School {
    @Value("浙江大学")
    private String schoolName;
    @Value("浙江杭州")
    private String addr;
    /*toString()*/
}

byType 自动注入@Autowired

@Component("tq")
public class Student {
    @Value("田七")
    private String name;
    @Value("20")
    private int age;
    @Autowired
    private School school;
/*相当于
<bean id="tq" class="com.why.Student" autowire="byType">
	<property name="name" value="田七"></property>
	<property name="age" value="20"></property>
</bean>
*/
}

byName 自动注入@Autowired 与@Qualifier

需要在引用属性上联合使用注解@Autowired 与@Qualifier。@Qualifier的 value 属性用于指定要匹配的 Bean 的 id 值。类中无需 set 方法,也可加到set 方法上。

@Component("tq")
public class Student {
    @Value("田七")
    private String name;
    @Value("20")
    private int age;
    @Autowired
    @Qualifier("middleSchool")
    private School school;

@Autowired 还有一个属性 required,默认值为 true,表示当匹配失败后,会终止程序运行。若将其值设置为 false,则匹配失败,将被忽略,未匹配的属性值为 null。

JDK 注解@Resource 自动注入

Spring 提供了对 jdk 中@Resource 注解的支持。@Resource 注解既可以按名称匹配 Bean,也可以按类型匹配 Bean。默认是按名称注入,采用默认按名称的方式注入按名称不能注入 bean时,则会按照类型进行 Bean 的匹配注入。

@Component("tq")
public class Student {
    @Value("田七")
    private String name;
    @Value("20")
    private int age;
    @Resource //byType
    //@Resouces(name="middleSchool") byName
    private School school;
}

AOP 面向切面编程

AOP(Aspect Orient Programming),面向切面编程。面向切面编程是从动态角度考虑程序运行过程。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

AOP 编程术语

  • 切面(Aspect)
    切面泛指交叉业务逻辑。如事务处理、日志处理就可以理解为切面
  • 连接点(JoinPoint)
    连接点指可以被切面织入的具体方法。通常业务接口中的方法均为连接点。
  • 切入点(Pointcut)
    切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。
  • 目标对象(Target)
    目标对象指将要被增强的对象。即包含主业务逻辑的类的对象。
  • 通知(Advice)
    通知表示切面的执行时间,Advice 也叫增强。通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。切入点定义切入的位置,通知定义切入的时间。

AspectJ

对于 AOP 这种编程思想,很多框架都进行了实现。Spring 就是其中之一,可以完成面向切面编程。然而,AspectJ 也实现了 AOP 的功能,且其实现方式更为简捷,使用更为方便,而且还支持注解式开发。所以,Spring 又将AspectJ 的对于 AOP 的实现也引入到了自己的框架中。

AspectJ官网

AspectJ 的通知类型

AspectJ 中常用的通知有五种类型:
(1)前置通知 @Before
(2)后置通知 @AfterReturning
(3)环绕通知 @Around
(4)异常通知 @AfterThrowing
(5)最终通知 @After

AspectJ 的切入点表达式

execution(访问权限类型? 返回值类型 包名类名? 方法名(参数类型和参数个数) 抛出异常类型?)
?表示可选部分
返回值类型和方法名(参数类型和参数数量)是必须要有的
符号意义
*0至多个任意字符
用在方法参数中表示任意多个参数
用在包名后面表示当前包以及其子包路径
+用在类名后面表示当前类及子类
用在接口后表示当前接口及其实现类

例:

  • execution(public * *(…))
    指定切入点为:任意公共方法。
  • execution(* set*(…))
    指定切入点为:任何一个以“set”开始的方法。
  • execution(* *…service.*.*(…))
    指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点

AspectJ 基于注解的 AOP 实现

1、创建maven工程
在这里插入图片描述
2、引入相关依赖
完整依赖

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.why</groupId>
  <artifactId>ch08-Spring-aop</artifactId>
  <version>1.0-SNAPSHOT</version>

  <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>
  </properties>

  <dependencies>
  	<!--测试单元-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!--spring依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <!--AspectJ依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

3、定义业务接口和实现类

public interface SomeService {
    void doSome();
    int doOther(int x,int y);
}

public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome() {
        System.out.println("执行了doSome业务方法");
    }
    @Override
    public int doOther(int x, int y) {
        return x>y ? x : y;
    }
}

4、定义切面类

/**
 *@Aspect:是AspectJ框架的注解表示当前类是切面类
 */
@Aspect
public class MyAspect {

    @Before(value = "execution(* com.why.SomeServiceImpl.doSome(..))")
    public void before(){
        System.out.println("前置通知:在目标方法之前执行,例如输出日志");
    }
}

5、声明目标对象切面类对象

	<!--声明目标类对象-->
    <bean id="target" class="com.why.SomeServiceImpl"></bean>

    <!--声明切面类对象-->
    <bean id="myAspect" class="com.why.MyAspect"></bean>

6、注册 AspectJ 的自动代理

	<!--声明自动代理生成器,创建代理-->
    <aop:aspectj-autoproxy/>

7、测试

@Test
    public void test01(){
        String resource = "applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
        SomeService target = (SomeService) ac.getBean("target");
        target.doSome();
    }

结果

前置通知:在目标方法之前执行,例如输出日志
执行了doSome业务方法

Process finished with exit code 0

JoinPoint 参数

 @Before(value = "execution(* com.why.SomeServiceImpl.doOther(..))")
    public void beforeOther(JoinPoint joinPoint){
   	 	/*不光前置通知的方法,可以包含一个 JoinPoint 类型参数,所有的通知方法均可包含该参数。*/
        System.out.println("连接点的方法定义:"+joinPoint.getSignature());
        System.out.println("连接点方法的参数个数:"+joinPoint.getArgs().length);
        /*
            方法参数信息
            Object[] args = joinPoint.getArgs();
        */
    }

调用doOther方法的执行结果

连接点的方法定义:int com.why.SomeService.doOther(int,int)
连接点方法的参数个数:2
执行了other业务方法

后置通知@AfterReturning注解的 returning 属性

在目标方法执行之后执行。由于是目标方法之后执行,所以可以获取到目
标方法的返回值。该注解的 returning 属性就是用于指定接收方法返回值的变
量名的。

    @AfterReturning(value = "execution(* com.why.SomeServiceImpl.doOther(..))",returning = "result")
    public void afterReturning(Object result){
        if (result != null) {
            Integer i = (Integer) result;
            result = i * 100;
        }
        System.out.println("后置通知:在目标方法执行后的功能增强,如事务的处理");
        System.out.println("较大数的100倍:"+result);
    }

调用doOther方法后的执行结果

执行了other业务方法
后置通知:在目标方法执行后的功能增强,如事务的处理
较大数的100倍:200

Process finished with exit code 0

环绕通知@Around 方法有ProceedingJoinPoint 参数

在目标方法执行之前之后执行。被注解为环绕增强的方法要有返回值Object 类型。并且方法可以包含一个 ProceedingJoinPoint 类型的参数。接口 ProceedingJoinPoint 其有一个 proceed()方法,用于执行目标方法。若目标方法有返回值,则该方法的返回值就是目标方法的返回值。最后,环绕增强
方法将其返回值返回。该增强方法实际是拦截了目标方法的执行。

//接口方法
String doFirst(String name,int age);

//接口方法实现类
@Override
 public String doFirst(String name,int age) {
	System.out.println("执行了doFirst业务方法");
    return "doFirst";
}

定义切面

    @Around(value = "execution(* com.why.SomeServiceImpl.doFirst(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        Object object;
        System.out.println("环绕通知:在目标方法之前执行,如输出日志");
        //执行目标方法
        object = pjp.proceed();
        System.out.println("环绕通知:在目标方法之后执行,如事务处理");
        System.out.println(object.toString());
        return object;
    }

异常通知@AfterThrowing注解的 throwing 属性

在目标方法抛出异常后执行。该注解的 throwing 属性用于指定所发生的异常类对象。当然,被注解为异常通知的方法可以包含一个参数 Throwable,参数名称为 throwing 指定的名称,表示发生的异常对象。

	@AfterThrowing(value = "execution(public * com.why.SomeServiceImpl.doSecond(..))",throwing = "ex")
    public void afterThrowing(Throwable ex){
        /*
        异常通知可以做什么?
        把异常发生的时间、地点、原因记录到数据库,日志文件等等
        可以在异常发生时,把异常信息通过邮件、短信发送给开发人员
        */
        System.out.println("异常通知:在目标方法抛出异常执行,异常原因:"+ex.getMessage());
    }
异常通知:在目标方法抛出异常执行,异常原因:/ by zero

java.lang.ArithmeticException: / by zero

Process finished with exit code -1

最终通知@After

无论目标方法是否抛出异常,该增强均会被执行。

	@After("execution(public * com.why.SomeServiceImpl.doSecond())")
    public void after(){
        System.out.println("最终通知,总是会执行");
    }

@Pointcut 定义切入点

当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维护均较为麻烦。AspectJ 提供了@Pointcut 注解,用于定义 execution 切入点表达式。其用法是,将@Pointcut 注解在一个方法之上,以后所有的 execution 的value 属性值均可使用该方法名作为切入点。代表的就是@Pointcut 定义的切入点。这个使用@Pointcut 注解的方法一般使用 private 的标识方法,即没有实际作用的方法。

	@After("myPoint()")
    public void after(){
        System.out.println("最终通知,总是会执行");
    }
	/**
    * @Pointcut:用来定义和管理切面点,简化切入点的定义
    */
    @Pointcut(value = "execution(public * com.why.SomeServiceImpl.doSecond())")
    public void myPoint(){
        //无需写代码
    }

Spring集成Mybatis

将 MyBatis 与 Spring 进行整合,主要解决的问题就是将SqlSessionFactory 对象交由 Spring 来管理。只需要将SqlSessionFactory 的对象生成器 SqlSessionFactoryBean 注册在 Spring 容器中,再将其注入给 Dao 的实现类即可完成整合。
1、准备数据库表

create table student
(
	id int not null,
	name varchar(10) null,
	sex varchar(2),
	age int null,
	constraint student_pk primary key (id)
);

2、创建maven工程
在这里插入图片描述
3、导入依赖

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.1</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.1</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.9</version>
    </dependency>
    <!--阿里的连接池-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.12</version>
    </dependency>
  </dependencies>

  <build>
    <resources>
      <resource>
        <directory>src/main/java</directory>
        <includes>
        <include>**/*.properties</include>
        <include>**/*.xml</include>
      </includes>
      <filtering>false</filtering>
    </resource>
   </resources>
   <plugins>
     <plugin>
       <artifactId>maven-compiler-plugin</artifactId>
       <version>3.1</version>
       <configuration>
         <source>1.8</source>
         <target>1.8</target>
       </configuration>
     </plugin>
     </plugins>
  </build>

4、定义实体类、dao接口

//实体类
public class Student {
    private int id;
    private String name;
    private char sex;
    private int age;
    /*getter and setter*/
    /*toString()*/
}
//StudentDao接口
public interface StudentDao {
    int insertStudent(Student student);
    List<Student> selectStudent();
}

5、定义mapper映射文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.why.dao.StudentDao">
    <insert id="insertStudent" parameterType="com.why.Student">
        insert into t_student(name,sex,age) value (#{name},#{sex},#{age})
    </insert>

    <select id="selectStudent" resultType="com.why.Student">
        select name,sex,age from t_student
    </select>
</mapper>

6、定义 Service 接口和实现类

public interface StudentService {
    int addStudent(Student student);
    List<Student> queryStudent();
}

public class StudentServiceImpl implements StudentService {
    private StudentDao studentDao;
    public void setStudentDao(StudentDao studentDao) {
        this.studentDao = studentDao;
    }
    @Override
    public int addStudent(Student student) {
        int rows = studentDao.insertStudent(student);
        return rows;
    }
    @Override
    public List<Student> queryStudent() {
        List<Student> students = studentDao.selectStudent();
        return students;
    }  
}

7、定义 MyBatis 主配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
    	<!--日志:将sql语句打印到控制台-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    
    <mappers>
        <!--引入mapper的第一种方式-->
        <!--<mapper resource="com/why/dao/StudentDao.xml"/>-->
        <!--引入mapper的第二种方式-->
        <package name="com.why.dao"/>
    </mappers>
</configuration>

8、修改 Spring 配置文件

8.1、数据源的配置
Druid 是阿里的开源数据库连接池。是 Java 语言中最好的数据库连接池。Druid 能够提供强大的监控和扩展功能。
Druid官方网址

    <!--声明数据源DataSource:阿里druid连接池-->
    <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url" value="jdbc:mysql://localhost:3306/whynode"/>
        <property name="username" value="root"/>
        <property name="password" value="xxxxx"/>
    </bean>

8.2、注册 SqlSessionFactoryBean

    <!--声明sqlSessionFactoryBean-->
    <bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="myDataSource"/>
        <property name="configLocation" value="classpath:mybatis.xml"/>
    </bean>

8.3、定义 Mapper 扫描配置器 MapperScannerConfigurer

    <!--声明MapperScannerConfigurer,创建Dao代理对象,循环basePackage包中所有的接口
    调用sqlSession的getMapper方法创建Dao代理对象-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="factory"/>
        <property name="basePackage" value="com.why.dao"/>
    </bean>

向 Service 注入接口名

    <bean id="studentService" class="com.why.Service.StudentServiceImpl">
        <property name="studentDao" ref="studentDao"/>
    </bean>

完整的Spring配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--声明数据源DataSource:阿里druid连接池-->
    <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url" value="jdbc:mysql://localhost:3306/whynode"/>
        <property name="username" value="root"/>
        <property name="password" value="xxxx"/>
    </bean>
    <!--声明sqlSessionFactoryBean-->
    <bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="myDataSource"/>
        <property name="configLocation" value="classpath:mybatis.xml"/>
    </bean>
    <!--声明MapperScannerConfigurer,创建Dao代理对象,循环basePackage包中所有的接口
    调用sqlSession的getMapper方法创建Dao代理对象-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="factory"/>
        <property name="basePackage" value="com.why.dao"/>
    </bean>

    <bean id="studentService" class="com.why.Service.StudentServiceImpl">
        <property name="studentDao" ref="studentDao"/>
    </bean>
</beans>

从属性文件读取数据库连接信息

在resources目录下创建jdbc.properties文件

jdbc.driver = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/whynode
jdbc.user = root
jdbc.password = why0417

Spring 配置文件从属性文件中读取数据时,需要在的 value属性中使用${ },将在属性文件中定义的 key 括起来,以引用指定属性的值。

	<!--
        引入属性配置文件 spring-context.xsd
        location:指定属性配置问价位置,使用classpath表示类路径
    -->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--声明数据源DataSource:阿里druid连接池-->
    <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

测试

@Test
public void Test01(){
	String config = "applicationContext.xml";
	ApplicationContext act = new ClassPathXmlApplicationContext(config);
	StudentDao studentDao = (StudentDao) act.getBean("studentDao");
    Student student = new Student();
    student.setName("张三");
	student.setSex('男');
	student.setAge(20);
    studentDao.insertStudent(student);
}

Spring事务

使用spring的事务管理器,管理不同数据库访问技术的事务处理。 开发人员只需要掌握spring的事务处理一个方案, 就可以实现使用不同数据库访问技术的事务管理。

事务管理器接口

事务管理器是 PlatformTransactionManager 接口对象。其主要用于完成事务的提交、回滚,及获取事务的状态信息。PlatformTransactionManager 接口有两个常用的实现类:

  • DataSourceTransactionManager:使用 JDBC 或 MyBatis 进行数据库操作时使用。
  • HibernateTransactionManager:使用 Hibernate 进行持久化数据时使用。

事务定义接口

事务定义接口 TransactionDefinition 中定义了事务描述相关的三类常量:事务隔离级别、事务传播行为、事务默认超时时限,及对它们的操作

定义了五个事务隔离级别常量

这些常量均是以 ISOLATION_开头。即形如 ISOLATION_XXX。

  • DEFAULT : 采 用 DB 默 认 的 事 务 隔 离 级 别 。 MySql 的默认为REPEATABLE_READ; Oracle 默认为 READ_COMMITTED。
  • READ_UNCOMMITTED:读未提交。未解决任何并发问题。
  • READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。
  • REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读
  • SERIALIZABLE:串行化。不存在并发问题。

定义了七个事务传播行为常量

所谓事务传播行为是指,处于不同事务中的方法在相互调用时,执行期间事务的维护情况。如,A 事务中的方法 doSome()调用 B 事务中的方法doOther(),在调用执行期间事务的维护情况,就称为事务传播行为。事务传播行为是加在方法上的。
事务传播行为常量都是以 PROPAGATION_ 开头,形如PROPAGATION_XXX。

  • PROPAGATION_REQUIRED
    指定的方法必须在事务内执行。若当前存在事务,就加入到当前事务中;若当前没有事务,则创建一个新事务。这种传播行为是最常见的选择,也是Spring 默认的事务传播行为
  • PROPAGATION_REQUIRES_NEW
    总是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务执行完毕
  • PROPAGATION_SUPPORTS
    指定的方法支持当前事务,但若当前没有事务,也可以以非事务方式执行
  • PROPAGATION_MANDATORY
  • PROPAGATION_NESTED
  • PROPAGATION_NEVER
  • PROPAGATION_NOT_SUPPORTED

定义了默认事务超时时限

常量 TIMEOUT_DEFAULT 定义了事务底层默认的超时时限,sql 语句的执行时长。事务的超时时限起作用的条件比较多,且超时的时间计算点较复杂。该值一般就使用默认值即可。

使用 Spring 的事务注解管理事务

@Transactional 的所有可选属性如下所示:

  • propagation:用于设置事务传播属性。该属性类型为 Propagation 枚举,默认值为 Propagation.REQUIRED。
  • isolation:用于设置事务的隔离级别。该属性类型为 Isolation 枚举,默认值为 Isolation.DEFAULT。
  • readOnly:用于设置该方法对数据库的操作是否是只读的。该属性为boolean,默认值为 false。
  • timeout:用于设置本操作与数据库连接的超时时限。单位为秒,类型为 int,默认值为-1,即没有时限。
  • rollbackFor:指定需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
  • rollbackForClassName:指定需要回滚的异常类类名。类型为 String[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
  • noRollbackFor:指定不需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
  • noRollbackForClassName:指定不需要回滚的异常类类名。类型为 String[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组

@Transactional 若用在方法上,只能用于 public 方法上

实现注解的事务步骤

1、声明事务管理器

	<!--声明事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="myDataSource"/>
    </bean>

2、开启注解驱动

	<!--选http://www.springframework.org/schema/tx-->
    <tx:annotation-driven transaction-manager="transactionManager"/>

3、@Transactional注解

@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
readOnly = false, timeout = 20,
rollbackFor =
{NullPointerException.class,NotEnougthException.class})
public void buy(Integer goodsId, Integer num) { }

使用 AspectJ 的 AOP 配置管理事务

1、加入依赖

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-aspects</artifactId>
	<version>5.2.5.RELEASE</version>
</dependency>

2、在容器中添加事务管理器

	<!--声明事务管理器-->
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="myDataSource"/>
    </bean>

3、配置事务通知

   <tx:advice id="buyAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT" rollback-for="java.lang.NullPointerException"/>
            <tx:method name="*" propagation="SUPPORTS"/>
            <!--...-->
        </tx:attributes>
    </tx:advice>

4、配置增强器

<!--声明切入点表达式: 表示那些包中的类,类中的方法参与事务-->
    <aop:config>
        <!--声明切入点表达式-->
        <aop:pointcut id="servicePointcut" expression="execution(* com.why.*(..))" />
        <!--关联切入点表达式和事务通知-->
        <aop:advisor advice-ref="buyAdvice" pointcut-ref="servicePointcut" />
    </aop:config>

Spring-Web项目

在 Web 项目中使用 Spring 框架,首先要解决在 web 层(这里指Servlet)中获取到 Spring 容器的问题。只要在 web 层获取到了 Spring 容器,便可从容器中获取到 Service 对象。
1、创建web项目
在这里插入图片描述
2、将整合mybatis项目中以下内容复制到当前项目中:
(1) Service 层、Dao 层全部代码
(2) 配置文件 applicationContext.xml 及 jdbc.properties,mybatis.xml
(3) pom.xml

3、在pom.xml文件中添加下面依赖

    <!-- servlet依赖 -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>
    <!-- jsp依赖 -->
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.2.1-b03</version>
      <scope>provided</scope>
    </dependency>

4、web.xml 注册 Servlet

 <servlet>
    <servlet-name>myServlet</servlet-name>
    <servlet-class>com.why.controller.MyServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>myServlet</servlet-name>
    <url-pattern>/test</url-pattern>
  </servlet-mapping>

5、定义Servlet

public class MyServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //接收请求参数
        String stuName = request.getParameter("name");
        String stuAge = request.getParameter("age");
        //创建spring容器,获取Service对象
        String configLocation = "applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(configLocation);
        System.out.println(ac);
        StudentService studentService = (StudentService)ac.getBean("studentService");
        //添加对象
        Student student = new Student();
        student.setName(stuName);
        student.setAge(Integer.parseInt(stuAge));
        studentService.addStudent(student);
        //显示处理结果的jsp
        request.getRequestDispatcher("/result.jsp").forward(request,response);
    }
}

6、定义index和result页面

<!--index页面-->
<body>
    <form action="/test" method="post">
        姓名:<input type="text" name="name"><br>
        年龄:<input type="text" name="age"><br>
              <input type="submit" value="注册">
    </form>
</body>

<!--result页面-->
<body>
    注册成功
</body>

7、配置tomcat并且启动
在这里插入图片描述
以上程序存在的问题:表单提交三次 我们发现就new了3个srping容器。但是对于一个应用来说,只需要一个 Spring 容器即可。所以,将 Spring 容器的创建语句放在 Servlet 的 doGet()或 doPost()方法中是有问题的。

10-Mar-2022 20:35:56.445 淇℃伅 [http-nio-8080-exec-7] com.alibaba.druid.support.logging.JakartaCommonsLoggingImpl.info {dataSource-4} inited
org.springframework.context.support.ClassPathXmlApplicationContext@187ec5a8, started on Thu Mar 10 20:35:56 CST 2022
10-Mar-2022 20:35:58.397 淇℃伅 [http-nio-8080-exec-8] com.alibaba.druid.support.logging.JakartaCommonsLoggingImpl.info {dataSource-5} inited
org.springframework.context.support.ClassPathXmlApplicationContext@da69797, started on Thu Mar 10 20:35:58 CST 2022
10-Mar-2022 20:36:00.513 淇℃伅 [http-nio-8080-exec-9] com.alibaba.druid.support.logging.JakartaCommonsLoggingImpl.info {dataSource-6} inited
org.springframework.context.support.ClassPathXmlApplicationContext@34a5a436, started on Thu Mar 10 20:36:00 CST 2022

那么如何保证创建容器时唯一的呢?

使用Spring 的监听器 ContextLoaderListener

对于 Web 应用来说,ServletContext 对象是唯一的,一个 Web 应用,只有一个 ServletContext 对象,该对象是在 Web 应用装载时初始化的。若将Spring 容器的创建时机,放在 ServletContext 初始化时,就可以保证 Spring容器的创建只会执行一次,也就保证了 Spring 容器在整个应用中的唯一性。

1、引入依赖

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-web</artifactId>
	<version>5.2.5.RELEASE</version>
</dependency>

2、注册监听器 ContextLoaderListener
若要在 ServletContext 初始化时创建 Spring 容器,就需要使用监听器接口 ServletContextListener 对 ServletContext 进行监听。在 web.xml 中注册该监听器。

  <!--自定义容器使用的配置文件路径、
      context-param:叫做上下文参数,给监听器提供参数的
      如果不想把applicationContext.xml放在WEB-INF下就必须配置这个-->
  <context-param>
    <!--固定写法-->
    <param-name>contextConfigLocation</param-name>
    <!--配置文件路径-->
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  
  <!--默认监听器:创建容器对象时,读取配置文件: [/WEB-INF/applicationContext.xml]-->
  <listener>
    <!-- 创建容器对象,并将容器对象放入到了 ServletContext的空间中。 -->
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

3、获取 Spring 容器对象
在 Servlet 中获取容器对象的常用方式有两种:

直接从 ServletContext 中获取

String attr = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
WebApplicationContext ac = (WebApplicationContext)this.getServletContext().getAttribute(attr);
//或者这种
//WebApplicationContext ac = (WebApplicationContext)request.getServletContext().getAttribute(attr);

通过 WebApplicationContextUtils 获取

WebApplicationContext ac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());

再提交三次表单

Root WebApplicationContext, started on Thu Mar 10 21:03:33 CST 2022
Root WebApplicationContext, started on Thu Mar 10 21:03:33 CST 2022
Root WebApplicationContext, started on Thu Mar 10 21:03:33 CST 2022
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值