目录

Spring

1.Spring框架概叙

2. IOC控制反转

2.1 概念

2.2Spring的配置文件

2.3 spring容器创建对象的特点

2.4 DI:给属性赋值

2.4.1 基于xml的DI

2.4.2基于注解的DI

2.5 IOC总结

3.AOP面向切面编程

3.1增加功能,导致的问题

3.2AOP概念

3.2.1什么是AOP

3.3AOP作用

3.4 AOP中术语

3.5 什么时候你想用AOP

3.6 AOP技术思想的实现

3.7 使用AspectJ框架实现AOP

3.7.1 通知

3.7.2 Pointcut位置

3.7.3 前置通知@Before

3.7.4 后置通知@AfterReturning

3.7.5 环绕通知 @Around

3.7.6 异常通知 @AfterThrowing

3.7.7 最终通知 @After

3.7.8 定义和管理切入点注解 @Pointcat

3.8 AOP总结

4. Spring集成MyBatis

4.1 集成思想

4.2要使用框架,例如mybatis,怎么使用mybatis?

5. Spring事务

5.1 事务的概念

5.2 在程序中事务在哪说明

5.3 事务管理器

5.3.1 不同的数据库访问技术,处理事务是不同的

5.3.2 spring统一管理事务,把不同的数据库访问技术的事务处理统一起来

5.3.3 Spring事务管理器

5.3.4 事务的提交和回滚的时机

5.3.5 事务使用AOP的环绕通知

5.4 事务定义接口TransactionDefintion

5.4.1 隔离级别

5.4.2 超时时间

5.4.3 传播行为

5.5 Spring框架用自己的注解@Transactional控制事务

5.6 使用Aspectj框架在spring的配置文件中,声明事务控制

6. Spring和web

6.1 现在使用容器对象的问题

6.2 需要一个什么样的容器对象

6.3 ContenxtLoaderListener

6.4 ContextLoaderLister 源代码


Spring

1.Spring框架概叙

核心:控制反转IOC 面向切面编程AOP

Spring又叫容器,Spring作为容器,装的是java对象。可以让Spring创建java对象,给属性赋值。

Spring作用:实现解耦合,解决java对象之间的耦合,解决模块之间的耦合。

Spring | Home

spring sowing人物 spring sowing人物心理变化_赋值

https://spring.io

JFrog

spring sowing人物 spring sowing人物心理变化_赋值

https://repo.spring.io/ui/native/release/org/springframework/spring

优点:

  1. 轻量
  2. 针对接口编程,解耦合
  3. AOP编程支持
  4. 方便集成各种优秀框架

tomcat也是容器:管理的是servlet,listener,filter等对象

创建HelloServlet类,写web.xml

spring:创建SomeServiceImpl,写spring的配置文件

2. IOC控制反转

2.1 概念

IOC :Inversion of Control 控制反转,是一个理论,一个指导思想。指导开发人员如何使用对象,管理对象的。把对象的创建,属性赋值,对象的声明周期交给代码之外的容器管理。

  1. IOC分为 控制和反转

控制:对象创建,属性赋值,对象声明周期管理

反转:把开发人员管理的权限转移给了代码之外的容器实现。由容器完成对象的管理

正转:开发人员在代码中,使用new构造方法创建对象。开发人员掌握了对象的创建,属性赋值,对象从开始到销毁的全部过程。开发人员有对 对象全部控制

通过容器,可以使用容器中的对象(容器已经创建了对象,对象也组装好了)。

Spring就是一个容器,可以管理对象,创建对象,给属性赋值。

  1. IOC的技术实现
    DI(依赖注入):Dependency Injection,缩写是DI,是IOC的一种技术实现。程序只需提供使用的对象的名称就可以了,对象如何创建,如何从容器中查找,获取都是由容器内部自己实现。
    依赖名词:比如说ClassA类使用了ClassB类的属性或者方法,叫做ClassA依赖ClassB
  2. Spring框架使用的DI实现IOC
    通过Spring框架,只需要提供要使用的对象名词就可以了。从容器中获取名称对应的对象。
    Spring底层使用的反射机制,通过反射创建对象,给属性。

2.2Spring的配置文件


<?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">

</beans>
           
spring标准的配置文件:                                                                                         1.根标签 beans
2.beans 后面的是约束文件说明
3.beans里面是bean声明
4.bean:bean就是java对象,spring容器管理的Java对象,


2.3 spring容器创建对象的特点

  1. 容器对象ApplicationContext:接口

通过ApplicationContext对象,获取要使用的其他java对象,执行getBean(“<bean>的id”)

  1. spring默认是调用类的无参构造方法,创建对象
  2. spring读取配置文件,一次创建好所有的Java对象,都放在map中

2.4 DI:给属性赋值

spring调用类的无参构造方法,创建对象。对象创建后给属性赋值。

  1. xml配置文件中的标签和属性;
  2. 使用注解;

DI分类:1.set注入,也叫设值注入;2.构造注入。

2.4.1 基于xml的DI

在xml配置文件中使用标签和属性,完成对象创建,属性赋值

  1. set注入,也叫设值注入

概念:spring调用类中的set方法,在set方法中可以完成属性赋值。推荐使用


