Spring框架一文详解

Spring框架

Spring

一:Spring概述

Spring全家桶:

​ Spring,SpringMVC,SpringBoot,SpringCloud

Spring:

​ 出现在2002年左右,解决企业开发的难度,减轻对项目模块之间的管理,类和类之间的管理,帮助开发人员创建对象,管理对象之间的关系。

Spring核心技术:

​ ioc(控制反转),aop(面向切面编程)。能够实现模块之间,类之间的解耦合。

依赖:

​ Class A中使用Class B的属性或者方法,叫做Class A依赖Class B。

框架怎么学:

​ 框架是一个软件,其他人写好的软件。

1):知道框架能做什么,myBatis–>访问数据库的框架,对表中的数据执行增删改查。

2):框架的语法,框架要完成一个功能,需要一定的步骤支持的。

3):有工作经验以后,需要学习框架的内部实现,框架内部怎么做,原理是什么?

4):通过学习,可以自己写一个框架。

二:Ioc 控制反转

1):Ioc((Inversion of Control)

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

IoC 是一个概念,是一种思想,其实现方式多种多样。当前比较流行的实现方式是依赖

注入。应用广泛。

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

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

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

**容器:**是一个服务器软件,也是一个框架(Spring)。

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

java中创建对象有哪些方式:

​ 1:构造方法:new Student()

​ 2:反射

​ 3:序列化

​ 4:克隆

​ 5:ioc:容器创建对象

​ 6:动态代理

ioc的体现:

​ servlet 1:创建类继承HttpServlet

​ 2:在Web.xml中注册servlet。

​ 3:我们没有创建过 Servlet对象,没有MyServlet myservlet = new MyServlet();

​ 4:Servlet对象 是Tomcat服务器创建的。Tomcat也称为容器。

​ Tomcat作为容器:里面存放的有Servlet对象,Listener(监听器),Filter(过滤器)对象等。

ioc的技术实现:

​ DI(Dependency Injection) 是ioc的技术实现。

​ **DI:**依赖注入,只需要在程序中提供要使用的对象名称就可以,至于对象如何在容器中创建,赋值,查找,都 由容器内部实现。

​ Spring是使用的di实现了ioc的功能,spring底层创建对象,使用的是反射机制。

​ Spring是一个容器,用来管理对象,给属性赋值,底层使用反射机制创建对象。

每日一道Java面试题

记得关注我【Java有话说
在这里插入图片描述

2):Spring的第一个程序

实现步骤:

实现步骤:
    1:创建maven项目

    2:加入maven的依赖
        spring的依赖

    3:创建类
        和没有使用框架一样,就是普通的类。

    4:创建spring需要使用的配置文件。在main目录下的resources目录下创建 Spring Config
        声明类的信息,这些类由spring创建和管理。

    5:测试spring创建的对象。

pom.xml:加入spring依赖

<!--spring依赖-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.2.7.RELEASE</version>
</dependency>

Spring Config 配置文件:

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

<!--告诉spring创建对象
    声明bean,就是告诉spring要创建某个类的对象
    id:对象的自定义名称,唯一值。spring通过这个名称来找到这个对象
    calss:类的全限定名称(不能是接口,因为spring是反射机制创建对象,必须使用类)

    spring底层就完成 SomeService someService = new SomeServiceImpl();
    spring是把创建好的对象放入到map中,spring框架有一个map存放对象的。
        springMap.put(id的值,对象);
        例如:springMap.put("someService",new SomeServiceImpl())

    一个bean标签声明一个对象。
-->
    <bean id="someService" class="com.sss.service.impl.SomeServiceImpl"/>
</beans>

<!--
    spring的配置文件
    1.beans:是根标签,spring把java对象称为bean。
    2.spring-beans.xsd 是约束文件,和myBatis中的dtd是一样的。
-->

@Test:

/*测试spring*/
@Test
public void testDoSome2(){
    //使用spring容器创建的对象
    // 1:指定spring配置文件的名称。
    String config = "beans.xml";

    //2:创建表示spring容器的对象,ApplicationContext
    //ApplicationContext就是表示spring容器,就可以通过容器获取对象了。
    //new ClassPathXmlApplicationContext(配置文件路径);
    //ClassPathXmlApplicationContext:表示从类路径中加载spring的配置文件的。
    ApplicationContext ac = new ClassPathXmlApplicationContext(config);

    //3:从容器中获取对象,你要调用对象的方法。
    //ac.getBean("配置文件中的bean的id值");
    //转换成接口类型。
    SomeService service = (SomeService) ac.getBean("someService");
    
}

3):spring默认创建对象的时间

在创建spring容器时,就会创建配置文件中所有的对象。

spring创建对象:默认调用的是无参数构造方法。

4):获取spring容器中,java对象的信息

/**
 * 获取spring容器中,java对象的信息。
 */
@Test
public void testDoSome3(){
    String config = "beans.xml";
    ApplicationContext ac = new ClassPathXmlApplicationContext(config);

    //使用spring提供的方法,获取容器中定义的对象的数量。
    int count = ac.getBeanDefinitionCount();
    System.out.println("count="+count); //1

    //获取容器中每个定义的对象的名称。
    String[] names = ac.getBeanDefinitionNames();
    for (String name:names){
        System.out.println("name="+name);
    }
}

5):获取一个非自定义的类对象

<!--
    spring能创建一个非自定义的对象吗?可以 创建一个已经存在的某个类的对象
-->
<bean id="mydate" class="java.util.Date" />
@Test
public void testDoSome4(){
    String config = "beans.xml";
    ApplicationContext ac = new ClassPathXmlApplicationContext(config);

    //获取当前的系统时间
    Date date = (Date)ac.getBean("mydate");
    System.out.println(date);
}

三:基于xml的DI

1):di表示

依赖注入,表示创建对象,给属性赋值

2):di的注入分类

1)set注入(设值注入)

​ spring调用类的set方法,在set方法可以实现属性的赋值。
​ 80%左右都是使用的set注入。

简单类型的设值注入实现:

<!--声明Student对象
    注入:就是赋值的意思。
    简单类型:spring中规定基本数据类型和字符串都是简单类型。
    di:给属性赋值
        1:set注入(设值注入):spring调用类的set方法,你可以在set方法中完成属性赋值。
            1):简单类型的set注入
                <bean id = "" class = "">
                    <property name = "属性名字" value ="属性的值" />
                    一个property只能给一个属性赋值
                </bean>
-->
<bean id="mystudent" class="com.sss.ch01.Student">
    <property name="name" value="小黑"/><!--setName("小黑")-->
    <property name="age" value= "20"/>
</bean>

引用类型的设值注入实现:

​ 当指定 bean 的某属性值为另一 bean 的实例时,通过 ref 指定它们间的引用关系。ref的值必须为某 bean 的 id 值。

<!--声明Student对象-->
<bean id="mystudent" class="com.sss.ch02.Student">
    <property name="name" value="小黑"/>
    <property name="age" value= "20"/>
    <!--引用类型-->
    <property name="school" ref="myschool"/>
</bean>

<!--声明School对象-->
<bean id="myschool" class="com.sss.ch02.School">
    <property name="name" value="青清校园"/>
    <property name="addres" value="河南省开封市"/>
</bean>

设值注入的注意事项:

​ 必须有set方法。

​ 设置注入只是使用set方法。

​ value的值必须使用引号。

2)构造注入

​ spring调用类的有参构造方法,创建对象,在构造方法中完成赋值。

<!--声明Student对象
    构造注入:spring调用类的有参构造方法,创建对象,在构造方法中完成赋值。
        构造注入使用<constructor-arg>标签
        <constructor-arg> 标签:一个<constructor-arg>表示构造方法一个参数
        <constructor-arg> 标签属性:
            name:表示构造方法的形参名
            index:表示构造方法的位置,参数从左往右位置是0,1,2的顺序。
            value:构造方法的形参类型是简单类型,使用value
            ref:构造方法的形参类型是引用类型,使用ref

-->
    <bean id="mystudent" class="com.sss.ch03.Student">
        <constructor-arg name="name" value="小黑"/>
        <constructor-arg name="age" value= "20"/>
        <constructor-arg name="school" ref="myschool"/>
    </bean>

 <!--使用index属性-->
    <bean id="mystudent2" class="com.sss.ch03.Student">
        <constructor-arg index="0" value="小黑"/>
        <constructor-arg index="1" value= "20"/>
        <constructor-arg index="2" ref="myschool"/>
    </bean>

<!--按照位置顺序,可以省略index属性-->
    <bean id="mystudent2" class="com.sss.ch03.Student">
        <constructor-arg value="小黑"/>
        <constructor-arg value= "20"/>
        <constructor-arg ref="myschool"/>
    </bean>

<!--声明School对象-->
    <bean id="myschool" class="com.sss.ch03.School">
        <constructor-arg name="name" value="青清校园"/>
        <constructor-arg name="addres" value="河南省开封市"/>
    </bean>

 <!--小案例:创建File,使用构造方法注入-->
    <bean id="myfile" class="java.io.File">
        <constructor-arg name="parent" value="E:\Ajax_jQuery_maven_mybatis_Spring_Springmvc\Spring\ch01_Spring" />
        <constructor-arg name="child" value="readme.txt"/>
    </bean>

构造注入必须有:带参构造方法

3:引用类型属性自动注入

​ 对于引用类型属性的注入,也可不在配置文件中显示的注入。可以通过为标签

设置 autowire 属性值,为引用类型属性进行隐式自动注入(默认是不自动注入引用类型属

性)。

根据自动注入判断标准的不同,可以分为两种:

byName:根据名称自动注入

byType: 根据类型自动注入

1)byName方式自动注入

根据名称自动注入

<!--
    引用类型的自动注入:spring框架根据某些规则可以给引用类型赋值。不用你再给引用类型赋值
    使用的规则常用的是 byName,byType
    1.byName:按名称注入,java类中引用类型的属性名spring容器中(配置文件)<bean>的id名称一样,
              且数据类型是一致的,这样的容器中的bean,spring能够赋值给引用类型。
      语法:
         <bean id = "xx" class = "yy" autowire="byName">

         </bean>
-->
<bean id="mystudent" class="com.sss.ch04.Student" autowire="byName">
    <property name="name" value="小黑"/>
    <property name="age" value= "20"/>
    <!--<property name="school" value="school"/>-->
</bean>

<!--声明School对象-->
<bean id="school" class="com.sss.ch04.School">
    <constructor-arg name="name" value="青清校园"/>
    <constructor-arg name="addres" value="河南省开封市"/>
</bean>
2)byType方式自动注入

根据类型自动注入

<!--
    byType:按类型注入,java类中引用类型的数据类型和spring容器中被调用者的<bean>的class属性是同源关系,这样的bean能够赋值给引用类型。
    同源:就是一类的意思
          1.java类中引用类型的数据类型和bean的class的值是一样的。
          2.java类中引用类型的数据类型和bean的class的值是父子类的关系。
          3.java类中引用类型的数据类型和bean的class的值是接口和实现类的关系。
    语法:
        <bean id = "xx" class = "yy" autowire="byType">

        </bean>
-->
<bean id="mystudent" class="com.sss.ch05.Student" autowire="byType">
    <property name="name" value="小绿"/>
    <property name="age" value= "20"/>
    <!--<property name="school" value="school"/>-->
</bean>

<!--声明School对象-->
<bean id="myschool" class="com.sss.ch05.School">
    <constructor-arg name="name" value="开封大学"/>
    <constructor-arg name="addres" value="河南省开封市"/>
</bean>

注意:

但这样的同源的被调用 bean 只能有一个。多于一个,容器就不知该匹配

哪一个了。

3)多个spring配置文件

优势:

​ 1:每个文件的大小比一个文件要小很多,效率高。

​ 2:避免多人竞争带来的冲突。

​ 如果你的项目有很多个模块(相关功能在一起),一个模块一个配置文件。

多配置文件的分配方式:

​ 1:按功能模块,一个模块一个配置文件

