Spring

Spring

1.spring概述

spring全家桶:spring,springmvc,springboot,springcloud

spring:spring的出现是为了解决企业开发难度,减轻对项目模块之间的管理,类和类之间的管理,帮助开发人员创建对象,管理对象之间的关系,spring的核心技术iocaop,能实现模块之间,类之间的解耦合

依赖:一个类使用另一个类中的方法

2.框架

框架怎么学:框架是一个软件,其他人写好的软件

(1)知道框架能干什么,mybatis–访问数据库,对表中的数据进行增删改查

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

(3)框架的内部实现:框架内部怎么做,原理是什么

(4)通过学习,实现一个框架

3.spring框架的第一个核心功能–ioc

IOC(Inversion of Control):控制反转,是一个理论,概念,思想

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

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

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

正转:由开发人员在代码中,使用new关键字创建对象,开发人员主动管理对象

容器:是一个服务器软件,一个框架(spring)

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

3.1.java创建对象的方式
  1. 构造方法,new Student();
  2. 反射
  3. 序列化
  4. 克隆
  5. ioc:容器创建对象
  6. 动态代理
3.2.ioc的体现

servlet:

  1. 创建类继承HttpServlet
  2. 在web.xml中注册servlet,使用myservlet,com.lkw.MyServlet
  3. 没有创建servlet对象,因为没有使用new关键字
  4. servlet是tomcat服务器能够帮你创建的,tomcat也称为容器,tomcat作为容器,里面存放的有servlet对象,Listener,Filter对象
3.3.ioc技术的实现

DI是ioc的技术实现

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

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

3.4.使用spring创建对象的案例:
实现步骤:
  1. 创建maven项目
  2. 加入maven的依赖:spring的依赖,版本5.2.5,junit单元测试依赖
  3. 创建类(接口和它的实现类),和没有使用框架一样,就是普通的类
  4. 创建spring需要使用的配置文件,声明类的信息,这些类由spring创建和管理
  5. 测试spring创建对象
  6. 调用对象的方法

spring-context和spring-webmvc是spring中的两个模块

spring-context:是ioc功能的,创建对象的

spring-webmvc:做web开发使用的,是servlet的升级

spring-webmvc中也可以用到spring-context中创建对象的功能的

创建对象的代码:

 public void testStudent02(){
        //声明spring的配置文件
        String config = "beans.xml";
        //创建spring容器对象
        //spring默认创建对象的时机:在spring创建容器的时候,会将beans标签中的所有bean对象创建出来
        //spring创建对象默认调用的是无参数的构造方法
        ApplicationContext app = new ClassPathXmlApplicationContext(config);//对象是在这句代码这里完成对象创建的
        Student student =(Student) app.getBean("MyStudent");
        //调用对象的方法
        student.fn();
    }

对以上过程的理解:以上过程就体现了spring的ioc,即控制反转,就是将对象的创建交给spring容器来管理,可以把application当作是spring容器,然后所有的对象都要放在标签中,也就是只要是放在标签中的对象,spring容器在创建的时候都会调用相关类的构造方法创建对象

####4.使用spring完成对象的创建和相关属性的赋值

4.1.使用spring创建对象,并对实体类对象的属性完成赋值的步骤:

第一步:创建maven项目,加入spring的依赖和juint依赖

第二步:创建实体类对象Student

