Spring入门

第1章 Spring概述

sping简述

  • 一个轻量级框架,核心技术是ioc,aop,针对接口编程,实现解耦合。
  • 一个容器,储存java对象的容器,需要做的是把对象放入到容器中
  • 方便集成各种优秀框架

Spring 不排斥各种优秀的开源框架,相反 Spring 可以降低各种框架的使用难度,Spring提供了对各种优秀框架(如 Struts,Hibernate、MyBatis)等的直接支持。简化框架的使用。Spring 像插线板一样,其他框架是插头,可以容易的组合到一起。需要使用哪个框架,就把这个插头放入插线板。不需要可以轻易的移除。

怎么使用spring

  1. spring是一个容器,把项目中用的对象放入到容器中
  2. 让容器完成对象的创建,对象之间关系的管理(属性赋值)
  3. 我们在程序中从容器中获取 要使用的对象

不放入到spring容器中的对象

  1. 实体类对象,实体类数据来自数据库的
  2. servlet, listener, filter等由Tomcat管理的

什么样的对象放入容器中

  • dao类,service类,controller类,工具类
    1. xml配置文件,使用<bean>
    2. 注解
  • spring中的对象默认都是单例的,在容器中叫这个名称的对象只有一个

使用spring框架的步骤

  1. 加入依赖

    <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-context</artifactId>
       <version>5.2.5.RELEASE</version>
     </dependency>
    
  2. 创建类:接口,实现类,没有接口的类

    public interface SomeService {
     void doSome();
    }
    public class SomeServiceImpl  implements SomeService{
     @Override
     public void doSome() {
         System.out.println("SomeServiceImpl 启动");
     }
    }
    
  3. 创建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">
     <!--xsd 是约束文件的扩展名  xsd 的约束文件功能强,验证比较全面-->
    
     <!--声明java对象交给Spring创建和管理
           class:类的全限定名称,不能是接口(Spring使用反射创建对象)
           id:自定义的对象名称,要求是唯一值。表示在Spring中的对象名称,
               通过这个名称可以从Spring中找到对象。获取对象
            对象是放入到Spring的容器(Map<id,对象>)
     -->
     <bean id="someService" class="com.test.service.SomeServiceImpl"></bean>
    
     <!--<bean>等同于SomeService someService = new SomServiceImpl();-->
    </beans>
    
  4. 我们使用容器中的对象,通过ApplicationContext接口和他的实现类ClassPathXmlApplcactionContext的方法getBean()

     @Test
     public void test(){
         System.out.println( "Hello World!" );
         //定义Spring的配置文件
         String config="applicationContext.xml";
    
         //创建Spring的容器对象,根据Spring配置文件的位置,使用接口的不同实现类
         //如果Spring配置文件位置是在类路径下(classpath)
         ApplicationContext ctx= new ClassPathXmlApplicationContext(config);
    		//根目录下    ApplicationContext ctx = new FileSystemXmlApplicationContext(config);
    
         //创建容器对象时,就把配置文件的所有对象都创建出来,放到容器中,需要时再取出来
         SomeService service= (SomeService) ctx.getBean("someService");
         //调用业务的方法
         service.doSome();
     }
    
    

第2章 IOC 控制反转

控制反转(IoC,Inversion of Control),是一个概念,是一种思想。指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。通过容器实现对象的创建,属性赋值,依赖的管理。

  • 描述:把对象的创建,赋值,管理工作都交给代码之外的容器实现,也就是对象的创建是有其它外部资源完成

  • 控制: 创建对象,对象的属性赋值,对象之间的关系管理。

  • 反转: 把原来的开发人员管理,创建对象的权限转移给代码之外的容器实现。 由容器代替开发人员管理对象。创建对象,给属性赋值。

  • 正转:由开发人员在代码中,使用new 构造方法创建对象, 开发人员主动管理对象。

    	public static void main(String args[]){
              Student student = new Student(); // 在代码中, 创建对象。--正转。
    	 }
    