​ 2:按类的功能,数据库相关的配置一个配置文件,做事务的功能一个配置文件,做service功能的一个配置文件等。

<!--
    包含关系的配置文件:
    spring-total表示主配置文件:包含其他的配置文件的。
        主配置文件,一般是不定义对象的。
        语法:
            <import resource="其他配置文件的路径"/>
        关键字:"classpath:"表示类路径(class文件所在目录,在spring的配置文件中要指定其他文件的位置
                需要使用classpath,告诉spring到哪里去加载读取文件。
-->

<!--加载的是文件列表-->
<import resource="classpath:ch06/spring-school.xml"/>
<import resource="classpath:ch06/spring-student.xml"/>

<!--
    在包含关系的配置文件中,可以使用通配符(*:表示任意字符)
    注意:主的配置文件名称不能包含在通配符的范围内(不能叫spring-total.xml),
          否则将出现循环递归包含
-->
<!--<import resource="classpath:ch06/spring-*.xml"/>-->

注意:

主的配置文件名称不能包含在通配符的范围内(不能叫spring-total.xml),否则将出现循环递归包含。

使用通配符这种方式,配置文件必须有上级目录,不然读取不到。

4)基于注解的DI

通过注解完成java对象创建,属性赋值。

1.加入maven的依赖 spring-context,在你加入spring-context时,会间接加入spring-aop的依赖。

​ 使用注解必须使用spring-aop依赖。

2.在类中加入spring的注解(多个不同功能的注解)

3.在spring的配置文件中,加入一个组件扫描器的标签。说明注解在你的项目中的位置。

需要学习的注解:

​ 1.@Component

​ 2.@Respotory

​ 3.@Service

​ 4.@Controller

​ 5.@Value

​ 6.@Autowired

​ 7.@Resource

@Component @Repository @Service @Controller

Java:

/**
 * @Component:创建对象的,等同于<bean>的功能
 *      属性:value 就是对象的名称,也就是bean的id值
 *            value的值是唯一的,创建的对象在整个spring容器中就一个。
 *
 *      位置:在类的上面书写
 *
 *      @Component(value = "myStudent")等同于:
 *          <bean id = "myStudent" calss = "com.sss.ch01.Student"/>
 *          表示创建的是Student类的对象,名字叫做myStudent
 *
 *		spring中和@Component 功能一致,创建对象的注解还有:
 *          1.@Repository(用在持久层类的上面):放在dao的实现类上面,表示创建dao对象,dao对象是能访	*				问数据库的。
 *          2.@Service(用在业务层类的上面):放在service的实现类上面,创建service对象,service对象	*				是做业务处理的。
 *              可以有事务等功能的。
 *          3:@Controller(用在控制器的上面):放在控制器(处理器)类的上面,创建控制器对象的,
 *              控制器对象,能够接受用户提交的参数,显示请求的处理结果。
 *
 *          以上三个注解的使用语法和@Component 一样的,都能创建对象,但是这三个注解还有额外的功能。
 *
 *          @Repository,@Service,@Controller 是给项目的对象分层的。
 */
//使用value属性,指定对象的名称
//@Component(value = "myStudent")

//可以省略value,(最常用)
@Component("myStudent")

//不指定对象名称,由spring提供默认名称:类名,首字母是小写
//@Component
public class Student {
    private String name;
    private int age;

    public Student() {
        System.out.println("无参构造执行了!");
    }
}

spring配置文件:

<!--声明组件扫描器(component-scan),组件就是java对象
    base-package:用来指定注解在你的项目中的包名。
    component-scan工作方式:spring会扫描遍历base-package指定的包,
        把包中和子包中所有的类,全部扫描,然后找到类中的注解,按照注解的功能创建对象,或给属性赋值。

    (了解)加入了 component-scan 标签,配置文件的变化:
        1.加入了一个新的约束文件spring-context.xsd
        2.给这个新的约束文件起个命名空间的名称:xmlns:context="http://www.springframework.org/schema/context"
-->
<context:component-scan base-package="com.sss.ch01"/>
指定多个包的方式
<!--指定多个包的三种方式-->
<!--第一种方式:使用多次组件扫描器,指定不同的包-->
<context:component-scan base-package="com.sss.ch01"/>
<context:component-scan base-package="com.sss.ch02"/>

<!--第二种方式:使用分隔符(;或,)分割多个包名-->
<context:component-scan base-package="com.sss.ch01;com.sss.ch02"/>

<!--第三种方式:指定父包-->
<context:component-scan base-package="com.sss"/>
@Value

给简单类型赋值

@Component("myStudent")
public class Student {

    /**
     * @Value:简单类型的属性赋值
     *      属性:value 是String类型的,表示简单类型的属性值
     *      位置:
     *          1.在属性定义的上面,无需set方法,推荐使用。
     *          2.或者在set方法的上面。
     */

    @Value(value = "小黑")
    private String name;
    @Value(value = "20")
    private int age;

@Autowired

实现引用类型的赋值

@Component("myStudent")
public class Student {

    @Value(value = "小黑")
    private String name;
    @Value(value = "20")
    private int age;

    /**
     * @Autowried:spring框架提供的注解,实现引用类型的赋值,
     *      spring中通过注解给引用类型赋值,使用的是自动注入原理。支持byName,byType
     *
     * @Autowried:默认使用的是byType自动注入
     *
     * @Autowried:属性:required,是一个boolean类型的,默认是true
     *              required = true:表示引用类型赋值失败,程序报错,并终止执行。推荐使用默认true。
     *              required = false:表示引用类型赋值失败,程序正常执行,引用类型是null。
     *
     * 位置:1.在属性定义的上面,无序set方法。推荐使用
     *      2.或者在set方法的上面
     *
     * 如果要使用byName方式,需要做的是俩注解一起写:
     *  1.在属性上面加入@Autowired
     *  2.在属性的上面加入@Quakufier(value="bean的id"):表示使用指定名称完成赋值。
     */

    //byType自动注入
    //@Autowired

    //byNmae自动注入
    @Autowired(required = false)
    @Qualifier("mySchool")
    private School school;
@Resource

JDK注解@Resource自动注入:引用类型属性赋值

@Component("myStudent")
public class Student {