public class Student {
    private String name;
    private int age;

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

第三步:在resources目录下,创建一个application.xml文件中,将Student类放到spring容器中

<bean id="student01" class="com.lkw.test01.entity01.Student">
        <!--在bean标签的内部为Student的属性注入值-->
        <property name="name" value="lkw"></property>
        <property name="age" value="20"></property>
</bean>
<!--以上方式使用set注入的方式,这是使用最多的方式
需要注意的是:以上是对Student类对象的简单数据类型进行set注入,前提是在这个实体类中有相关属性的set方法才可以,就拿上面的代码来说,name和age是Student对象中的属性,是简单数据类型,并且有相关的set方法,这样就可以在bean标签的内部使用property标签完成属性的赋值,既然是这样,那么就可以通过name属性的值,推出相关的对象的set方法的名称
-->

第四步:在test目录下创建相应的测试方法

public class TestStudent01 {
    /*
    使用set注入的方式为对象的属性赋值
    * */
    @Test
    public void test01(){
        //获取容器对象
        String config = "di01/application.xml";
        ApplicationContext app = new ClassPathXmlApplicationContext(config);
        //获取学生对象
        Student student =(Student) app.getBean("student01");
        System.out.println(student);
    }
}
4.2.使用spring创建对象,并使用set注入的方式对对象的引用数据类型的属性进行赋值
<bean id="student01" class="com.lkw.test01.entity02.Student">
        <!--在备案标签的内部为Student的属性注入值-->
        <property name="name" value="lkw"></property>
        <property name="age" value="20"></property>
        <!--为引用数据类型赋值-->
        <property name="school" ref="school01"></property>
    </bean>
    <!--创建school对象-->
    <bean id="school01" class="com.lkw.test01.entity02.School">
        <!--先对School对象进行赋值-->
        <property name="name" value="XUPT"></property>
        <property name="address" value="郭杜街道"></property>
</bean>

如果属性是引用数据类型,需要再声明一个bean标签,然后依照之前的方式,对简单数据类型进行赋值,然后在要使用的类的bean标签中引用已经定义好的引用数据类型的id

4.3.使用spring创建对象,并使用构造方法对对象的属性进行赋值
<bean id="student03" class="com.lkw.test01.entity03.Student">
        <!--方法一:使用Student的构造方法创建对象-->
<!--        <constructor-arg name="myname" value="lkw"></constructor-arg>-->
<!--        <constructor-arg name="myage" value="20"></constructor-arg>-->
<!--        <constructor-arg name="myschool" ref="school03"></constructor-arg>-->
        <!--方法二:使用index来创建对象-->
<!--        <constructor-arg index="0" value="lkw"></constructor-arg>-->
<!--        <constructor-arg index="1" value="20"></constructor-arg>-->
<!--        <constructor-arg index="2" ref="school03"></constructor-arg>-->

        <!--方法三:使用index的省略形式,但是需要注意的是书写的顺序必须和构造方法上的形参的顺序一致-->
        <constructor-arg value="lkw"></constructor-arg>
        <constructor-arg value="20"></constructor-arg>
        <constructor-arg ref="school03"></constructor-arg>

        <!--总结:建议使用第一种方式创建对象-->

    </bean>
    <!--School对象-->
    <bean id="school03" class="com.lkw.test01.entity03.School">
        <!--为school对象进行初始化-->
        <property name="name" value="Princeton"></property>
        <property name="address" value="USA"></property>
    </bean>
4.4.使用spring创建对象,并使用spring的语法规则(byName)对属性进行赋值
<!--使用spring提供的语法规则对对象中的引用数据类型的属性进行赋值-->
    <!--表示使用通过属性名的方式进行自动注入,就是说bean的id和要被注入的类中的这个属性名一致-->
    <bean id="student04" class="com.lkw.test01.entity04.Student" autowire="byName">
        <!--简单数据类型没有办法完成赋值操作-->
        <property name="name" value="lkw"></property>
        <property name="age" value="20"></property>
    </bean>
    <bean id="school" class="com.lkw.test01.entity04.School" ><!--因为在Student对象中有一个School类型的变量,变量名叫做school,因此在使用
    byname的形式进行属性值的注入的时候,要确保,School的bean标签的id值等于Student类的这个school变量的变量名
    -->
        <!--对里面的属性进行赋值-->
        <property name="name" value="Duke"></property>
        <property name="address" value="USA"></property>
    </bean>
4.5.使用spring创建对象,并使用spring的语法规则(byType)对属性进行赋值
    <!--使用spring提供的语法规则对对象中的引用数据类型的属性进行赋值-->
    <!--使用spring中按照语法规则(通过类型)对属性进行赋值-->
    <!--
        byType(按照类型注入):java类中引用类型的数据类型和spring容器中(配置文件)<bean>的class属性是同源关系的,这样的bean能够赋值给引用数据类型
        同源就是一类的意思:
            1.java类中引用数据类型和bean的class的值是一样的
            2.java类中引用数据类型和bean的class的值是父子关系
            3.java类中应用数据类型和bean的class的值是接口和实现类的关系