简单类型的设值注入,使用value
    <bean id="mySchool" class="org.example.ba02.School">
        <property name="name" value="北京大学"/>
        <property name="address" value="北京海淀区"/>
    </bean>
 
<!--引用类型set注入-->
<!--   简单类型set注入-->
    <bean id="myStudent" class="org.example.ba02.Student">
        <property name="name" value="李四"/>
        <!--20必须加引号-->
        <property name="age" value="20"/>

        <property name="school" ref="mySchool"/><!--setSchool(mySchool)-->

    </bean>
        
 
1. 构造注入
构造注入:spring调用类中的有参构造方法,在创建对象的同时,给属性赋值 <!--   构造注入,使用name属性-->
    <bean id="myStudent" class="org.example.ba03.Student">
       <constructor-arg name="myname" value="李四"/>
        <constructor-arg name="myage" value="22"/>
        <constructor-arg name="myschool" ref="mySchool"/>
    </bean>

<!--构造注入,使用index,参数位置,构造方法参数从左往右位置是0,1,2-->
    <bean id="myStudent2" class="org.example.ba03.Student">
        <constructor-arg index="0" value="张三"/>
        <constructor-arg index="1" value="26"/>
        <constructor-arg index="2" ref="mySchool"/>
    </bean>

    <!--构造注入,省略index属性-->
    <bean id="myStudent3" class="org.example.ba03.Student">
        <constructor-arg  value="王五"/>
        <constructor-arg  value="28"/>
        <constructor-arg  ref="mySchool"/>
    </bean>

<!--声明School-->
    <bean id="mySchool" class="org.example.ba03.School">
        <property name="name" value="北京大学"/>
        <property name="address" value="北京海淀区"/>
    </bean>

<!--声明File对象-->
     <bean id="myFile" class="java.io.File">
          <constructor-arg name="parent" value="F:\Progrom File\ij\Spring\ch02-di-xml"/>
          <constructor-arg name="child" value="1.txt"/>
     </bean>2. 引用类型的自动注入
概念:spring可以根据某些规则给引用类型完成赋值。只对引用类型有效,规则byType,byName1. byName(按名称注入):java类中引用类型属性名称和spring容器中bean的id一样,且数据类型也一样。这些bean能够赋值给引用类型。 <!--   byName自动注入-->
    <bean id="myStudent" class="org.example.ba04.Student" autowire="byName">
        <property name="name" value="李四"/>
<!--        20必须加引号-->
        <property name="age" value="20"/>

<!--        <property name="school" ref="mySchool"/><!–setSchool(mySchool)–>-->

    </bean>
<!--声明School bean中的id名称要与类中引用类型的名称一致-->
    <bean id="school" class="org.example.ba04.School">
        <property name="name" value="清华大学"/>
        <property name="address" value="北京海淀区"/>
    </bean>
2. byType(按类型注入):java类中引用类型的数据类型和spring容器中bean的class值是同源关系的,这样的bean赋值给引用类型。 <!-- 声明bean   -->
<!--
   引用类型自动注入:spring 根据byName,byType规则给引用类型赋值
   1.byName(按名称注入):java类中引用类型的属性名称和spring容器中bean的id名称一样,
                         且数据类型一样的,这样的bean能够赋值给引用类型
     语法:
         <bean id="xxx" class="yyy" autowire="byName">
         简单类型赋值
         </bean>

   2.byType(按类型注入):java类中引用类型的数据类型和bean的class是同源的
                         这样的bean可以赋值给引用类型。
          同源关系:
          1.java中引用类型的数据类型和bean中的class值是一样的
          2.java中引用类型的数据类型和bean中的class值是父子关系
          3.java中引用类型的数据类型和bean中的class是接口和实现类关系的

          语法:
              <bean id="xxx" class="yyy" autowrie="byType">
              简单类型属性赋值
              </bean>

           注意:在xml配置文件中,符合条件的对象,只能有一个,多余一个是报错的
   -->  

<!--   byType自动注入-->
    <bean id="myStudent" class="org.example.ba05.Student" autowire="byType">
        <property name="name" value="张三"/>
<!--        20必须加引号-->
        <property name="age" value="26"/>

<!--        <property name="school" ref="mySchool"/><!–setSchool(mySchool)–>-->

    </bean>
<!--声明School bean中的id名称要与类中引用类型的名称一致-->
    <bean id="mySchool" class="org.example.ba05.School">
        <property name="name" value="航空大学"/>
        <property name="address" value="北京海淀区"/>
    </bean>