ioc技术实现使用的DI(依赖注入)

  • Spring 容器是一个超级大工厂,负责创建、管理所有的 Java 对象,这些 Java 对象被称为 Bean。Spring 容器管理着容器中 Bean 之间的依赖关系,Spring 使用“依赖注入”的方式来管理 Bean 之间的依赖关系。使用 IoC 实现对象之间的解耦和。
    开发人员在项目中只需要提供对象的名称, 对象的创建,查找,赋值都由容器内部自己实现
    (spring使用di的技术, 底层使用的是反射机制)

  • 为什么要使用 ioc : 目的就是减少对代码的改动, 也能实现不同的功能。 实现解耦合

基于xml的DI

set注入

  • spring调用类的set方法实现属性赋值
  1. 简单类型的set注入<property name="属性名" value="属性的值" />

     <bean id="student" class="com.text.pack01.Student" >
         <!--通过set方法注入-->
         <property name="name" value="lisi"/>
         <property name="age"  value="20"/>
    
  2. 引用类型的set注入<proper name="属性名" ref="bean的id" />

     <property name="school" ref="school"/>
     </bean>
    

构造注入

  • spring调用有参数构造方法
  1. <constructor-arg>的name属性,name表示构造方法的形参名
  2. <constructor-arg>的index属性,表示构造方法形参的位置,从0开始

自动注入

  1. byName: 按名称注入, java类中引用类型的**【属性名】和spring容器中bean的【id】**一样,数据类型一样。这样的bean赋值给引用类型
    <bean id="student" class="com.text.pack01.Student" autowire="byName">

  2. byType:按类型注入,java类中引用类型的是**【数据类型】和spring容器中bean的【class】**是同源关系的,这样的bean能够赋值给引用类型

基于注解的DI

常用注解

  1. @Component 创建对象
  2. @Repository 创建dao对象,用来访问数据库的
  3. @Service 创建Service对象, 处理业务逻辑的,可以有事务功能
  4. @Controller 创建控制器对象的, 接收请求,显示处理结果的
  5. @Value 简单类型的属性赋值
  6. @Autowired spring框架中引用类型的赋值注解, 支持byName,byType,默认是byType
  7. @Resource jdk中的注解,spring框架提供了对这个注解的功能支持,使用自动注入给引用类型赋值,
    支持byName,byType,默认是byName

注解的使用步骤:

  1. 加入依赖 :spring-context, 间接加入spring-aop

  2. 在类中加入注解

    //使用注解创建对象 默认类的第一个字母小写
    @Component
    public class Student {
     //可以在属性上,也可以在set方法上
     @Value("李四")
     private String name;
     @Value("20")
     private int age;
    
     //@Resource  设置byType @Resource(name=byType)
    
     //@Autowired 默认byType  用byName加@Qualifier注解
     //@Autowired(required=true) 默认true 注入失败报错,required=false 注入失败值为null。
     @Autowired
     @Qualifier("mySchool")//设置byName
     private School school;
    ......
    
  3. 在spring的配置文件中,加入组件扫描器的标签 <context:component-scan base-package="包名">

ioc能实现解耦合

ioc能够实现业务对象之间的解耦合, 例如service和dao对象之间的解耦合。

第3章 AOP 面向切面编程

动态代理

动态代理是指, 程序在整个运行过程中根本就不存在目标类的代理类,目标对象的代理
对象只是由代理生成工具(不是真实定义的类)在程序运行时由 JVM 根据反射等机制动态
生成的。代理对象与目标对象的代理关系在程序运行时才确立。

jdk动态代理

使用jdk反射包中的类实现创建代理对象的功能
要求:目标类必须实现接口

public class DaiLi implements InvocationHandler {
	//目标对象
    private Object target;
    public DaiLi(Object target) {
        this.target = target;
    }
    //代理类的业务方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        SqlSession session=null;
        Object obj=null;

        try {
            session= SqlSessionUtil.getSession();
            //处理业务逻辑

            //真实对象的业务方法
            obj=method.invoke(target,args);

            //处理业务逻辑完毕
            session.commit();
        } catch (Exception e) {
            //异常回滚
            session.rollback();
            e.printStackTrace();
        }finally {
            SqlSessionUtil.closeSession(session);
        }
        return obj;
    }

    //获取代理对象
    public Object getProxy(){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }
}

cglib动态代理

使用第三方的工具库,实现代理对象的创建
要求:目标类必须能够继承,不能是final
原理:就是继承,子类就是代理