    -->
    <bean id="student05" class="com.lkw.test01.entity05.Student" autowire="byType">
        <!--简单数据类型没有办法完成赋值操作-->
        <property name="name" value="lkw"></property>
        <property name="age" value="20"></property>
    </bean>
    <bean id="school" class="com.lkw.test01.entity05.School" >
        <!--对里面的属性进行赋值-->
        <property name="name" value="Duke"></property>
        <property name="address" value="USA"></property>
    </bean>

注意:以上byName和byType都是专门用来创建引用数据类型的对象的,对简单数据类型不适用

多个配置文件

在spring中,为了便于对类进行管理,通常情况下会创建多个配置文件,这样做可以提高程序的效率,还可以实现解耦合

主配置文件:

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

    -->
<!--    <import resource="classpath:di06/spring-school.xml"></import>-->
<!--    <import resource="classpath:di06/spring-student.xml"></import>-->
    <!--
    可以使用通配符的形式一次性指定多个文件,使用*表示匹配所有
    但是需要注意的是:
        1.主配置文件不能包含在通配符的包含范围之内,因为那样的话,spring创建容器对象的时候,当容器扫描到主配置文件的时候,会再次创建主配置文件,这样就形成了死循环
        2.使用通配符的形式指定多个文件的时候,需要将所有的配置文件放在一个directory下面,不能直接放在resources目录下面,否则无法扫描成功

    -->
    <import resource="classpath:/di06/spring-*.xml"></import>

Student的配置文件:

<bean id="student06" class="com.lkw.test01.entity06.Student" autowire="byType">
            <property name="name" value="lkw"></property>
            <property name="age" value="20"></property>
</bean>

School配置文件:

<bean id="school06" class="com.lkw.test01.entity06.School">
        <!--对school的属性进行赋值-->
        <property name="name" value="XUPT"></property>
        <property name="address" value="xian"></property>
</bean>
基于注解的DI

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

使用注解的步骤:

  1. java如maven的依赖,spring-context

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

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

  4. 学习的注解:

    @Component:在spring中相当于bean标签的作用
    @Repository:创建dao对象,用来访问数据库
    @Service:创建service对象,处理业务逻辑,可以有事务功能
    @Controller:创建Controller对象,接收请求,显示处理结果
    @Value:对简单数据类型的变量进行赋值
    @Autowired:对引用数据类型进行赋值,默认是byType的方式,它是spring框架提供的注解
    @Resource:对引用数据类型进行赋值,默认是byName的方式,如果byName不成功的话,就会使用byType注解,它是jdk中的注解
    
注解的使用
@Autowired

1.创建实体类对象

School:

@Component("school01")//使用Component声明类来代替使用bean标签
public class School {
    @Value("Standford")
    private String name;
    @Value("USA")
    private String address;

    @Override
    public String toString() {
        return "School{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

Student:

/*
* 使用component注解创建对象,等同于<bean>的功能,
* 属性:value:就是对象的名称,也就是bean的id值,value的值是唯一的,创建的对象在整个容器中就一个
* 位置:这个注解使用在类的上面
* Component(value="student01"),就等同于<bean id="student01" class="com.lkw.entity01"></bean>
*
* 在spring中和Component注解的功能一致,创建对象的注解还有:
* 1.@Repository:(用在持久层上面):放在dao的实现类上面,表示创建dao对象,dao对象是能够访问数据库的
* 2.@Service(用在业务层上面):放在service的实现类上面,创建service对象,service对象是做业务处理的,可以有事务等功能
* 3.@Controller(用在控制器上面):放在控制器(处理器)类的上面,创建控制器对象的,控制器对象,能够接收用户提交的参数,显示请求的处理结果
* @Repository,@Service,@Controller这三个注解是对项目进行分层的
*
* 使用时机:就是上面的这三个和@Component的作用是一样的,但是还拥有其他额外的功能,当要创建的类有特殊作用的时候,比如控制层的类,就是用@Controller
* 注解来标明控制层的类,以此类推,当不属于这些类的时候,就使用Component
*
*
* */
@Component(value = "student01")
public class Student {
    /*
    简单数据类型的赋值:使用@Value注解
    使用这个注解在类中不需要定义set方法
    */
    @Value(value="lkw")
    private String name;
    @Value(value="20")
    private int age;

    /*
    给引用数据类型赋值,使用Autowired注解,该注解是spring框架提供的,实现引用数据类型的赋值
    spring中通过注解给引用数据类型赋值,使用的是自动注入的原理,支持byName和byType,但是Autowired默认是byType自动注入
    Autowired使用在引用数据类型的属性之上,无需set方法,推荐使用
    Autowired(required=true):Autowired有一个参数值,是boolean类型的,默认为true
    required=true:表示引用类型赋值失败,程序报错,并终止执行,这种情况下要求该bean必须存在
    required=false:引用数据类型如果赋值失败,程序正常执行,引用类型是null
    * */

//    @Autowired//默认是byType的方式完成注入
//    private School school;

    /*使用byName的方式完成属性的注入
    * 步骤:
    * 1.加上Autowired注解
    * 2.再加一个Qualifier(value="bean的id"):表示使用指定名称的bean完成赋值
    * */
    @Autowired
    /*Auto*/
    @Qualifier("school01")
    private School school;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", school=" + school +
                '}';
    }
}

applicationContext.xml