<!--声明School的子类-->
<!--    <bean id="primarySchool" class="org.example.ba05.PrimarySchool">-->
<!--        <property name="name" value="北京大兴小学"/>-->
<!--        <property name="address" value="北京大兴区"/>-->
<!--    </bean>-->
  1. 作业IOC
    需求:模拟用户注册操作
    需要定义一个dao接口(User Dao),定义接口实现类MySqlUserDao(insertUse()方法里面不需要操作数据,输出”使用了dao执行insert操作“)
    需要定义一个service接口(UserService),定义接口的实现类UserServiceImpl。在service的实现类有一个UserDao类型的属性。service类中有一个方法addUser(SysUser user)。
    操作时service类中的addUser(){ userDao.insertUser()}完成注册。
    定义一个实体类SysUser,提醒用户的数据
    要求实现:
    程序中的UserServiceImpl,MysqlUserDao这些类都有spring容器创建和管理,同时要给UserService类中的userDao属性赋值。从spring容器中获取UserServiceImpl类型的对象,调用addUser()方法,输出”使用了dao执行insert操作
  2. 项目中使用多个Spring配置文件
    分多个配置文件的方式:1.按功能模块分,一个模块一个配置文件
    在一个文件,service类在一个配置文件,配置redis,事务等等的一个配置文件
    spring管理多个配置文件:常用的是包含关系的配置文件。项目中有一个总的文件,里面是有import标签包含其它的多个配置文件 总的文件(xml) <import resource="其他的文件的路径1"/> <import resource="其他的文件的路径2"/> 关键字"classpath":表示类路径,也就是类文件(class文件)所在的目录。spring到类路径中加载文件什么时候用classpath:在一个                   文件中要使用其他的文件,需要使用classpath

2.4.2基于注解的DI

基于注解的DI:使用spring提供的注解,完成java对象创建,属性赋值

注解使用的核心步骤

  1. 在源代码中加入注解,例如@Conponent
  2. 在spring的配置文件,加入组件扫描器的标签 <!--声明组件扫描器:使用注解必须加入这个语句 component-scan:翻译过来就是组件扫描器,组件就是java对象 属性:base-package 注解在你的项目中的包名 框架会扫描这个包和子包中的所有类,找类中的所有注解 遇到注解后,按照注解表示的功能,去创建对象,给属性赋值。 --> <context:component-scan base-package="注解所在的包名"/>
  3. 创建对象的注解
    @Component 普通java对象
    @Respository dao对象,持久层对象,表示对象能访问数据库
    @Service service对象,业务层对象,处理业务逻辑,具有事务能力
    @Controller 控制器对象,接受请求,显示请求的处理结果。视图层对象
  4. 简单类型属性赋值
    @Value
  5. 引用类型赋值
    @Autowired: spring提供的注解,支持byName,byType
    @Autowired:默认就是byType
    @Autowired @Qualifier :使用byName

@Resource:来自jdk1.8 中的注解,给引用类型赋值,默认是byName

@Resource:先使用byName,再byType

@Resource(name="bean的名称"):只使用byName注入

2.5 IOC总结

IOC:管理对象的,把对象放在容器中,创建,赋值,管理依赖关系。

IOC通过管理对象,实现解耦合。IOC解决处理业务逻辑对象之间的耦合关系,也就是service和dao之间的解耦合。

spring作为容器适合管理什么对象:

  1. service对象,dao对象
  2. 工具类对象

不适合交给spring的对象?

  1. 实体类
  2. servlet,listener,filter等web中的对象。他们是tomcat创建和管理的

3.AOP面向切面编程

3.1增加功能,导致的问题

在源代码中,业务方法中增加的功能

  1. 源代码可能改动的比较多。
  2. 重复代码比较多
  3. 代码难以维护

3.2AOP概念

3.2.1什么是AOP

AOP(Aspect Orient Programming):面向切面编程

Aspect:表示切面,给业务方法添加的功能,叫做切面。切面一般都是非业务功能,而且切面功能一般都是可以复用的。例如:日志功能,权限检查、参数检查、统计信息等等。

Orient:面向,对着

Programming:编程

怎么理解面向切面编程?以切面为核心设计开发你的应用

  1. 设计项目时找出切面的功能
  2. 安排切面的执行时间,执行的位置

3.3AOP作用

  1. 让切面功能复用
  2. 让开发人员专注业务逻辑,提高开发效率
  3. 实现业务功能和其他非业务功能解耦合
  4. 给存在的业务方法,增加功能,不用修改原来的代码

3.4 AOP中术语

  1. Aspect:切面,给业务方法增加的功能
  2. JoinPoint:连接点,连接切面的业务方法。在这个业务方法执行时,会同时执行切面的功能
  3. Pointcut:切入点,是一个或多个连接点集合。表示这些方法执行时,都能增加切面的功能
    表示切面执行的位置
  4. Target:目标对象,给哪个对象增加切面功能,这个对象就是目标对象
  5. Advice:通知,(增强),表示切面的执行时间。在目标方法之前执行切面还是之后

AOP中重要的三个要素:Aspect、 Pointcut、Advice.这三个的概念理解:在Advice的时间,在Pointcut的位置,执行Aspect

AOP是一种动态的思想,在程序运行区间,创建代理(ServiceProxy),使用代理执行方法时,增加切面的功能。这个代理对象是存在内存中的

3.5 什么时候你想用AOP

你要给某些方法增加相同的一些功能。源代码不能改。给业务方法怎加非业务功能,也可以使用AOP

3.6 AOP技术思想的实现

实现框架实现AOP。实现AOP的框架有很多。有名的两个

  1. spring:spring框架实现AOP思想中的部分功能。spring框架实现AOP的操作比较繁琐,比重。
  2. Aspectj:独立的框架,专门是AOP。属于Eclipse The AspectJ Project | The Eclipse Foundation

    spring sowing人物 spring sowing人物心理变化_赋值

    http://www.eclipse.org/aspectj/