动态代理作用

  1. 在目标类不修改源代码的情况下,增加功能
  2. 减少重复的代码
  3. 专注业务功能的实现
  4. 解耦合: 业务功能和日志,事务这些非业务功能的耦合

AOP概述

  • 面向切面编程, 基于动态代理的,可以使用jdk,cglib两种代理方式。
    Aop就是动态代理的规范化, 把动态代理的实现步骤,方式都定义好了,
    让开发人员用一种统一的方式,使用动态代理。
  • 怎么理解面向切面编程 ?
    1)需要在分析项目功能时,找出切面。
    2)合理的安排切面的执行时间(在目标方法前, 还是目标方法后)
    3)合理的安全切面执行的位置,在哪个类,哪个方法增加增强功能

AOP 编程术语(掌握)

  1. aspect:切面,表示给业务方法增加的功能,一般日志输出,事务,权限检查等是切面
  2. JoinPoint:连接点,连接点指可以被切面织入的具体方法。通常业务接口中的方法均为连接点。
  3. pointcut:切入点,是一个或多个JoinPoint的集合,表示切面功能执行的位置
  4. Target:目标对象,目标对象指将要被增强的对象 ,即包含主业务逻辑的类的对象 。
  5. advice:通知,也叫增强,表示切面执行的时间,在方法前,方法后等等

什么时候考虑使用AOP技术

  1. 当你要给一个系统中存在的类修改功能,但是原有类的功能不完善,但是你还有源代码,使用aop就增加功能
  2. 你要给项目中的多个类,增加一个相同的功能,使用aop
  3. 给业务方法增加事务, 日志输出

AOP的实现框架

  1. spring实现了aop, 实现方式是接口
  2. aspectj框架 (一个优秀面向切面的框架,它扩展了 Java 语言,提供了强大的切面实现。)
    1. 使用注解可以实现aop的功能
    2. 使用xml配置文件中的标签实现aop功能

aspectj框架的使用

表示切面位置的切入点表达式

execution(访问修饰符 返回值 包名.类名.方法名称(方法的参数) 异常)
切入点表达式