    <!--声明组件扫描器(component-scan),组件就是java对象

      base-package:指定注解在你的项目中的包名
      component-scan工作方式:spring会扫描 遍历base-package指定的包
      把包中和子包中的所有类,找到类中的注解,按照注解的功能创建对象,或给属性赋值
    -->

    <context:component-scan base-package="com.lkw.entity01"></context:component-scan>
    <context:component-scan base-package="com.lkw.entity02"></context:component-scan>

    <!--指定多个包名的三种方式-->
    <!--第一种:使用多次组件扫描器,指定不同的包名-->
<!--    <context:component-scan base-package="com.lkw.entity01"></context:component-scan>-->
<!--    <context:component-scan base-package="com.lkw.entity02"></context:component-scan>-->
    <!--第二种方式:使用分隔符(;或者,)分隔多个包名-->
<!--    <context:component-scan base-package="com.lkw.entity01,com.lkw.entity02"></context:component-scan>-->
    <!--第三种方式:指定父包-->
<!--    <context:component-scan base-package="com.lkw"></context:component-scan>-->
@Resource注解的使用

Student:


@Component(value = "student02")
public class Student {

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

    /*
    * 引用数据类型的赋值:
    * @Resource:作用和@Autowired一样,都是给引用数据类型赋值,@Resource注解都有byName和byType,默认是byName
    * 来自jdk的注解,spring框架提供了这个注解的功能支持,可以使用它完成引用数据类型的赋值,使用的也是自动注入原理
    * 默认是byName:先使用byName自动注入,如果byName赋值失败,再使用byType
    *
    *
    * 位置:
    * 1.在属性的定义上面,无需set方法,推荐使用
    * 2.在set方法上面
    *
    * 在resource注解中可以只使用byName注解完成属性的赋值
    * 使用name属性=bean的id(也就是Component的value属性)
    *
    * */
   @Resource(name="school02")
    private School school;//这里先去entity02包下通过name去找school,结果发现失败,于是就使用byType,然后发现在entity02包下有一个School类型的对象
    //然后将这个对象自动装配到Student类里面

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", school=" + school +
                '}';
    }
}

School类不变

使用配置文件完成类的属性的赋值

配置文件:test.properties

myname=lkk
myage=22

Student:

public class Student {