3.7 使用AspectJ框架实现AOP

AspectJ框架可以使用注解和xml配置文件两种方式实现AOP

xml配置文件实现步骤:

1. <!--声明目标对象-->
    <bean id="someService" class="org.example.service.impl.SomeServiceImpl"></bean>
2. <!--声明切面类对象-->
    <bean id="myAspect" class="org.example.handle.MyAspect"></bean>
3. <!--声明自动代理生成器:目的是创建目标对象的代理(就是06项目中的ServiceProxy)
    调用aspectj框架中的功能,寻找spring容器中的所有目标对象,
    把每个目标对象加入切面类中的功能,生成代理。-->
    <aop:aspectj-autoproxy/>

3.7.1 通知

AspectJ表示切面执行时间,用的通知(Advice)。这个通知可以使用注解表示。

讲5个注解,表示切面的5个执行时间,这些注解叫做通知注解。

@Before:前置通知

@AfterRetunring:后置通知

@Around:环绕通知

@AfterThrowing:异常通知

@After:最终通知

3.7.2 Pointcut位置

Pointcut用来表示切面执行的位置,使用AspectJ中切入点表达式。

切入点表达式语法:execution(访问权限 方法返回值 方法声明(参数)异常类型)

3.7.3 前置通知@Before


/**
 * @Aspect:切面类的注解
 *      位置:放在某个类的上面
 *      作用:表示当前类是切面类
 *      切面类:表示切面功能的类
 */
@Aspect
public class MyAspect {
    //定义方法,表示切面的具体功能
    /*
       前置通知方法的定义
       1.方法是public
       2.方法是void
       3.方法名称自定义
       4.方法可以有参数,如果有是JoinPoint
         也可以没有

     */
    /**
       @Before:前置通知
              属性:value 切入点表达式,表示切面的执行位置。
                   在这个方法时,会同时执行切面的功能
              位置:在方法的上面

        特点:
            1.执行时间:在目标方法执行之前执行的
            2.不会影响目标方法的执行
            3.不会修改目标方法的执行结果
     */
//    @Before(value = "execution(public void org.example.service.impl.SomeServiceImpl.dosome(String,int))")
//    public void myBefore(){
//        System.out.println("前置功能,切面的功能,在目标方法执行之前执行:"+new Date());
//    }

//    @Before(value = "execution(void org.example.service.impl.SomeServiceImpl.dosome(String,int))")
//    public void myBefore(){
//        System.out.println("前置功能,切面的功能,在目标方法执行之前执行:"+new Date());
//    }

    @Before(value = "execution(* *..do*(..))")
    public void myBefore(){
        System.out.println("前置功能,切面的功能,在目标方法执行之前执行:"+new Date());
    }

    /**
     * 切面类中的通知方法,可以有参数
     * JoinPoint必须是他
     *
     * JoinPoint:表示正在执行的业务方法。相当于反射中 Method
     *      使用要求:必须是参数列表中的第一个
     *      作用:获取方法执行时的信息,例如方法名称,方法的参数集合
     */
    @Before(value = "execution(* *..do*(..))")
    public void myBefore2(JoinPoint jp ){
        //获取方法的定义
        System.out.println("前置通知中,获取目标方法的定义:"+jp.getSignature());
        System.out.println("前置通知中,获取方法的名称="+jp.getSignature().getName());
        //获取方法执行时参数
        Object arges[] =jp.getArgs(); //数组存放的是 方法的所有参数
        for(Object arge:arges){
            System.out.println("前置通知中,获取方法的参数:"+arge);
        }
        //切面的代码
        System.out.println("前置通知,切面的功能,在目标方法之前先执行=="+new Date());
        System.out.println("");
    }
}


3.7.4 后置通知@AfterReturning

@AfterReturning :在目标方法执行之后


后置通知方法的定义
       1.方法是public
       2.方法是void
       3.方法名称自定义
       4.方法可以有参数,推荐Object类型
         也可以没有

     */
    /**
       @AfterReturning:后置通知
              属性:value 切入点表达式,表示切面的执行位置。
                    在这个方法时,会同时执行切面的功能
                    returnring:自定义变量,表示目标方法的返回值的
                                自定义变量名称必须和通知方法的形参一致
              位置:在方法的上面

        特点:
            1.执行时间:在目标方法执行之后执行的
            2.不会影响目标方法的执行
            3.能获取到目标方法的执行结果
        方法的参数:
            Object res:表示目标方法返回值,使用res接收doother的调试结果。
            Object res=doother();
     
        后置通知的执行顺序
        Object res =SomeServiceImpl。dother(..);
        myAfterReturning(res);
     
        思考:
         1. doother方法返回是String,int,Long等基本类型
              在后置通知方法中,修改返回值,是不会影响目标方法的最后调用结果的
         2. doother返回的结果是对象类型,例如Student。
              在后置通知方法中,修改这个Student对象的属性值,会不会影响最后的调用结果

     */
    @AfterReturning(value = "execution(* *..SomeServiceImpl.doother(String,int))",returning = "res")
    public void myAfterReturning(Object res){
        
        //修改目标方法的返回值
        if(res!=null){
            res="hello Aspectj"
        }
        System.out.println("后置通知,在目标方法之后,执行的。能拿到执行结果:"+res);
        //Object res有什么用
        if("abcd".equals(res)){
            System.out.println("根据返回值的不同,做不同的增强功能");
        }else if("add".equals(res)){
            System.out.println("doother做了添加数据库,我做了备份数据");
        }
    }