    @Value(value = "小黑")
    private String name;
    @Value(value = "20")
    private int age;

    /**
     * @Resource:来自jdk中的注解,spring框架提供了对这个注解的功能支持,可以使用它给引用类型赋值
     *            使用的也是自动注入原理,支持byName,byType,默认是byName
     *       位置:
     *          1.在属性的上面定义,无需set方法,推荐使用
     *          2.在set方法上面
     *
     *       默认是byName:先使用byName自动注入,如果byName赋值失败,再使用byType
     *
     * @Resource 如果只使用byName方式,需要增加一个属性 name
     *  name的值是bean的id(名称):@Resource(name = "mySchool")
     *
     */
    @Resource
    private School school;

四:AOP面向切面编程

动态代理:

​ 实现方式:jdk动态代理,使用jdk中的Proxy,Method,InvocationHandler创建代理对象。

​ jdk动态代理要求目标类必须实现接口。

​ cglib动态代理:第三方的工具库,创建代理对象,原理是继承,通过继承目标类,创建子类。

​ 子类就是代理对象,要求目标类不能是final的,方法也不能是final的。

动态代理的作用:

​ 1)在目标类源代码不改变的情况下,增加功能。

​ 2)减少代码的重复。

​ 3)专注业务逻辑代码。

​ 4)解耦合,让你的业务功能和日志,事务等非业务功能分离。

1)AOP简介

AOP(Aspect Orient Programming),面向切面编程。面向切面编程是从动态角度考虑程序运行过程。

**Aspect 切面:**给你的目标类增加的功能,就是切面。

切面的特点:一般都是非业务方法,独立使用的。

**Orient :**面向,对着。

**Programming:**编程。

AOP 底层,就是采用动态代理模式实现的。采用了两种代理:JDK 的动态代理,与 CGLIB的动态代理。

AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程,可通过运行期动态代理实现程序功能的统一维护的一种技术。说白了:Aop就是动态代理的规范化,把动态代理的实现步骤,方法都定义好了,让开发人员都使用统一的方式。

AOP 是 Spring 框架中的一个重要内容。利用 AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

2)怎么理解面向切面编程?

​ 1)需要在分析项目功能时,找出切面。

​ 2)合理的安排切面的执行时间(在目标方法前,还是在目标方法后)。

​ 3)合理的安排切面的位置,在那个类,那个方法增加增强功能。

3)术语

​ 1)Aspect:切面,表示增强的功能,就是一堆代码完成某一个功能。非业务功能。

​ 常见的切面功能有日志,事务,统计信息,参数检查,权限验证。

​ 2)JoinPoint:连接点,连接业务方法和切面的位置。就是某类中的业务方法。

​ 3)Pointcut:切入点,指多个连接点方法的集合。多个方法。

​ 4)目标对象:给那个类的方法增加功能,这个类就是目标对象。

​ 5)Advice:通知,通知表示切面功能执行的时间。

一个切面有三个关键的要素:

​ 1)切面的功能代码,切面干什么。

​ 2)切面的执行位置,使用Pointcut表示切面执行的位置。

​ 3)切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标方法之后。

4)Aop的是实现

​ aop是一个规范,是动态的一个规范化,一个标准。

aop的技术实现框架:

​ 1.Spring:spring在内部实现了aop规范,,能做aop的工作

​ spring主要在事务处理时使用aop。

​ 我们项目开发中很少使用spring的aop实现,因为spring的aop比较笨重。

​ 2.aspectJ:一个开源的专门做aop的框架。spring框架中集成了aspectJ框架,

​ 通过spring就能使用aspectJ的功能。

aspectJ框架实现aop有两种方式:

​ 1.使用xml的配置文件:配置全局事务。

​ 2.使用注解,我们在项目中要做aop功能,一般都使用注解,aspectJ有5个注解。

5)学习AspectJ框架的使用

1)切面的执行时间

​ 这个执行时间在规范中叫做Advice(通知,增强)

​ 在aspectJ框架中使用注解表示的。也可以使用xml配置文件中的标签。

​ 1)@Before

​ 2)@AfterReturning

​ 3)@Around

​ 4)@AfterThrowing

​ 5)@After

2)AspectJ的切入点表达式

​ AspectJ 定义了专门的表达式用于指定切入点。表达式的原型是:

execution(modifiers-pattern? ret-type-pattern 
declaring-type-pattern? name-pattern(param-pattern)
throws-pattern?)

解释:

modifiers-pattern] 访问权限类型

ret-type-pattern 返回值类型

declaring-type-pattern 包名类名

name-pattern(param-pattern) 方法名(参数类型和参数个数)

throws-pattern 抛出异常类型

?表示可选的部分

以上表达式共 4 个部分。

execution(访问权限 方法返回值 方法声明(参数) 异常类型)

6)AspectJ基于注解的AOP实现步骤(掌握)

实现步骤:

使用aop:目的是给已经存在的一些类和方法,增加额外的功能。前提是不改变原来的代码。

使用AspectJ实现aop的基本步骤:
    1.新建项目
    2.加入依赖
        1)spring依赖
        2)aspectJ依赖
        3)junit单元测试
    3.创建目标类:有接口和它的实现类
        要做的是给类中的方法增加功能。

    4.创建切面类:普通类
        创建要求:
            1)在类的上面加入 @Aspect
            2)在类中定义方法,方法就是切面要执行的功能代码
                在方法的上面加入aspectJ中的通知注解,例如@Before
                还需要指定切入点表达式execution()

    5.创建spring配置文件,声明对象,把对象交给容器统一管理。
        声明对象可以使用注解,或者xml配置文件<bean>
        1)声明目标对象
        2)声明切面对象
        3)声明aspectJ框架中的自动代理生成器标签。
            自动代理生成器:用来完成代理对象的自动创建功能的。

    6.创建测试类,从spring容器中获取目标对象(实际上就是代理对象)
        通过代理执行方法,实现aop的功能增强。

实现代码:

接口:

public interface SomeService {
    void doSome(String name,Integer age);
}