    //使用属性配置文件完成类的属性的赋值
    @Value("${myname}")
    private String name;
    @Value("${myage}")
    private int age;
}

总结:如果单纯使用配置文件的方式来为对象的属性进行赋值的话,那就不需要使用注解,也不用组件扫描器,组件扫描器是搭配注解一起使用的

4.spring框架的第二个核心技术–aop

4.1.动态代理

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

4.2.动态代理的分类
4.2.1.JDK动态代理

动态代理的实现方式常有两种,使用JDK和Proxy,与通过CGLIB生成代理,jdk的动态要求目标对象必须实现接口,这是java设计上的要求,java的包中提供了三个类支持代理模式:ProxyMethodInvocationHandler

使用JDK的Proxy实现代理,要求目标类与代理类实现相同的接口,若目标类不存在接口,则无法使用该方式实现,但对于无接口的类,要为其创建动态代理,就要使用CGLIB

4.2.2.CGLIB动态代理(了解)

CGLIB(Code Generation Library)是一个开源项目,是一个强大的,高性能的,高质量的Code生成类库,它可以在运行期扩展java类与实现java接口,它广泛被许多AOP框架使用,比如SpringAop

CGLIB代理的生成原理是生成目标类的子类,而子类是增强过的,这个子类对象就是代理对象,所以,使用CGLIB生成动态代理,要求目标类必须能够被继承,即不能是final的类,cglib的代理效率要高于jdk

总结:

有接口并且目标类和代理类实现相同的接口----JDK动态代理
	使用jdk中的Proxy,Method,InvocationHandler创建代理对象,jdk动态代理要求目标类必须实现接口
有无接口都可以使用,但是必须要有继承关系----CGLIb
	原理是继承,通过继承目标类创建子类,子类就是代理对象,要求目标类不能是final的,方法也不能是final的
4.3.动态代理的作用:
  1. 在目标类的源代码不变的情况下,增加功能
  2. 减少代码的重复使用
  3. 专注于业务逻辑代码
  4. 解耦合,让你的业务功能和日志,事务非业务功能分离
4.4.Aop(Aspect Orient Programming)
4.4.1.aop

就是面向切面编程,基于动态代理的,可以使用jdk,cglib两种代理方式,aop就是动态代理的规范化,把动态代理的实现步骤,方式都定义好了,让开发人员用一种统一的方式,使用动态代理

Aspect:切面,给你的目标类则更加的功能,就是切面,像打印日志,提交事务都是切面,切面的特点就是一般都是==非业务方法==,可以独立使用

4.4.2.怎么理解面向切面编程

(1)需要在分析项目功能的时候,找出切面

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

(3)合理安排切面执行的位置,在哪个类,哪个方法增加增强功能

4.4.3.术语

(1)Aspect:切面,表示增强的功能,就是一堆代码,完成某一个功能,非业务功能,常见的切面功能有日志,事务,统计信息,参数检查,权限验证

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

(3)PointCut:切入点,指多个连接点的集合,多个方法

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

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

4.4.4.切面三个关键的要素:
  • 切面的功能代码,就是切面要实现的功能
  • 切面的执行位置,使用PointCut表示切面的执行位置
  • 切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标之后
4.4.5.aop的实现

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

aop的技术实现框架:

1.spring:spring在内部实现了aop框架,能做aop的工作,但是一般不使用

2.aspectJ:一个开源的专门做aop的框架,spring框架中集成了aspectJ框架,通过spring就能使用aspectJ的功能

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

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

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

(1)切面的执行时间,这个执行时间在规范中叫做Advice(通知,增强)在aspectJ框架中使用注解来表示,也可以在xml配置文件中的标签

  1. @Before
  2. @AfterRetruning
  3. @Around
  4. @AfterThrowing
  5. @After

(2)表示切面执行的位置,使用的是切入点表达式

4.4.6.切入点表达式的语法

在这里插入图片描述

在这里插入图片描述

4.4.7.切入点表达式的使用(spring_pro/spring06-aspect)

1.通知:

第一步:创建新模块

第二步:创建一个接口,接口中定义业务方法

第三步:创建接口的实现类,在实现类中重写抽象方法

第四步:定义切面类,在切面类中书写相应的切入点表达式

在这里插入图片描述

SomeService类:

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

SomeServiceImpl类:

public class SomeServiceImpl implements SomeService{

    @Override
    public void doSome(String name, Integer age) {
        System.out.println("============执行doSome方法============");
    }
}

MyAspect类:

//前置通知---在业务方法执行之前执行的方法,一个业务方法可以由若干个前置通知方法
    /*@Before(value="execution(public void com.lkw.ba01.SomeServiceImpl.doSome(String,Integer))")
    public void doBefore(){
        System.out.println("前置方法,在目标方法执行之前执行");
    }*/
    /*@Before(value="execution(* *..do*(..))")
    public void doBefore(){
        System.out.println("前置方法,在目标方法执行之前执行");
    }*/