表示切面的执行时间,使用的通知注解

  1. @Before 前置通知 在目标方法之前先执行切面的功能

    @Aspect//定义切面类
    public class Advice {
     //增强功能的方法
     @Before(value = "execution(public void com.test.service.pack01.OneServiceImpl.sayHi())")
     //属性value表示切面执行的位置
     public void Before(){
         System.out.println("在目标方法执行之前,例如输出日志");
     }
    
  2. @AfterReturning 后置通知 在目标方法之后执行的,能够获取到目标方法的返回值

    /* returning是目标方法的返回值,命名与通知方法的形参名一致
         相当于 Object obj = sayHi(String,Integer)
           AfterReturning(obj)
         可以修改这个返回值,影响最后调用的结果
    */
     @AfterReturning(value = "execution(* *..OneServiceImpl.sayHi(String,Integer))",returning = "obj")
     public void AfterReturning(JoinPoint jp,Object obj){
         /*指定通知方法中的参数: JoinPoint
           JoinPoint(连接点)代表业务方法,要加入切面功能的业务方法 如sayHi
             作用是:可以在通知方法中获取方法执行时的信息,例如方法的名称,方法的实参。
             如果你的切面功能中需要用到方法的信息,就加入JoinPoint。
             这个JointPoint参数的值是由框架赋予,必须是第一个位置的参数
    
         System.out.println("方法的签名(定义)="+jp.getSignature());
         System.out.println("方法的名称 ="+jp.getSignature().getName());
         //获取方法的实参
         Object args[] = jp.getArgs();
         for (Object arg:args
              ) {
    	//可以用来判断目标方法的实参是否是想要的,然后再执行功能
             System.out.println("参数="+arg);
         }*/
         if(obj!=null){
             System.out.println("见面时间"+new Date());
         }
     }
    
  3. @Around 环绕通知 在目标方法前和后都能增强功能,控制目标方法的的访问,修改返回值

     /*
         @Around: 环绕通知
           属性:value 切入点表达式
           位置;在方法的定义上面
          特点:
           1.它是功能最强的通知
           2.在目标方法的前和后都能增强功能
           3.控制目标方法是否被调用执行
           4.修改原来的目标方法的执行结果,影响最后调用的结果
    
          环绕通知等同与jdk动态代理的,InvocationHandler接口
             返回值就是目标方法的执行结果,可以被修改。
      */
     @Around(value = "execution(* *..OneServiceImpl.sayHi(String,Integer))")
     public Object Around(ProceedingJoinPoint pjp) throws Throwable {
         Object obj=null;
    
         //目标方法前加入功能
         System.out.println("见面时间"+new Date());
         //通过ProceedingJoinPoint.proceed对目标方法的调用
         pjp.proceed();//method.invoke();
    
         //目标方法后加入功能
         System.out.println("提交事务");
         return obj;
     }
    
  4. @AfterThrowing 异常通知 在目标方法抛出异常后执行的通知

     //throwing属性 用于指定发生的异常对象
     //相当于try{目标方法}catch(){ myAfterThrowing(Throwable ex) }
     @AfterThrowing(value = "execution(* *..OneServiceImpl.sayHi(String,Integer))",throwing = "ex")
     public void myAfterThrowing(Throwable ex){
         //记录异常信息
         System.out.println("异常通知:异常发生时执行"+ex.getMessage());
     }
    
  5. @After 最终通知 总是会被执行的代码

     //相当于 try{}catch(){}finally{ myAfter() }
     @After(value="execution(* *..OneServiceImpl.sayHi(String,Integer))")
     public void myAfter(){
         System.out.println("最终通知");
     }
    
  6. @Pointcut 定义和管理切入点的辅助注解

       /*@Pointcut : 定义和管理切入点,如果你的项目中有多个切入点表达式是重复的,可以复用的。
                         可以使用@Pointcut,统一管理切入点表达式。
            属性: value 切入点表达式
            位置: 在自定义的方法上面
           特点:
            当使用@Pointcut定义在一个方法的上面,此时这个方法就是切入点表达式的别名
            其他的通知中,value就可以使用这个方法代替切入点表达式。
      */
     @Pointcut(value = "execution(* *..OneServiceImpl.sayHi(String,Integer))")
     private void pointCut(){
         //无需代码
     }
     @After(value="pointCut()")
     public void myAfter(){
         System.out.println("最终通知");
     }
    
  7. 代理的使用方式

    1. 如果目标类有接口,框架使用jdk动态代理,得到service对象是com.sun.proxy.$Proxy10
    2. 如果目标类没有接口,默认使用的cglib动态代理,得到service对象是com.test.service.pack04.OneServiceImpl$$EnhancerBySpringCGLIB$$eb85df45
    3. 有接口也可以强制使用cglib动态代理
     <!--
         声明自动代理对象:使用aspectj框架的功能,创建目标对象的代理对象。
         创建代理对象时在内存中实现的,修改目标对象的内存中的机构。
         使用目标对象就是被修改后的代理对象
     -->
     <aop:aspectj-autoproxy/>
     <!--
     如果你期望目标类有接口,但使用cglib代理
     proxy-target-class="true" 告诉框架使用cglib动态代理
     <aop:aspectj-autoproxy proxy-target-class="true"/>
     -->
    
  8. maven依赖

    <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-aspects</artifactId>
       <version>5.2.5.RELEASE</version>
     </dependency>
     </dependencies>
    

第4章 Spring集成Mybatis

整合的想法

使用spring的ioc核心技术,把mybatis框架中使用的对象交给spring统一创建和管理。spring是容器,存放你项目中要使用的各种对象, 例如Service对象, Dao对象,工具类对象等等。

mybatis使用步骤

  1. 定义dao接口 ,StudentDao,以及service接口和实现类

    public interface StudentDao {
     int insertStudent(Student student);
     List<Student> selectStudents();
    }
    //service.....
    
  2. 定义mapper文件 StudentDao.xml

    <?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="cn.JInterest.dao.StudentDao">
    	<insert id="insertStudent">
       	 	insert into student values(#{id},#{name},#{email},#{age})
    	</insert>
    
    	<select id="selectStudents" resultType="Student">
       	 	select id,name,email,age from student order by id desc
    	</select>
    </mapper>
    
  3. 定义mybatis的主配置文件 mybatis.xml

<?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:控制mybatis全局行为-->
    <settings>
        <!--设置mybatis输出日志-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <!--设置别名-->
    <typeAliases>
        <!--name:实体类所在的包名
            表示cn.JInterest.domain包中的类名就是别名
            你可以使用Student表示cn.JInterest.domain.Student
        -->
        <package name="cn.JInterest.domain"/>
    </typeAliases>
    <!-- sql mapper(sql映射文件)的位置-->
    <mappers>
        <!--
          name:是包名, 这个包中的所有mapper.xml一次都能加载
        -->
        <package name="cn.JInterest.dao"/>
    </mappers>
</configuration>
  1. 创建dao的代理对象
    StudentDao dao = SqlSession.getMapper(StudentDao.class);
    List<Student> students = dao.selectStudents();

  2. 要使用dao对象,需要使用getMapper()方法,
    怎么能使用getMapper()方法,需要哪些条件
    1)获取SqlSession对象, 需要使用SqlSessionFactory的openSession()方法。
    2)创建SqlSessionFactory对象。 通过读取mybatis的主配置文件,能创建SqlSessionFactory对象
    使用Factory能获取SqlSession,有了SqlSession就能有dao ,目的就是获取dao对象,最后service层调用dao对象

  3. spring整合mabatis后的applicationContext.xml文件
    使用阿里公司的Druid连接池,配置文档:DruidDataSource配置