目标类:

/*使用注解:声明目标类对象*/
@Service("someserviceimpl")
public class SomeServiceImpl implements SomeService {

    @Override
    public void doSome(String name,Integer age) {
        //给doSome方法增加一个功能,在doSome()执行之前,输出方法的执行时间。
        System.out.println("=======目标方法doSome=======");
    }
}
@Before:前置通知

切面类:

**
 * @AspectAspectJ框架中的注解。
 *  作用:表示当前类是切面类
 *  切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码。
 */
/*使用注解:声明切面类对象*/
@Component("myaspect")
@Aspect
public class MyAspect {

    /**
     * 定义方法:实现切面功能的。
     * 方法的定义要求:
     *  1.公共方法 public
     *  2.方法没有返回值
     *  3.方法名称自定义
     *  4.方法可以有参数,也可以没有参数。如果有参数,参数不是自定义的,
     *      有几个参数类型可以使用,后面会讲。
     */

    /**
     * @Before:前置通知注解
     *  属性:value:是切入点表达式,表示切面的功能执行的位置。
     *  位置:方法的上面
     *  例如:@Before(value = "execution(* com.sss.ch01.SomeServiceImpl.doSome(..))")
     *  特点:
     *      1.在目标方法之前先执行的
     *      2.不会改变目标方法的执行结果
     *      3.不会影响目标方法的执行。
     */

    /**
     * 指定通知方法中的参数:JoinPoint
     *  JoinPoint:业务方法,要加入切面功能的业务方法。
     *      作用是:可以在通知方法中获取方法执行时的信息,例如方法名称,方法的实参。
     *      如果你的切面功能需要用到方法的信息,就加入JoinPoint。
     *      这个JoinPoint参数的值是由框架赋予的,必须是第一个位置的参数。
     */
    @Before(value = "execution(* com.sss.ch01.SomeServiceImpl.doSome(..))")
    public void someDate(JoinPoint jp){
        //获取方法完整的定义
        System.out.println("方法的签名(定义):"+jp.getSignature());
        System.out.println("方法的名称:"+jp.getSignature().getName());
        //获取方法的实参
         Object[] args = jp.getArgs();
         for (Object arg:args){
             System.out.println("方法的参数:"+arg);
         }
        //这就是切面要执行的功能代码
        System.out.println("输出日志时间:"+new Date());
    }
}

spring配置文件:

<!--声明组件扫描器-->
<context:component-scan base-package="com.sss.ch01"/>

<!--声明自动代理生成器:使用aspectJ框架内部的功能,创建目标对象的代理对象。
    创建代理对象是在内存中实现的,修改目标对象的内存中的结构。创建为代理对象。
    所以目标对象就是一个被修改后的代理对象。

    aspectj-autoproxy:会把spring容器中的所有的目标对象,一次性都生成代理对象。
-->
<aop:aspectj-autoproxy/>

Test:

public class Test01 {
    @Test
    public void test01(){
        String config = "ch01/applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        SomeService service = (SomeService) ac.getBean("someserviceimpl");

        //com.sun.proxy.$Proxy14:jdk动态代理,目标类必须有接口。
        System.out.println(service.getClass().getName());

        service.doSome("小黑",20);
    }
}
@AfterReturning:后置通知及JoinPoint
@Component("myaspect")
@Aspect
public class MyAspect {

    /**
     * 后置方法定义方法:实现切面功能的。
     * 方法的定义要求:
     *  1.公共方法 public
     *  2.方法没有返回值
     *  3.方法名称自定义
     *  4.方法可以有参数的,推荐是Object,参数名是自定义的
     */

    /**
     * @AfterReturning 后置通知
     *  属性:1.value 切入点表达式
     *        2.returning 自定义的变量,表示目标方法的返回值的。
     *          自定义的变量名必须和通知方法的形参名一样。
     *  位置:在方法定义的上面
     *  特点:
     *      1.在目标方法之后执行的。
     *      2.能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能。
     *      3.可以修改这个返回值。
     *
     *  后置通知的执行:
     *      先执行:Object res = doOther();
     *      后执行:someDate(Object res);
     * @param res
     */
    @AfterReturning(value = "execution(* com.sss.ch02.SomeServiceImpl.doOther(..))",returning = "res")
    public void someDate(Object res){
        //Object res:是目标方法执行后的返回值,根据返回值做你的切面的功能处理。

        System.out.println("后置通知:在目标方法之后输出日志时间:"+new Date());
        System.out.println("返回值是:"+res);
    }
}
@Around:环绕通知
/*使用注解:声明切面类对象*/
@Component("myaspect")
@Aspect
public class MyAspect {

    /**
     * 环绕通知方法的定义格式
     *  1.public
     *  2.必须有一个返回值,推荐使用Object
     *  3.方法名称自定义
     *  4.方法有参数,固定的参数 ProceedingJoinPoint
     * @param res
     */
    /**
     * @Around:环绕通知
     *  属性:value 切入点表达式
     *  位置:在方法的定义上面
     * 特点:
     *  1.它是功能最强的通知
     *  2.在目标方法的前和后都能增强功能
     *  3.控制目标方法是否被调用执行
     *  4.可以修改原来的目标方法的执行结果。影响最后的调用结果。
     *
     *  环绕通知,等同于jdk动态代理,InvocationHandler接口。
     *
     *  参数:ProceedingJoinPoint 就等同于Method
     *      作用:执行目标方法的。
     *  返回值(Object):就是目标方法的执行结果,可以被修改。
     *
     *  环绕通知:经常做事务,在目标方法之前开启事务,执行目标方法,在目标方法之后提交事务。
     * @param
     */
    @Around(value = "execution(* com.sss.ch03.SomeServiceImpl.doOther(..))")
    public Object someDate(ProceedingJoinPoint pj) throws Throwable {

        String name = "";
        //获取目标方法第一个参数的值
        Object[] args = pj.getArgs();
        if (args != null && args.length > 1){
            name = (String) args[0];
        }

        //实现环绕通知
        Object result = null;
        System.out.println("环绕通知:在目标方法之前,输出时间:"+new Date());

        if ("小黑".equals(name)){
            //1.表示目标方法的调用,result相当于目标方法的执行结果。
            result = pj.proceed();   //method.invoke()
        }

        //2.在目标方法的前或者后加入功能。
        System.out.println("环绕通知:在目标方法之后,提交事务!");

        return result;
    }
}
@AfterThrowing:异常通知
/**
     * 异常通知方法的定义格式:
     *  1.public
     *  2.没有返回值
     *  3.方法名称自定义
     *  4.方法有一个Exception参数,如果还有,就是JoinPoint
     */

    /**
     * @AfterThrowing 异常通知
     *  属性:1.value 切入点表达式
     *        2.throwing 自定义的变量,表示目标方法抛出的异常
     *          变量名必须和参数的变量名一样。
     *  特点:
     *      1.在目标方法抛出异常时执行的。
     *      2.可以做异常的监控,监控目标方法执行时是不是有异常。
     *          如果有异常,可以发送邮件,短信进行通知。
     */
    @AfterThrowing(value = "execution(* com.sss.ch03.SomeServiceImpl.dofrist())",throwing = "ex")
    public void myAfterThrowing(Exception ex){
        System.out.println("异常通知:方法发生异常时,异常原因:"+ex.getMessage());
    }