    //连接点:JoinPoint
    /*
    * 指定方法中的参数:JoinPoint
    *JoinPoint:业务方法,要加入切面功能的业务方法
    * 作用是:可以在通知方法中获取方法执行时的信息,例如方法名称,方法的实参
    * 如果你的切面功能中需要用到方法的信息,就加入JoinPoint
    * 这个JoinPoint蚕食的值是由框架赋予的,必须时第一个位置的参数
    * */
    /*@Before(value="execution(public void com.lkw.ba02.SomeServiceImpl.doSome(String,Integer))")
    public void doBefore(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("前置方法,在目标方法执行之前执行");
    }*/

    /*
    * 定义后置通知方法,方法时实现切面功能的
    * 方法定义的要求:
    * 1.访问修饰符:public
    * 2.方法没有返回值
    * 3.方法名称自定义
    * 4.方法可以有参数,也可以没有参数
    * 如果有参数,参数不是自定义的,有几个参数类型可以使用
    *
    * */

    /**
     * @AfterReturning:后置通知
     * 属性:1.value:切入点表达式
     * 2.returning 自定义的变量,表示目标方法的返回值的,自定义的变量名必须和方法的形参名一样
     * 位置:在方法定义的上面
     *
     * 特点:
     * 1.在目标方法之后执行的
     * 2.能够获取到目标方法的返回值,可以根据返回值做出不同的处理
     * 3.可以修改这个返回值
     */



    /**
     * String res = someService.doOther("kkk",20);
     * 后置通知表达式相当于函数的参数的传递,有简单数据类型和引用数据类型传参的区别
     * 简单数据类型传递参数是值传递,引用数据类型传参是引用传递
     * 在函数中对简单数据类型进行修改,不会影响最终函数的执行结果,即不影响函数之外定义的简单类型的变量的值
     * 但是如果是引用数据类型的话,会影响最终的函数之外的变量的结果
     *
     */
    @AfterReturning(value = "execution(* *..someService.doOther(..))",returning = "res")
    public void doAfterReturning(Object res){
        System.out.println("方法的返回值:"+res);
        if(res != null){
            res = "hello";//因为这里传入的参数的类型是简单数据类型,所以最终的结果不会改变,但如果是引用数据类型的话,结果就不发生改变
        }
    }
/**
     * 环绕通知方法的定义格式:
     * 1.public
     * 2.必须有一个返回值,推荐使用Object
     * 3.方法名称自定义
     * 4.方法有参数,固定的参数ProceedingJoinPoint
     */

    /**
     * @Around: 环绕通知
     * 属性:value:切入点表达式
     * 位置:在方法定义的上面
     *
     * 特点:
     * 1.它是功能最强的通知
     * 2.在目标方法的前后都能增强功能
     * 3.控制目标方法是否被调用执行
     * 4.执行原来的目标方法的执行结果,影响最后的调用结果
     *
     * 环绕通知,等同于jdk动态代理,InvocationHandler接口
     * 参数:ProceedingJoinPoint 就等同于Method,作用:执行目标方法
     * 返回值:就是目标方法的执行结果,可以被修改
     *
     * 环绕通知:经常做事务,在目标方法执行之前开启事务,在目标方法执行结束后,提交事务
     */

    @Around(value="execution(* *..*.doFirst(..))")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        //可以通过ProceedingJoinPoint对象来获得方法的相关信息
        System.out.println("目标方法的名称为:"+pjp.getSignature().getName());

        //实现环绕通知
        Object res = null;
        //在目标方法之前,输出时间
        System.out.println("在目标方法之前,输出时间"+new Date());
        res = pjp.proceed();//就相当于method.invoke();
        if(res != null){
            //可以通过环绕通知修改返回的值,这里是和后置通知不同的地方,因为后置通知是在方法调用结束之后,在将修改后的值传入到后置通知的方法之中,如果是简单
            //数据类型肯定结果不会改变,但是如果是引用数据类型,结果会受到影响
            //而环绕通知是在方法返回之前修改的返回值的结果,所以无论是简单数据类型还是引用数据类型都会受到影响
            res = "qwer";
        }
        System.out.println("在目标方法执行之后,提交事务");
        return res;
}
@AfterThrowing:异常通知
     *
     * 属性:1.value:切入点表达式
     *      2.throwing 自定义的变量,表示目标方法抛出的异常对象,这个变量的名称要和方法的参数名称一致
     * 特点:
     * 1.在目标方法抛出异常的时候执行
     * 2.可以做异常的监控程序,监控目标方法执行时是不是有异常,如果有异常,可以发送邮件,短信进行通知
     */
    @AfterThrowing(value = "execution(* *..*.doSecond(..))",throwing = "ex")
    public void doThrowing(Exception ex){
        System.out.println("异常信息:"+ex.getMessage());
   }