3.7.5 环绕通知 @Around

@Around(value=“切入点表达式”)

使用环绕通知就是执行切面类中的方法


/*
   环绕通知方法的定义
   1.方法是public
   2.方法是必须有返回值,推荐使用Object类型
   3.方法名称自定义
   4.方法必须有ProceedingJoinPoint参数
 */
/**
 * @Around: 环绕通知
 *   属性:value 切入点表达式
 *   位置:在方法定义的上面
 *   返回值:Object,表示你调用目标方法希望得到执行结果(不一定是目标方法自己的返回值)
 *   参数:ProceedingJoinPoint,相当于反射中的 Method
 *       作用:执行目标方法的,等于Method.invoke()
 *
 *     public interface ProceedingJoinPoint extends JoinPoint
 *   特点:
 *      1.在目标方法的前和后都能增强功能
 *      2.控制目标方法是否执行
 *      3.修改目标方法的执行结果
 */
@Around("execution(* *..SomeServiceImpl.doFirst(String))")
public Object myAround(ProceedingJoinPoint pjp) throws Throwable {

    Object methodReturn=null;
    System.out.println("执行了环绕通知,在目标方法执行之前,输出日志时间:"+new Date());
    //执行目标方法 ProceedingJoinPoint,表示doFirst
    methodReturn=pjp.proceed();//Method.invoke(),表示执行doFirst()方法本身

    System.out.println("执行了环绕通知,在目标方法执行之后,增加了事务提交功能");

    //return "helloAround,不是目标方法的执行结果";
    //返回目标方法的返回结果
    return methodReturn;

}


3.7.6 异常通知 @AfterThrowing

语法:

@AfterThrowing(value=“切入点表达式”,throwing="自定义变量")


/*
    异常通知方法定义
    1.方法是public
    2.方法是没有返回值
    3.方法名称自定义
    4.方法有参数是Exception
  */
/**
 * @AfterThrowing:异常通知
 * 属性: value 切入点表达式
 *        throwing 自定义变量,表示目标方法抛出的异常
 *                 变量名必须和通知方法的形参名一样
 *
 * 位置:在方法的上面
 * 特点:
 *    1.在目标方法抛出异常后执行的,没有异常不执行
 *    2.能获取到目标方法的异常信息
 *    3.不是异常处理程序,可以得到发送异常的通知,可以发送邮件,短信通知开发人员
 *       看作是目标方法的监控程序
 *       
 * 异常通知的执行
 * try{
 *     SomeServiceImpl.doSecond(...)
 * }catch(Exception e){
 *     myAfterThrowing(e)
 * }
 */
@AfterThrowing(value="execution(* *..SomeServiceImpl.doSecond(String))",throwing = "e")
public void myAfterThrowing(Exception e){
    System.out.println("异常通知,在目标方法抛出异常时执行的,异常原因是:"+e.getMessage());
    /*
    异常发生可以做:
    1.记录异常的时间,位置,等信息。
    2.发送邮件,短信,通知开发人员
     */
}


3.7.7 最终通知 @After

语法:

@After(value=“切入点表达式”)


/

*
    最终通知方法的定义:
    1.方法是public
    2.方法是没有返回值,是void
    3.方法名称自定义
    4.方法没有参数
  */

/**
 * @After: 最终通知
 *     属性:value 切入点表达式
 *     位置:方法的上面
 *  特点:
 *  1.在目标方法执行之后
 *  2.总是会被执行,即使是有异常
 *  3.可以用来做程序最后的收尾工作。例如清理临时数据,变量。清理内存
 *  
 *  最终通知
 *  try{
 *      SomeServiceImpl.doThird(..)
 *  }finally{
 *      myafter
 *  }
 */
@After(value = "execution(* *..SomeServiceImpl.doThird(..))")
 public void myAfter(){
    System.out.println("最终通知,总是会被执行");
}

3.7.8 定义和管理切入点注解 @Pointcat


/**
 * @Pointat:定义和管理切入点,不是通知注解
 *      属性: value 切入点表达式
 *      位置: 在一个自定义方法的上面,这个方法看作是切入点表达式的别名
 *             其他的通知注解中,可以使用方法名称,就表示使用这个切入点表达式了
 */
@Pointcut("execution(* *..SomeServiceImpl.doThird(..))")
private void mypt(){
    //无需代码
}
 @Before(value = "mypt()")
public void myBefore(){
    System.out.println("前置通知,在目标方法执行之前执行");
}

@After(value = "mypt()")
 public void myAfter(){
    System.out.println("最终通知,总是会被执行");
}


3.8 AOP总结

AOP是一种动态的技术思想,目的是实现业务功能和非业务功能的解耦合。业务功能是独立的模块,其他功能也是独立的模块。例如事务功能,日志等等。让这些事务,日志功能是可以被复用的

当目标方法需要一些功能时,可以在不修改,不能修改源代码的情况下,使用aop技术在程序执行期间,生成代理对象,通过代理执行业务方法,同时增加功能