@After:最终通知

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

/**
 * 最终通知方法的定义格式:
 *  1.public
 *  2.没有返回值
 *  3.方法名称自定义
 *  4.方法没有参数,如果还有,就是JoinPoint
 */

/**
 * @After:最终通知
 *  属性:value 切入点表达式
 *  位置:在方法的上面
 *
 * 特点:
 *  1.无论目标方法是否抛出异常,该增强均会被执行。
 *  2.在目标方法之后执行。
 */
@After(value = "execution(* com.sss.ch03.SomeServiceImpl.dofrist2())")
public void myAfter(){
    System.out.println("执行最终通知,总是会被执行!");
    //一般是做资源清除工作的。
}
@Pointcut 定义切入点

当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维护均较为麻烦。AspectJ 提供了@Pointcut 注解,用于定义 execution 切入点表达式。

**用法:**将@Pointcut 注解在一个方法之上,以后所有的 execution 的 value 属性值均可使用该方法名作为切入点。代表的就是@Pointcut 定义的切入点。这个使用@Pointcut 注解的方法一般使用 private 的标识方法,即没有实际作用的方法。

@After(value = "mypt()")
public void myAfter(){
    System.out.println("执行最终通知,总是会被执行!");
    //一般是做资源清除工作的。
}

@Pointcut(value = "execution(* com.sss.ch03.SomeServiceImpl.dofrist2())")
private void mypt(){
    //无需代码
}

7)动态代理的使用方式

<!--
    如何你期望目标类有接口,使用cglib动态代理
    proxy-target-class="true":告诉框架,你要使用cglib动态代理
-->
<aop:aspectj-autoproxy proxy-target-class="true"/>

五:Spring集成myBatis

​ 用的技术是:ioc。

​ 为什么ioc:能够把myBatis和spring集成在一起,像一个框架,是因为ioc能够创建对象。可以把myBatis框架中的对象交给spring统一创建,开发人员从spring中获取对象。开发人员就不用同时面对两个或多个框架了,就面对一个spring框架。

mybatis使用步骤,对象
1.定义dao接口 ,StudentDao
2.定义mapper文件 StudentDao.xml
3.定义mybatis的主配置文件 mybatis.xml
4.创建dao的代理对象, StudentDao dao = SqlSession.getMapper(StudentDao.class);

List students = dao.selectStudents();

要使用dao对象,需要使用getMapper()方法,
怎么能使用getMapper()方法,需要哪些条件
1.获取SqlSession对象, 需要使用SqlSessionFactory的openSession()方法。
2.创建SqlSessionFactory对象。 通过读取mybatis的主配置文件,能创建SqlSessionFactory对象

需要SqlSessionFactory对象, 使用Factory能获取SqlSession ,有了SqlSession就能有dao , 目的就是获取dao对象
Factory创建需要读取主配置文件

我们会使用独立的连接池类替换mybatis默认自己带的, 把连接池类也交给spring创建。

主配置文件:

1.数据库信息 
<environment id="mydev">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!--数据库的驱动类名-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <!--连接数据库的url字符串-->
                <property name="url" value="jdbc:mysql://localhost:3306/springdb"/>
                <!--访问数据库的用户名-->
                <property name="username" value="root"/>
                <!--密码-->
                <property name="password" value="123456"/>
            </dataSource>

2. mapper文件的位置
   <mappers>
        <mapper resource="com/bjpowernode/dao/StudentDao.xml"/>
        <!--<mapper resource="com/bjpowernode/dao/SchoolDao.xml" />-->
    </mappers>

==============================================================
通过以上的说明,我们需要让spring创建以下对象
1.独立的连接池类的对象, 使用阿里的druid连接池
2.SqlSessionFactory对象
3.创建出dao对象

需要学习就是上面三个对象的创建语法,使用xml的bean标签。

spring和myBatis的集成:

ch07-ioc-myBatis:spring和myBatis的集成

步骤:
    1.新建maven项目
    2.加入maven的依赖
        1)spring依赖
        2)mybatis依赖
        3)mysql驱动
        4)spring的事务依赖
        5)mybatis和spring集成的依赖,mybatis官方提供的,用来在spring项目中创建mybatis
            的SqlSessionFactory,dao对象的。

    3.创建实体类
    4.创建dao接口和mapper文件
    5.创建mybatis主配置文件
    6.创建Service接口和实现类,属性是dao
    7.创建spring的配置文件:声明mybatis的对象交给spring创建
        1)数据源
        2)SqlSessionFactory
        3)dao对象
        4)声明自定义的service

    8.创建测试类,获取Service对象,通过service调用dao完成数据库的访问。

实际步骤代码:

加入maven依赖:

<!--导入阿里镜像,方便下载-->
  <repositories>
    <repository>
      <id>aliyunmaven</id>
      <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
    </repository>
  </repositories>