/**
     * 最终通知方法的定义格式:
     * 1.public
     * 2.方法没有返回值
     * 3.方法名称自定义
     * 4.方法没有参数,有的话只能是JoinPoint
     *
     * @After:最终通知
     *
     * 属性:1.value:切入点表达式
     * 特点:该通知一般用在所有业务方法执行完毕之后,用来清除资源
     * 并且无论发生什么异常,该方法都会执行,相当于finally子句
     */
    @After(value="execution(* *..*.*(..))")
    public void doAfter(){
        System.out.println("无论发生什么情况,该代码都会执行");//即使抛出异常,该方法依然会执行
}

总结:使用最多的是前置,后置,环绕通知,异常和最终使用的不多,重点掌握前三个

5.mybatis和spring集成

用到的技术是ioc

为什么使用ioc:能把mybatis和spring继承在一起,像一个框架一样,是因为ioc能创建对象,可以把mybatis框架中的对象交给spring同意创建,开发人员从spring获取对象,开发人员就不用同时面对两个或多个框架了,就面对的是spring

5.1.mybatis的使用步骤

1.定义dao接口,PlayerDao

2.定义mapper文件PlayerDao.xml

3.定义mybatis的主配置文件mybatis.xml

4.创建dao的代理对象

PlayerDao  dao = SqlSession.getMapper(PlayerDao.class);
List<Player> players = dao.selectPlayer();

要使用dao对象,需要使用getMapper方法

怎么使用getMapper对象,需要哪些条件呢?

1.获取SqlSession对象,需要使用SqlSessionFactory的openSession方法来创建SqlSession对象

2.创建SqlSessionFactory对象,通过读取mybatis的主配置文件,能创建SqlSessionFactory对象

需要使用SqlSessionFactory对象,使用Factory能获取SqlSession,有了SqlSession就能有dao,目的就是获取dao对象

Factory创建需要读取主配置文件,需要配置数据库的连接信息,在以后的项目中不使用mybatis自带的数据库连接池,代之使用独立的数据库连接池类,把数据库连接池也交给spring进行管理

通过以上的说明,我们需要让spring创建以下对象:

1.独立的连接池类对象,使用阿里的druid连接池

2.SqlSessionFactory对象

3.dao对象

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

注意:以上三个步骤都是固定的,只是根据项目的需求,做一些小的改动即可

5.2.mybatis和spring的集成步骤
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方法访问数据库

6.spring的事务处理

6.1.什么是事务

事务是指一组sql语句的集合,集合中有多条sql语句,可能是可能是增删改查,我们希望这些sql要么全部执行成功要么全部执行失败,这些sql语句作为一个整体

6.2.什么时候想到使用事务

当我们的操作涉及到多个表,或者多个sql语句的增删改查的时候,要保证这些语句都成功才能完成我们要求的功能的时候,考虑到使用事务

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

service类的业务方法上,因为业务方法会调用多个dao方法,执行多个sql语句

6.4.通常使用jdbc访问数据库,还有mybatis访问数据该如何处理事务呢

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

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

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

6.5.上面对于不同的技术处理事务有什么不足之处

(1)不同的数据库访问技术,处理事务的对象,方法不同,需要了解不同数据库访问技术使用事务的原理

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

(3)需要掌握事务的多种方法

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

6.6.怎么解决不足

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

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

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

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

事务管理器适应个接口和它的众多实现类

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

实现类:spring把每一种数据库访问技术对应的事务处理类都创建好了

mybatis访问数据库—spring创建好的是DataSourceTransactionManager

Hibernate访问数据库----spring创建好的是HibernateTransactionManager