4. Spring集成MyBatis

4.1 集成思想

spring能集成很对的框架,是spring一个优势功能,让开发人员使用其他框架更方便。

集成使用的是spring IOC核心技术。

4.2要使用框架,例如mybatis,怎么使用mybatis?

使用mybatis,需要创建mybatis框架中的某些对象,使用这些对象,就能使用mybatis提供的功能了

分析:mybatis执行sql语句,需要使用那些对象

  1. 需要有Dao接口的代理对象,例如StudentDao接口,需要一个它的代理对象
    使用SqlSession.getMapper(StudentDao.class),得到dao代理对象
  2. 需要有SqlSessionFactory,创建SqlSessionFactory对象,才能使用openSession()得到SqlSession的对象
  3. 数据源DataSource对象,使用一个更强大,功能更多的连接池对象代替mybatis自己的PooledDataSource

pom.xml依赖

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

  <!--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>5.1.9</version>
  </dependency>

  <!--阿里连接池  JDBC的一个组件-->
  <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.12</version>
  </dependency>

</dependencies>

5. Spring事务

5.1 事务的概念

事务:一些sql序列的集合,是多条sql,作为一个整体执行。


mysql执行事务
beginTransaction 开启事务
insert into student() values.....
select = from student where id=1001
update school set name=xxx where id=1005
endTransaction 事务结束


什么情况下需要使用事务?

一个操作需要多条(两条或两条以上的sql)sql语句一起完成,操作才能成功

5.2 在程序中事务在哪说明

事务:加在业务类的方法上面(public方法上面),表示业务方法执行时,需要事务的支持


public class AccountService{
    
    private AccountDao dao;
    private MoneyDao dao2;
    //在service(业务类)的public方法上面,需要说明事务。
    public void trans(String a,String b,Integer moneny){
        dao.updateA();
        dao.updateB();
        dao2.insertA();
    }
}

public class AccountDao{
    public void updateA(){}
    public void updateB(){}
}
public class MoneyDao{
    public void insertA(){}
    public void deleteB(){}
}


5.3 事务管理器

5.3.1 不同的数据库访问技术,处理事务是不同的

1. 使用jdbc访问数据库,事务处理 public void updateAccount(){
    Connection conn =....
    conn.setAutoCommit(false);
    stat.insert();
    stat.updata();
    conn.commit();
    con.setAutoCommit(true);
}
2. mybatis执行数据库,处理事务public void updateAccount(){
    SqlSession session =SqlSession.openSession(false);
    try{
        session.insert("insert ......");
        session.update("update....");
        session.commit();
    }catch(Exception e){
        session.rollback();
    }
}

5.3.2 spring统一管理事务,把不同的数据库访问技术的事务处理统一起来

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

管理事务面向的是spring,有spring管理事务,做事务提交,事务回滚

5.3.3 Spring事务管理器

Spring框架使用事务管理器对象,管理所有的事务

事务管理器接口:PlatformTransactionManager

作用:定义了事务的操作,主要是commit(),rollback()

事务管理器有很多实现类:一种数据库的访问技术有一个实现类。由实现类具体完成事务的提交,回滚

意味着:jdbc或者mybatis访问数据库有自己的事务管理器实现类:DataSourceTranaction'Manager

hibernate框架,他的事务管理器实现类:HibernateTransactionManager

5.3.4 事务的提交和回滚的时机

当业务方法正常执行时,没有异常,事务是提交的,如果业务抛出了运行时异常,事务是回滚的

异常的分类:

Error:严重错误。回滚事务。

Exeption:异常类,可以出来的异常情况

1. 运行时异常:RuntimeException和他的子类都是运行时异常,在程序执行过程中抛出的异常。常见的运行时异常:NullPoinerException(空指针),NumberFormatException(转换异常),IndexOutOfBoundException(数组越界),ArithmeticException
      2. 受查异常:编写java代码的时候,必须出来的异常,例如IOException,SQLException,FileNotFoundException

方法中抛出了运行时异常,事务回滚,其他情况(正常执行方法,受查异常)就是提交事务

5.3.5 事务使用AOP的环绕通知

环绕通知:可以在目标方法的前后都能增强功能,不需要修改代码


spring给业务方法执行时,增加事务的切面功能
 
@Around("execution(* 所有的业务类中的方法)")    
public Object myAround(ProceedingJoinPoint pjp){
    
   try{ 
    PlatformTransactionManager.beginTransaction();
    pjp.proceed();//执行目标方法 //doSome()
    PlatformTransactionManager.commit();//业务方法正常执行,提交事务
   }catch(Exception e){
        PlatformTransactionManager.rollback();//业务方法正常执行,回滚事务
   }
}


5.4 事务定义接口TransactionDefintion

TransactionDefintion接口。定义了三类常量,定义了有关事务控制的属性。

事务的属性:1. 隔离级别 2.传播行为 3.事务的超时

给业务方法说明事务属性。和ACID不一样 。

5.4.1 隔离级别

隔离级别:控制事务之间影响的程度

5个值,只有四个隔离级别

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

5.4.2 超时时间

超时时间,以秒为单位。整数值,默认是-1

超时时间:表示一个业务方法最长的执行时间,没有到达时间没有执行完毕,spring回滚事务