<!--单元测试-->
<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.11</version>
  <scope>test</scope>
</dependency>

<!--myBatis依赖-->
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>3.5.5</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.49</version>
</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>

<!--阿里公司的数据库的连接池-->
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.1.12</version>
</dependency>

<build>
 <resources>
      <resource>
        <!--所在的目录-->
        <directory>src/main/java</directory>
        <includes><!--包括目录下的.properties,.xml文件都会被扫描到-->
          <include>**/*.properties</include>
          <include>**/*.xml</include>
        </includes>
        <!--filtering 选项 false 不启用过滤器,*.property 已经起到过滤的作用了-->
        <filtering>false</filtering>
      </resource>
    </resources>

  </build>

创建实体类:

public class Student {
    private Integer id;
    private String name;
    private Integer age;
	//构造和set,get方法省略
}

Dao接口和mapper文件:

public interface StudentDao {

    /*插入学生*/
    int insertStudent(Student student);

    /*查询学生*/
    List<Student> selectList();
}
<?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.sss.dao.StudentDao">
    <!--插入信息-->
    <insert id="insertStudent">
        insert into student values(#{id},#{name},#{age})
    </insert>
    
    <!--查询学生信息-->
    <select id="selectList" resultType="com.sss.entity.Student">
        select id,name,age from student
    </select>
</mapper>

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

    <!--设置别名-->
    <typeAliases>
        <!--name:实体类所在的包名-->
        <package name="com.sss.entity"/>
    </typeAliases>

    <!-- sql mapper(sql映射文件)的位置-->
    <mappers>
        <!--
            name:是包名,这个包中的所有mapper.xml文件都能够加载。
        -->
        <package name="com.sss.dao"/>
    </mappers>
</configuration>

Service接口和实现类:

public class StudentServiceImpl implements StudentService {

    //引用类型
    private StudentDao studentDao;

    //使用set注入,赋值
    public void setStudentDao(StudentDao studentDao) {
        this.studentDao = studentDao;
    }

    /*添加学生*/
    @Override
    public int addStudent(Student student) {
        int nums = studentDao.insertStudent(student);
        return nums;
    }

    /*查询学生*/
    @Override
    public List<Student> queryStudent() {
        List<Student> students = studentDao.selectList();
        return students;
    }
}
public interface StudentService {

    /*添加学生*/
    int addStudent(Student student);

    /*查询学生信息*/
    List<Student> queryStudent();
}

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

   <!--
        把数据库的配置信息,写在一个独立的文件,便于修改数据库的配置内容
        让spring知道jdbc.properties文件的位置。
    -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    
    <!--声明数据源DataSource,作用是连接数据库的-->
    <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <!--set注入给DruidDataSource提供连接数据库信息-->
        <!--也可以使用属性配置文件中的数据,语法:${key}-->
        <property name="url" value="jdbc:mysql://localhost:3306/springdb"/>
        <property name="username" value="root"/>
        <property name="password" value="999"/>
        <!--最大连接数-->
        <property name="maxActive" value="20"/>
    </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(Student.class)
        MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象。
    -->
    <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="com.sss.dao"/>
    </bean>

    <!--声明service-->
    <bean id="studentService" class="com.sss.service.Impl.StudentServiceImpl">
        <property name="studentDao" ref="studentDao"/>
    </bean>
</beans>

测试类:

@Test
public void test04(){
    String config = "applicationContext.xml";
    ApplicationContext ac = new ClassPathXmlApplicationContext(config);
    //获取spring容器中的service对象
    StudentService studentService = (StudentService) ac.getBean("studentService");

    List<Student> students = studentService.queryStudent();
    for (Student stu : students){
        System.out.println(stu);
    }

}

六:Spring事务

1)问题

1.什么是事务?

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

2.在什么时候使用事务?

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

​ 在java代码中写程序,控制事务,此时事务应该放在哪里呢?

​ 应该放在service类的业务方法上,因为业务方法会调用多个dao方法,执行多个sql语句。

3.通常使用JDBC访问数据库,还有myBatis访问数据库怎么处理事务?

​ jdbc访问数据库,处理事务 Connection conn; conn.commit(); conn.rollback();

​ mybatis访问数据库,处理事务,sqlSession.commit(); sqlSession.rollback();

​ hibernate访问数据库,处理事务,Session.commit(); Session.rollback();

4.第三个问题中事务的处理方式,有什么不足?

​ 1)不同的数据库访问技术,处理事务的对象,方法不同。

​ 需要了解不同的数据库访问技术使用事务的原理。

​ 2)需要掌握多种数据库中事务的处理逻辑,什么时候提交事务,什么时候回滚事务。

​ 3)处理事务的多种方法。

​ 总结:就是多种数据库的访问技术,有不同的事务处理的机制,对象,方法。

5.怎么解决不足?

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

​ 使用spring的事务处理机制,可以完成mybatis访问数据库的事务处理。

​ 使用spring的事务处理机制,可以完成hibernate访问数据库的事务处理。

2)处理事务,需要怎么做,做什么?

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

1)事务内部提交,回滚事务,使用的事务管理器对象,代替你完成commit,rollback

​ 事务管理器是一个接口和他的众多实现类。
​ 接口:PlatformTransactionManager ,定义了事务重要方法 commit ,rollback
​ 实现类:spring把每一种数据库访问技术对应的事务处理类都创建好了。
​ mybatis访问数据库—spring创建好的是DataSourceTransactionManager
​ hibernate访问数据库----spring创建的是HibernateTransactionManager

怎么使用:你需要告诉spring 你用是那种数据库的访问技术,怎么告诉spring呢?
声明数据库访问技术对于的事务管理器实现类, 在spring的配置文件中使用声明就可以了
例如,你要使用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_REQUIRES_NEW
​ PROPAGATION_SUPPORTS
​ 以上三个需要掌握的

​ PROPAGATION_MANDATORY
​ PROPAGATION_NESTED
​ PROPAGATION_NEVER
​ PROPAGATION_NOT_SUPPORTED

3)spring提交事务,回滚事务的时机