怎么使用:你需要告诉spring你使用的是哪种数据库的访问技术,在spring的配置文件中使用声明就可以了

例如,想要使用mybatis访问数据库,你应该在xml文件中这样配置:

<bean id="xxx" class="com...DataSourceTransactionManager"></bean>

(2)你的业务方法需要什么样的事务,要说明需要的事务类型

说明方法需要的事务:

  1. 事务的隔离级别:

    1.读未提交读(Read Uncommitted):也称为脏读,一个事务读取到另一个事务未提交的内容

    2.不可重复读(Read Committed):一个事务读到另一个事务已提交的内容,此级别解决了读未提交读的问题,即其他事务没有提交的内容对本事务不可见,这种方式读取到的数据比较真实,但是会出现不可重复读的现象,就是第一次读取数据的时候可能只有1条数据,但是同时用户又向表中插入了一条数据,第二次读取的时候,发现读到的数据就变成了2条,就是不可重复读就是说每一次读到的数据可能不一致,因为用户可能一直在向表中插入数据

    3.可重复读(Repeatable Read):也称为幻读或虚读,一个事务读取到另一个事务已提交的内容(主要是数据的插入),此级别解决了读未提交读和不可重复读的问题,即事务A能读到事务B提交的数据插入的内容,即使是B事务对数据进行修改了,A事务读取到的依然还是开启事务时的数据

    4.可串行化(Serializable):最高的隔离级别,通过强制事务排序,使之不可能相互冲突,从而解决幻读问题,效率最低,但是读取到的数据最真实,就是一个事务在读取数据的时候,其他所有事务只有排队等待,只有上一个操作完毕之后才能继续其他事务,这就相当于是java中的synchronized(同步机制)

    mysql的默认隔离级别:可重复读

  2. 事务的传播行为:控制业务方法是不是有事务的,是什么样的事务,7个传播行为,表示你的业务方法调用时,事务在方法之间时如何使用的在这里插入图片描述

    上面的只需要掌握1,2,4即可

  3. 事务的超时时间:表示一个方法的最长的执行时间,如果方法执行时超过了时间,事务就回滚,单位时秒,整数值,默认是-1

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

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

2.当你的业务方法抛出运行时异常或者ERROR,spring执行回滚,调用事务的rollback

3.当你的业务方法抛出非运行时异常的(主要是受查异常),提交事务

总结spring的事务

  1. 管理事务的是事务管理和它的实现类

  2. spring的事务是一个统一的模型

    (1)指定要使用哪些事务管理器的实现类,使用

    (2)制定哪些类,哪些方法需要加入事务的功能

    (3)指定方法需要的隔离级别,传播行为,超时

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

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

1.适合小型项目使用的,注解方案

spring框架自己用aop实现给业务方法的增加事务的功能,使用@Transactional注解增加事务

@Transactional注解是spring框架自己的注解,放在public方法的上面,表示当前方法具有事务

可以给注解的属性赋值,表示具体的隔离级别,传播行为,异常信息等

2.使用@Transactional注解的步骤:

  1. 需要声明事务管理器对象

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

    spring使用aop机制,创建@Transactional所在的类代理对象,给方法加入事务的功能

    spring给业务方法加入事务:

    ​ 在你的业务方法执行之前,先开启事务,在业务方法之后提交或回滚事务,使用aop的环绕通知

    @Around("你要增加的事务功能的业务方法名称")
    Object myAround(){
    	开启事务,spring给你开启
    	try{
    		buy(1001,10);
    		spring的事务管理.commit();
    	}catch(Exception e){
    		spring的事务管理rollback();
    	}
    }
    

  3. 在你的方法上面加入@Transactional注解

7.web项目中使用容器对象

7.1.普通的javase项目和web项目的区别

1.javase项目是有main方法的,执行代码是执行main方法的,在main方法里面创建容器对象

ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

2.文本项目是在tomcat服务器上运行的,tomcat一启动,项目一直运行下去

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

怎么实现:

​ 使用监听器 当全局作用域对象被创建的时候 创建容器 存入ServletContext

​ 监听器作用:

​ (1)创建容器对象,执行ApplicationContext ctx = new ClassPathXmlApplicationContext(“applicationContext.xml”);

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值