<?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
    https://www.springframework.org/schema/context/spring-context.xsd">
   
    <!--
       把数据库的配置信息,写在一个独立的文件,编译修改数据库的配置内容
       让spring知道jdbc.properties文件的位置
    -->
 <context:property-placeholder location="classpath:jdbc.properties" />
   
    <!--声明数据源DataSource, 作用是连接数据库的,代替了mybatis的连接方式-->
    <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <!--set注入给DruidDataSource提供连接数据库信息 -->
        <!--
            使用属性配置文件中的数据,语法 ${key}
        -->
        <property name="url" value="${jdbc.url}" /><!--setUrl()-->
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.passwd}" />
        <property name="maxActive" value="${jdbc.max}" />
 </bean>
   
    <!--声明的是mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory的-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--set注入,把数据库连接池付给了dataSource属性-->
        <property name="dataSource" ref="myDataSource" />
        <!--mybatis主配置文件的位置
           configLocation属性是Resource类型,读取配置文件
           它的赋值,使用value,指定文件的路径,使用classpath:表示文件的位置
        -->
        <property name="configLocation" value="classpath:mybatis.xml" />
 </bean>
   
    <!--
        创建dao对象,使用SqlSession的getMapper(StudentDao.class)
        MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象 。
        org.apache.ibatis.binding.MapperProxy@9cd25ff
    -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--指定SqlSessionFactory对象的id-->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
        <!--指定包名, 包名是dao接口所在的包名。
            MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行
            一次getMapper()方法,得到每个接口的dao对象。
            创建好的dao对象放入到spring的容器中的。 dao对象的默认名称是 接口名首字母小写
        -->
        <property name="basePackage" value="cn.JInterest.dao"/>
 </bean>
   
    <!--声明service-->
    <bean id="studentService" class="cn.JInterest.service.impl.StudentServiceImpl">
        <property name="studentDao" ref="studentDao" />
 </bean>
	</beans>
  1. pom.xml

    <?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>cn.JInterest</groupId>
      <artifactId>Spring_04-mybatis</artifactId>
      <version>1.0-SNAPSHOT</version>
    
    
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
      </properties>
    
      <dependencies>
        <!--单元测试-->
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.11</version>
          <scope>test</scope>
        </dependency>
        <!--spring核心ioc-->
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.2.5.RELEASE</version>
        </dependency>
        <!--做spring事务用到的-->
        <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>
        <!--mybatis依赖-->
        <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis</artifactId>
          <version>3.5.1</version>
        </dependency>
        <!--mybatis和spring集成的依赖-->
        <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis-spring</artifactId>
          <version>1.3.1</version>
        </dependency>
        <!--mysql驱动-->
        <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>8.0.13</version>
        </dependency>
        <!--阿里公司的数据库连接池-->
        <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>druid</artifactId>
          <version>1.1.12</version>
        </dependency>
      </dependencies>
    
      <build>
        <!--目的是把src/main/java目录中的xml文件包含到输出结果中。输出到classes目录中-->
        <resources>
          <resource>
            <directory>src/main/java</directory><!--所在的目录-->
            <includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
              <include>**/*.properties</include>
              <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
          </resource>
        </resources>
      </build>
    </project>
    

交给spring的mybatis对象

  1. 数据源DataSource,使用阿里公司的Druid连接池

  2. SqlSessionFactory对象, 使用的SqlSessionFactoryBean在内部创建的SqlSessionFactory

  3. Dao代理对象,使用的MapperScannConfigure,在这个类的内部,调用getMapper(),创建接口的Dao对象

第5章 Spring事务

什么是事务

  • 讲mysql的时候,提出了事务。 事务是指一组sql语句的集合, 集合中有多条sql语句可能是insert , update ,select ,delete,
    我们希望这些多个sql语句都能成功,或者都失败, 这些sql语句的执行是一致的,作为一个整体执行。

在什么时候想到使用事务

  • 当我的操作,涉及得到多个表,或者是多个sql语句的insert,update,delete。需要保证这些语句都是成功才能完成我的功能,
    或者都失败,保证操作是符合要求的。

  • 在java代码中写程序,控制事务,此时事务应该放在那里呢?
    service类的业务方法上,因为业务方法会调用多个dao方法,执行多个sql语句

通常怎么处理事务

  • jdbc访问数据库,处理事务 Connection conn ; conn.commit(); conn.rollback();
  • mybatis访问数据库,处理事务, SqlSession.commit(); SqlSession.rollback();
  • hibernate访问数据库,处理事务, Session.commit(); Session.rollback();

问题中事务的处理方式,有什么不足

  1. 不同的数据库访问技术,处理事务的对象,方法不同,需要了解不同数据库访问技术使用事务的原理
  2. 掌握多种数据库中事务的处理逻辑。什么时候提交事务,什么时候回顾事务
  3. 处理事务的多种方法。
  • 总结: 就是多种数据库的访问技术,有不同的事务处理的机制,对象,方法。

怎么解决不足

  • spring提供一种处理事务的统一模型, 能使用统一步骤,方式完成多种不同数据库访问技术的事务处理。
    使用spring的事务处理机制,可以完成mybatis访问数据库的事务处理
    使用spring的事务处理机制,可以完成hibernate访问数据库的事务处理。

处理事务,需要怎么做,做什么

spring处理事务的模型,使用的步骤都是固定的。把事务使用的信息提供给spring就可以了

  1. 事务内部提交,回滚事务,使用的事务管理器对象,代替你完成commit,rollback事务管理器是一个接口和他的众多实现类。

    • 接口:PlatformTransactionManager ,定义了事务重要方法 commit ,rollback

    • 实现类:spring把每一种数据库访问技术对应的事务处理类都创建好了。
      mybatis访问数据库---spring创建好的是DataSourceTransactionManager
      hibernate访问数据库----spring创建的是HibernateTransactionManager

    • 怎么使用:你需要告诉spring 你用是那种数据库的访问技术,怎么告诉spring呢?
      声明数据库访问技术对于的事务管理器实现类, 在spring的配置文件中使用<bean>声明就可以了
      例如,你要使用mybatis访问数据库,你应该在xml配置文件中
      <bean id=“xxx" class="...DataSourceTransactionManager">

  2. 你的业务方法需要什么样的事务,说明需要事务的类型。
    说明方法需要的事务:

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

    2. 事务的超时时间: 表示一个方法最长的执行时间,如果方法执行时超过了时间,事务就回滚。
      单位是秒, 整数值, 默认是 -1. (涉及因素多,一般默认)

    3. 事务的传播行为 : 控制业务方法是不是有事务的, 是什么样的事务的。
      7个传播行为,表示你的业务方法调用时,事务在方法之间是如果使用的。
      掌握以下三个:

      • PROPAGATION_REQUIRED(指定的方法必须在事务内执行。若当前存在事务,就加入到当前事务中;若当前没有事
        务,则创建一个新事务。)
        PROPAGATION_REQUIRED
      • PROPAGATION_REQUIRES_SUPPORTS(指定的方法支持当前事务,但若当前没有事务,也可以以非事务方式执行。)
        PROPAGATION_REQUIRES_NEW
      • PROPAGATION_REQUIRES_NEW(总是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务执行完毕。)
        PROPAGATION_REQUIRES_NEW
  3. spring提交事务,回滚事务的时机

    1. 当你的业务方法,执行成功,没有异常抛出,当方法执行完毕,spring在方法执行后提交事务。事务管理器commit

    2. 当你的业务方法抛出运行时异常或ERROR, spring执行回滚,调用事务管理器的rollback
      运行时异常的定义: RuntimeException 和他的子类都是运行时异常, 例如NullPointException , NumberFormatException

    3. 当你的业务方法抛出非运行时异常, 主要是受查异常时,提交事务
      受查异常:在你写代码中,必须处理的异常。例如IOException, SQLException

spring框架中提供的事务处理方案

  1. 适合中小项目使用的, 注解方案。
    spring框架自己用aop实现给业务方法增加事务的功能, 使用**@Transactional注解增加事务。@Transactional注解是spring框架自己注解,放在public方法的上面,表示当前方法具有事务。
    可以给注解的属性赋值,表示具体的
    隔离级别,传播行为,异常信息**等等

    • 使用**@Transactional**的步骤:
    1. 需要声明事务管理器对象
      <bean id="xx" class="DataSourceTransactionManager">

    2. 开启事务注解驱动, 告诉spring框架,我要使用注解的方式管理事务。
      <tx:annotation-driven transaction-manager="transactionManager" />
      spring使用aop机制,创建**@Transactional**所在的类代理对象,给方法加入事务的功能。
      在你的业务方法执行之前,先开启事务,在业务方法之后提交或回滚事务,使用aop的环绕通知

      /*
      rollbackFor:表示发生指定的异常一定回滚.
      处理逻辑是:
      1) spring框架会首先检查方法抛出的异常是不是在rollbackFor的属性值中
      2) 如果你的抛出的异常不在rollbackFor列表中,spring会判断异常是不是RuntimeException,
             如果是一定回滚。    
      @Transactional(
            propagation = Propagation.REQUIRED,
            isolation = Isolation.DEFAULT,
            readOnly = false,
            rollbackFor = {
                    NullPointerException.class,  NotEnoughException.class
            }
       )*/
      @Transactional
      @Around("你要增加的事务功能的业务方法名称")
      Object myAround(){
           //开启事务,spring给你开启
      	 try{
      	    buy(1001,10);
      		//spring的事务管理器.commit();
      	 }catch(Exception e){
      		//spring的事务管理器.rollback();
      	 } 
       }
      
  2. 适合大型项目,有很多的类,方法,需要大量的配置事务,使用aspectj框架功能,在spring配置文件中声明类,方法需要的事务。这种方式业务方法和事务配置完全分离。