5.4.3 传播行为

传播行为有七个值

传播行为:业务方法在调用时,事务在方法之间的,传递和使用。

使用创播行为,标识方法有无事务

PROPAGATION_REQUIRED

PROPAGATION_REQUIRES_NEW

PROPAGATION_SUPPORTS

PROPAGATION_MANDATORY

PROPAGATION_NESTED

PROPAGATION_NEVER

PROPAGATION_NOT_SUPPORTED

  1. REQUIRED: spring默认传播行为,方法在调用的时候,如果存在事务就是使用当前的事务,如果没有事务,则新建事务,方法在新事务中执行
  2. SUPPORTS: 支持,方法有事务可以正常执行,没有事务也可以正常执行
  3. REQUIRES_NEW: 方法需要一个新事务,如果调用方法时,存在一个事务,则原来的事务暂停,直到新事务执行完毕,如果调用方法时,没有事务,则新建一个事务,在新事务执行代码。

5.5 Spring框架用自己的注解@Transactional控制事务

@Transactional注解,使用注解的属性控制事务(隔离级别,传播行为,超时)

属性:

  1. propagation:事务的传播行为,它使用的Propagation类的枚举值。例如Propagation.REQUIRED
  2. isolation:表示隔离级别,使用isolation类的枚举值,表示隔离级别,默认isolation.DEFAULT
  3. readOnly: boolean类型的值,表示数据库操作是不是只读的默认是false
  4. timeout:事务超时,默认是-1,整数值,单位是秒。例如timeout=20
  5. rollbackFor:表示回滚的异常类列表,它的值是一个数组,每个值是异常类型的class。
  6. rollbackForClassName:表示回滚的异常类列表,它的值是异常名称,是String类型的值
  7. noRollbackFor:不需要回滚的异常类列表。是class类型的值

位置:1.在公共业务方法上面,是在public方法上面

2.在类的上面

注解的使用步骤:

  1. 在spring的配置文件,声明事务的内容
    声明事务管理器,说明使用哪个事务管理器对象
    声明使用注解管理事务,开启注解驱动
  2. 在类的源代码中,加入@Transaction

事务的控制模式:

  1. 编程式,在代码中编程控制事务。
  2. 声明式事务,不用编码

例子:


//@Transactional 放在public方法上面,表示方法有事务功能
    /*第一种设置方式
    @Transactional(
                   propagation = Propagation.REQUIRED,
                   isolation = Isolation.DEFAULT,
                   readOnly = false,
                   timeout = 20,
                   rollbackFor = {NullPointerException.class,NotEnougthException.class})
    */
    /*第二种设置方式
     @Transactional(
            propagation = Propagation.REQUIRED,
            isolation = Isolation.DEFAULT,
            readOnly = false,
            timeout = 20)

            //解释rollbackFor 的使用:
            //1.框架首先检查方法抛出的异常是不是在rollbackFor的数组中,如果在一定回滚
            //2.如果方法抛出的异步不在rollbackFor数组,框架会继续检查抛出的异常是不是RuntimeException 如果是一定回滚

            例如 抛出 SQLException,IOException
            rollbackFor={SQLException.class,IOException.class}
    */
    //第三种方式: 使用默认值 REQUIRED,发生运行时异常回滚。
    @Transactional
    @Override
    public void buy(Integer goodsId, Integer num) {
        System.out.println("*****buy方法的开始******");

         //生成销售记录
        Sale sale =new Sale();
        sale.setGid(goodsId);
        sale.setNum(num);
        saleDao.insertSale(sale);

        //查询商品
        Goods goods =goodsDao.selectByid(goodsId);
        if (goods==null){
            throw new NullPointerException(goodsId+"商品不存在");
        }else if(goods.getAmount()<num){
            throw new NotEnougthException(goodsId+"库存不足");
        }

         //更新库存
        Goods buyGoods=new Goods();
        buyGoods.setId(goodsId);
        buyGoods.setAmount(num);
        goodsDao.updateGoods(buyGoods);
        System.out.println("*****buy方法的结束******");

    }


spring配置文件
<!--声明事务的控制-->
    <!--声明事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--指定数据源DataSource-->
        <property name="dataSource" ref="myDataSource"/>
    </bean>

    <!--开启事务注解驱动 告诉框架使用注解管理事务
        transaction-manager:指定事务管理器的id -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

/**
 * 运行时异常
 */
public class NotEnougthException extends RuntimeException {
    public NotEnougthException() {
        super();
    }

    public NotEnougthException(String message) {
        super(message);
    }
}

@Transactional使用的特点:

  1. spring框架自己提供的事务控制
  2. 适合中小型项目
  3. 使用方便,效率高。

5.6 使用Aspectj框架在spring的配置文件中,声明事务控制

使用aspectj的aop,声明事务控制叫做声明式事务

使用步骤:

  1. pom.xml加入 spring-aspects的依赖
  2. 在spring的配置文件声明事务的内容
  1. 声明事务管理器
  2. 声明业务方法需要的事务属性
  3. 声明切入点表达式

声明式事务:

<!--声明式事务,不用写代码-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="myDataSource"/>
    </bean>
    <!--声明业务方法的事务属性(隔离级别,传播行为,超时)
        id:给业务方法配置事务代码起个名称,唯一值
        transaction-manager:事务管理器的id
    -->
    <tx:advice id="serviceAdivce" transaction-manager="transactionManager">
         <!--给具体的业务方法增加事务的说明
             name:业务方法名称。配置name的值; 1.业务方法的名称;2.带有部分通配符(*)的方法名称;3.使用通配符*
             propagation:指定传播行为的值
             isolation:隔离级别
             read-only:是否只读,默认false
             timeout:超时时间
             rollback-for:指定回滚的异常类列表,使用的异常全限定名称
             -->
        <tx:attributes>
            <tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
                       timeout="20" read-only="false"
                       rollback-for="java.lang.NullPointerException,org.example.excetion.NotEnougthException"/>
            
            <!--在业务方法有命名规则后,可以对一些方法使用事务-->
            <tx:method name="add*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"/>
            <tx:method name="modify*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
            <tx:method name="remove*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>

            <!--以上方法以外的 *:querySale,findSale,searchSale...查询-->
            <tx:method name="*" propagation="SUPPORTS" read-only="true"/>
        </tx:attributes>
    </tx:advice>

    <!--声明切入点表达式:表示哪些包中的类,类中的方法参与事务-->
    <aop:config>
        <!--声明切入点表达式
           expression:切入点表达式,表示哪些类和类中的方法要参与事务
           id: 切入点表达式的名称,唯一值

           expression怎么写?
        -->
        <aop:pointcut id="servicePointcut" expression="execution(* *..service..*.*(..))"/>
        <!--关联切入点表达式和事务通知-->
        <aop:advisor advice-ref="serviceAdivce" pointcut-ref="servicePointcut"/>
    </aop:config>

声明式事务优缺点:

  1. 缺点:理解难,配置复杂。
  2. 优点:代码和事务配置是分开的。控制事务源代码不用修改
    能快速的了解和掌控项目的全部事务。适合大项目

6. Spring和web

6.1 现在使用容器对象的问题

  1. 创建容器对象次数多
  2. 在多个servlet中,分别创建容器对象

6.2 需要一个什么样的容器对象

  1. 容器对象只有一个,创建一次就可以了
  2. 容器对象应该在整个项目中共享使用。多个servlet都能使用同一个容器对象

解决问题使用监听器 ServletContextListener(两个方法 初始时执行的,销毁时执行时)

在监听器中,创建好的容器对象,应该放在web应用中的ServletContext作用域中

6.3 ContenxtLoaderListener

ContextLoaderListener 是一个监听器对象,是spring框架提供的,使用这个监听器作用:

  1. 创建容器对象,一次
  2. 把容器对象放入到ServletContext作用域。

使用步骤:

  1. pom.xml加入依赖spring-web
  2. web.xml声明监听器的存在
1. 依赖: <!--监听器依赖-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>5.2.5.RELEASE</version>
</dependency>

web.xml声明:

设置监听器


<!--声明监听器 要放在servlet之前--> 
<!--声明监听器
        默认监听器:创建容器对象时,读取的配置文件:/WEB-INF/spring-beans.xml-->
    <!--自定义容器使用的配置文件路径
        context-param:叫做上下文参数,给监听器,提供参数-->
    <context-param>
        <!--contextConfigLocation:名称是固定的,表示自定义spring配置文件的路径-->
        <param-name>contextConfigLocation</param-name>
        <!--自定义配置文件的路径,可以指定多个 例如:classpath:spring-beans.xml,classpath:mybeans.xml-->
        <param-value>classpath:spring-beans.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>


6.4 ContextLoaderLister 源代码

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    
    //监听器的初始方法
    public void contextInitialized(ServletContextEvent event) {
        this.initWebApplicationContext(event.getServletContext());
    }
    
}


private WebApplicationContext context;

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {

		try {
			if (this.context == null) {
                //创建spring的容器对象
				this.context = createWebApplicationContext(servletContext);
			}
          			//把容器对象放入ServletContext作用域
                    //key=WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
            //value=容器对象
                                                  servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

		}
		catch (RuntimeException | Error ex) {

		}
	}

//WebApplicationContext是web项目中使用的容器对象
public interface WebApplicationContext extends ApplicationContext

例如servlet类:

String stuId= request.getParameter("stuid");
        //创建容器对象
       // String config = "spring-beans.xml";
       // ApplicationContext ctx=new ClassPathXmlApplicationContext(config);

        //使用了监听器已经创建好了容器对象,从ServletContext作用域获取容器
        WebApplicationContext ctx = null;
        String key= WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
        ServletContext sc=getServletContext(); //ServletContext,servlet中的方法
        //ServletContext sc =request.getServletContext();//HttpServletRequest 对象的方法

        Object attr = sc.getAttribute(key);
        if (attr!=null){
            ctx= (WebApplicationContext) attr;
        }

        System.out.println("在servlet中创建的容器对象=="+ctx);
        StudentService service= (StudentService) ctx.getBean("studentService");

        Student student =service.findStudentById(Integer.valueOf(stuId));
        System.out.println("student对象"+student);


        request.setAttribute("stu",student);
        request.getRequestDispatcher("/show.jsp").forward(request,response);
    }