​ 1)当你的业务方法,执行成功,没有异常抛出,当方法执行完毕,spring在方法执行后提交事务。事务管理器commit
​ 2)当你的业务方法抛出运行时异常或ERROR, spring执行回滚,调用事务管理器的rollback
​ 运行时异常的定义: RuntimeException 和他的子类都是运行时异常, 例如NullPointException , NumberFormatException

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

总结spring的事务
1.管理事务的是 事务管理器和他的实现类
2.spring的事务是一个统一模型
1)指定要使用的事务管理器实现类,使用
2)指定哪些类,哪些方法需要加入事务的功能
3)指定方法需要的隔离级别,传播行为,超时

你需要告诉spring,你的项目中类信息,方法的名称,方法的事务传播行为。

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

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

使用@Transactional的步骤:
1.需要声明事务管理器对象

<!--使用spring的事务处理-->
    <!--声明事务管理器-->
    <bean id="TransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--连接的数据库-->
        <property name="dataSource" ref="myDataSource"/>
    </bean>

2.开启事务注解驱动, 告诉spring框架,我要使用注解的方式管理事务。

<!--声明事务注解驱动,告诉spring使用注解管理事务,创建代理对象
    transaction-manager:事务管理器的id
-->
<tx:annotation-driven transaction-manager="TransactionManager"/>

3.在你的方法的上面加入@Trancational

@Transactional(
        /*事务的传播行为*/
        propagation = Propagation.REQUIRED,
        /*隔离级别*/
        isolation = Isolation.DEFAULT,
        /*:用于设置该方法对数据库的操作是否是只读的,false表示不是只读,true为只读,用在查询语句上*/
        readOnly = false,
        /*指定需要回滚的异常类,类型为 Class[],数组*/
        rollbackFor = {
                NullPointerException.class,
                NotEnoughException.class
        }
)
//如果只写这个,使用的是事务控制的默认值,与上边的一堆代码一致。默认抛出运行时异常时,回滚事务。
@Transactional	
@Override
public void buy(Integer goodsId, Integer nums) {
    System.out.println("buy方法的开始===============");
    //记录销售信息,向sale表添加记录
    Sale sale = new Sale();
    sale.setGid(goodsId);
    sale.setNums(nums);
    saleDao.insertSale(sale);

    //查询商品
    Goods goods = goodsDao.selectGoods(goodsId);
    if (goods == null){
        //商品不存在
        throw new NullPointerException("编号是:"+goodsId+"商品不存在");
    }else if (goods.getAmount() < nums){
        //商品库存不足
        throw new NotEnoughException("编号是:"+goodsId+"商品库存不足");
    }

    //更新库存
    Goods buyGoods = new Goods();
    buyGoods.setId(goodsId);
    buyGoods.setAmount(nums);

    goodsDao.updateGoods(buyGoods);
    System.out.println("buy方法的结束===============");
}

spring内部使用aop机制,创建@Transactional所在的类代理对象,给方法加入事务的功能。
spring给业务方法加入事务:
在你的业务方法执行之前,先开启事务,在业务方法之后提交或回滚事务,内部使用aop的环绕通知

如下方代码:

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

2)声明事务管理器对象

<!--声明事务管理器-->
<bean id="TransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--连接的数据库-->
    <property name="dataSource" ref="myDataSource"/>
</bean>

  1. 声明方法需要的事务类型(配置方法的事务属性【隔离级别,传播行为,超时】)
<!--声明业务方法它的事务属性(隔离级别,传播行为,超时时间)
    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,com.sss.exce.NotEnoughException"/>
        <!--使用通配符,指定很多的方法-->
       <!-- <tx:method name="add*" propagation="REQUIRED"/>-->
    </tx:attributes>
</tx:advice>
  1. 配置aop:指定哪些哪类要创建代理。
<!--配置aop :通知应用的切入点-->
<aop:config>
    <!--配置切入点表达式,指定那些包中的类,要应用事务
        id:切入点表达式的名称,唯一值
        expression:切入点表达式,指定那些类要应用事务
    -->
    <aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>

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

七:spring与Web

1.做的是javase项目有main方法的,执行代码是执行main方法的,
在main里面创建的容器对象
ApplicationContext ctx = new ClassPathXmlApplicationContext(“applicationContext.xml”);

2.web项目是在tomcat服务器上运行的。 tomcat一起动,项目一直运行的。

需求:
web项目中容器对象只需要创建一次, 把容器对象放入到全局作用域ServletContext中。

怎么实现:
使用监听器 当全局作用域对象被创建时 创建容器 存入ServletContext

监听器作用:
1)创建容器对象,执行

ApplicationContext ctx = new ClassPathXmlApplicationContext(“applicationContext.xml”);

2)把容器对象放入到ServletContext, ServletContext.setAttribute(key,ctx)

监听器可以自己创建,也可以使用框架中提供好的ContextLoaderListener

web.xml中使用监听器:

<!--注册监听器ContextLoaderListener
    监听器被创建对象后,会读取[/WEB-INF/applicationContext.xml]这个文件。
    为什么要去读取这个文件?
        因为在监听器中要创建ApplicationContext对象,需要加载配置文件。
        [/WEB-INF/applicationContext.xml]就是监听器默认读取的spring配置文件路径。

        可以修改默认的文件位置,使用context-parm重新执行文件的位置

配置监听器:目的是创建容器对象,创建了容器对象,applicationContext.xml配置文件中所有的对象都创建好
-->
<context-param>
    <!--contextConfigLocation:表示配置文件的路径-->
    <param-name>contextConfigLocation</param-name>
    <!--自定义配置文件的路径-->
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
    <listener-class>
        org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>

源码:

private WebApplicationContext context;
public interface WebApplicationContext extends ApplicationContext

 ApplicationContext:javase项目中使用的容器对象
WebApplicationContext:web项目中的使用的容器对象

把创建的容器对象,放入到全局作用域
 key: WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
       WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
 value:this.context

 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

每日一道Java面试题

记得关注我【Java有话说
在这里插入图片描述

  • 6
    点赞
  • 86
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柳落青

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值