实现步骤: 都是在xml配置文件中实现。

  1.  要使用的是aspectj框架,需要加入依赖
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-aspects</artifactId>
		<version>5.2.5.RELEASE</version>
	</dependency>
  1. 声明事务管理器对象
<bean id="xx" class="DataSourceTransactionManager">
  1. 声明方法需要的事务类型(配置方法的事务属性【隔离级别,传播行为,超时】)

  2. 配置aop:指定哪些哪类要创建代理。

spring配置:

<!--
数据源配置
.....
.....
-->
    <!--声明式事务处理:和源代码完全分离的-->
    <!--1.声明事务管理器对象-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="myDataSource" />
    </bean>
<!--2.声明业务方法它的事务属性(隔离级别,传播行为,超时时间)
      id:自定义名称,表示 <tx:advice> 和 </tx:advice>之间的配置内容的
      transaction-manager:事务管理器对象的id
-->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
    <!--tx:attributes:配置事务属性-->
    <tx:attributes>
        <!--tx:method:给具体的方法配置事务属性,method可以有多个,分别给不同的方法设置事务属性
            name:1)完整的方法名称,不带有包和类。
			2)方法可以使用通配符,* 表示任意字符
            propagation:传播行为,枚举值
            isolation:隔离级别
            rollback-for:你指定的异常类名,全限定类名。 发生异常一定回滚
        -->
        <tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
                   rollback-for="java.lang.NullPointerException,cn.JInterest.except.NotEnoughException"/>
		
        <!--使用通配符,指定很多的方法,从上往下匹配-->
        <tx:method name="add*" propagation="REQUIRES_NEW" />
        <!--指定修改方法-->
        <tx:method name="modify*" />
        <!--删除方法-->
        <tx:method name="remove*" />
        <!--查询方法,query,search,find-->
        <tx:method name="*" propagation="SUPPORTS" read-only="true" />
    </tx:attributes>
</tx:advice>

<!--配置aop-->
<aop:config>
    <!--配置切入点表达式:指定哪些包中类,要使用事务
        id:切入点表达式的名称,唯一值
        expression:切入点表达式,指定哪些类要使用事务,aspectj会创建代理对象

        com.crm.service
        com.service
    -->
    <aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>

    <!--配置增强器:关联adivce和pointcut
       advice-ref:通知,上面tx:advice哪里的配置
       pointcut-ref:切入点表达式的id
    -->
    <aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt" />
</aop:config>
</beans>

总结spring的事务

  1. 管理事务的是 事务管理和他的实现类
  2. spring的事务是一个统一模型
    1)指定要使用的事务管理器实现类,使用<bean>
    2)指定哪些类,哪些方法需要加入事务的功能
    3)指定方法需要的隔离级别,传播行为,超时
    你需要告诉spring,你的项目中类信息,方法的名称,方法的事务传播行为。

第6章 Web项目怎么使用容器对象

  • web使用容器对象:
    servlet接受请求调用service层做数据操作

    //请求数据...
    
          //创建spring的容器对象
          String config= "spring.xml";
          ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
    
          //获取service,处理业务
    	StudentService service  = (StudentService) ctx.getBean("studentService");
    	service.addStudent(student);
    

    需要的依赖

     <!-- 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>
    
  • 因为web项目是在tomcat服务器上运行的。tomcat启动,项目就一直运行。如果用上面那种方式,获得一次请求就要创建一次ApplicationContext对象,然后把spring.xml文件中所有对象创建出来,同时发送100个请求就要创建100个,垃圾回收器还没来得及回收又创建了,这样很浪费内存

    解决方案: 容器对象创建一次, 把容器对象放入到全局作用域ServletContext中。
    实现: 使用监听器 当全局作用域对象被创建时 创建容器 存入ServletContext
    1)配置web.xml

    <!--注册监听器ContextLoaderListener
        监听器被创建对象后,会读取/WEB-INF/spring.xml
        为什么要读取文件:因为在监听器中要创建ApplicationContext对象,需要加载配置文件。
        /WEB-INF/applicationContext.xml就是监听器默认读取的spring配置文件路径
    
        可以修改默认的文件位置,使用context-param重新指定文件的位置
    
        配置监听器:目的是创建容器对象,创建了容器对象, 就能把spring.xml配置文件中的所有对象都创建好。
        用户发起请求就可以直接使用对象了。
    -->
    <context-param>
      <!-- contextConfigLocation:表示配置文件的路径  -->
      <param-name>contextConfigLocation</param-name>
      <!--自定义配置文件的路径-->
      <param-value>classpath:spring.xml</param-value>
    </context-param>
    <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    

    2)加入监听器依赖

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

    3)servlet接受请求调用service层做数据操作

    //使用框架中的方法,获取容器对象
    WebApplicationContext ctx = null;
          ServletContext sc = getServletContext();
          ctx = WebApplicationContextUtils.getRequiredWebApplicationContext( sc );
          System.out.println("容器对象的信息========"+ctx);
    
    //获取service,处理业务
    StudentService service  = (StudentService) ctx.getBean("studentService");
    service.addStudent(student);
    
  • WebApplicationContext底层实现
    查其源码,看其调用关系,就可看到其是从 ServletContext 中读取的属性值,即 Spring容器。

    (容器对象在 ServletContext 的中存放的 key 为 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE。)

更新时间:2020-09-22 01:07:52

本文由 阿俊 创作,如果您觉得本文不错,请随意赞赏
采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
原文链接:https://jinterest.cn/archives/spring
最后更新:2020-09-22 01:07:52

评论

Your browser is out of date!

Update your browser to view this website correctly. Update my browser now

×