自学之Spring框架

Spring

概述

spring全家桶:spring , springmvc ,spring boot , spring cloud

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

依赖:class a中使用class b的属性或者方法, 叫做class a依赖class b


ioc控制反转

IoC (Inversion of Control) : 控制反转, 是一个理论,概念,思想。描述的:把对象的创建,赋值,管理工作都交给代码之外的容器实现, 也就是对象的创建是有其它外部资源完成。

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

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

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

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

  1. 构造方法 , new Student()

  2. 反射

  3. 序列化

  4. 克隆

  5. ioc :容器创建对象

  6. 动态代理

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

ioc的体现:

**servlet **

1:创建类继承HttpServelt
2:在web.xml 注册servlet ,使用

myservlet
com.allspark.controller.MyServlet1

3:没有创建 Servlet对象, 没有 MyServlet myservlet = new MyServlet()

4:Servlet 是Tomcat服务器它能你创建的。 Tomcat也称为容器。Tomcat作为容器:里面存放的有Servlet对象, Listener , Filter对象

IoC的技术实现 ,
DI 是ioc的技术实现 ,

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

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

spring是一个容器,管理对象,给属性赋值, 底层是反射创建对象。

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

spring-context:是ioc功能的,创建对象的。
spring-webmvc做web开发使用的, 是servlet的升级。
spring-webmvc中也会用到spring-context中创建对象的功能的。

set设值注入

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

    <!--声明student对象
        注入:就是赋值的意思
        简单类型:spring中规定java的基本数据类型和String都是简单类型
        di:给属性赋值
        1.set注入(设置注入):spring调用类的set方法,你可以在set方法中完成属性的赋值
          1)简单类型的set注入
            <bean id="" class="yyy">
              <property name = "属性名字" value="此属性的值">
              一个property只能给一个属性赋值
              <property...>
            </bean>

          2)引用类型的set注入:
             <bean id="" class="yyy">
              <property name = "属性名字" ref="bean的id(对象的名称)"/>
            </bean>

    -->

    <!--声明student对象-->
    <bean id="myStudent" class="com.allspark.ba02.Student">
        <property name="name" value="李四"/>
        <property name="age" value="20"/>
    <!--引用类型-->
        <property name="school" ref="mySchool"/> <!--setSchool(mySchool)-->
    </bean>
    <!--声明School对象-->
    <bean id="mySchool" class="com.allspark.ba02.School">
        <property name="name" value="西安邮电大学"/>
        <property name="address" value="西安的长安区"/>
    </bean>

</beans>

构造注入

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

    -->

   <!--使用name属性实现构造注入-->
    <bean id="myStudent" class="com.allspark.ba03.Student">
        <constructor-arg name="myname" value="张三"/>
        <constructor-arg name="myage" value="20"/>
        <constructor-arg name="myschool" ref="myXuexiao"/>
    </bean>

    <!--使用index属性实现构造注入-->
    <bean id="myStudent2" class="com.allspark.ba03.Student">
        <constructor-arg index="0" value="张三"/>
        <constructor-arg index="1" value="20"/>
        <constructor-arg index="2" ref="myXuexiao"/>
    </bean>

    <!--省略index-->
    <bean id="myStudent3" class="com.allspark.ba03.Student">
        <constructor-arg  value="张三"/>
        <constructor-arg  value="20"/>
        <constructor-arg  ref="myXuexiao"/>
    </bean>
    <!--声明School对象-->
    <bean id="myXuexiao" class="com.allspark.ba03.School">
        <property name="name" value="西安邮电大学"/>
        <property name="address" value="西安的长安区"/>
    </bean>
    <!--创建File,使用构造注入-->
    <bean id="myfile" class="java.io.File">
        <constructor-arg name="parent" value="D:\java代码\spring-course\ch01-hello-spring"/>
        <constructor-arg name="child" value="readme.txt"/>
    </bean>

自动注入

byName

按名称注入,java类中引用类型的属性名和spring容器中bean的[id]一样,数据类型一样,这样的bean赋值给引用类型

<?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框架根据某些规则可以给引用类型赋值。不用你给引用类型赋值了
    使用的规则常用的是byName,byType
    1.byName(按名称注入):java类中引用类型的属性名和spring容器中(配置文件)<bean>的id名称一样,
                        且数据类型是一致的,这样的容器中的bean,spring能够赋值给引用类型。
      语法:
      <bean id="xx" class="yyy" autowire="byName">
          简单类型属性赋值
      </bean>
    -->
    <!--byName-->
    <!--声明student对象-->
    <bean id="myStudent" class="com.allspark.ba04.Student" autowire="byName">
        <property name="name" value="李四"/>
        <property name="age" value="20"/>
    <!--引用类型-->
    <!--    <property name="school" ref="mySchool"/>-->
    </bean>
    <!--声明School对象-->
    <bean id="school" class="com.allspark.ba04.School">
        <property name="name" value="清华大学"/>
        <property name="address" value="西安的长安区"/>
    </bean>

</beans>
byType

按类型注入,java类中引用类型是[数据类型]和spring中bean的[class]是同源关系的,这样的bean能够赋值给引用数据类型

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

    2.byType(按类型注入):java类中引用类型的数据类型和spring容器中(配置文件)<bean>的class属性
                        是同源关系的,这样的bean能够赋值给引用类型
      同源就是一类的意思:
        1.java类中引用类型的数据类型和bean的class的值是一样的。
        2.java类中引用类型的数据类型和bean的class的值是父子类关系的。
        3.java类中引用类型的数据类型和bean的class的值接口和实现类关系的
        语法:
        <bean id="xx" class="yyy" autowire="byType">
          简单类型属性赋值
        </bean>

        注意:在byType中,在xml配置文件中声明bean只能有一个符合条件的,
             多于一个是错误的
    -->
    <!--byType-->
    <!--声明student对象-->
    <bean id="myStudent" class="com.allspark.ba05.Student" autowire="byType">
        <property name="name" value="张三"/>
        <property name="age" value="20"/>
    <!--引用类型-->
    <!--    <property name="school" ref="mySchool"/>-->
    </bean>

    <!--声明School对象-->
    <!--<bean id="myschool" class="com.allspark.ba05.School">
        <property name="name" value="人民大学"/>
        <property name="address" value="西安的长安区"/>
    </bean>-->
    <!--声明School的子类-->
    <bean id="primarySchool" class="com.allspark.ba05.PrimarySchool">
        <property name="name" value="西安小学"/>
        <property name="address" value="西安的长安区"/>
    </bean>

</beans>

多个配置文件

  1. 多个配置优势
    1.每个文件的大小比一个文件要小很多。效率高
    2.避免多人竞争带来的冲突。

​ 如果你的项目有多个模块(相关的功能在一起) ,一个模块一个配置文件。
​ 学生考勤模块一个配置文件, 张三
​ 学生成绩一个配置文件, 李四

多文件的分配方式:

  1. 按功能模块,一个模块一个配置文件
  2. 按类的功能,数据库相关的配置一个文件配置文件, 做事务的功能一个配置文件, 做service功能的一个配置文件等
<?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">


    <!--School模块所有bean的声明,School模块的配置文件-->
    <!--声明School对象-->
    <bean id="myschool" class="com.allspark.ba06.School">
        <property name="name" value="航空大学"/>
        <property name="address" value="西安的长安区"/>
    </bean>
</beans>


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


    <!--
    student模块所有bean的声明
    -->
    <!--byType-->
    <!--声明student对象-->
    <bean id="myStudent" class="com.allspark.ba06.Student" autowire="byType">
        <property name="name" value="张三"/>
        <property name="age" value="30"/>
    <!--引用类型-->
    <!--    <property name="school" ref="mySchool"/>-->
    </bean>


</beans>


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

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

    <!--
      在包含关系的配置文件中,可以使用通配符(*:表示任意字符)
      注意:主的配置文件名称不能包含在通配符范围内(不能叫做spring-total.xml)
    -->
    <import resource="classpath:ba06/spring-*.xml"/>
</beans>

基于注解的di

通过注解完成java对象创建,属性赋值。
使用注解的步骤:
1.加入maven的依赖 spring-context ,在你加入spring-context的同时, 间接加入spring-aop的依赖。
使用注解必须使用spring-aop依赖

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

3.在spring的配置文件中,加入一个组件扫描器的标签,说明注解在你的项目中的位置。<context:component-scan base-package=“包名”/>

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

用户处理请求:
用户form ,参数name ,age-----Servlet(接收请求name,age)—Service类(处理name,age操作)—dao类(访问数据库的)—mysql

package com.allspark.ba01;

import org.springframework.stereotype.Component;

/**
 * @Component:创建对象的,等同于<bean>的功能
 *    属性:value就是对象的名称,也就是bean的id值,
 *         value的值是唯一的,创建的对象在整个spring容器中就一个
 *    位置:在类的上面
 *    @Component(value = "myStudent")等同于
 *    <bean id="myStudent" class="com.allspark.ba01.Student">
 *
 * spring中和@Component功能一致,创建对象的注解还有:
 * 1.@Respotory(用在持久层类的上面):放在dao的实现类上面,表示创建dao对象,dao对象是能访问数据库的。
 * 2.@Service(用在业务层类的上面):放在service的实现类上面,创建service对象,service对象是做业务处理,可以有事务等功能的
 * 3.@Controller(用在控制器类的上面):放在控制器(处理器)类的上面,创建控制器对象的,控制器对象,能够接收用户提交的参数,显示请求的处理结果
 *
 * 以上三个注解的使用语法和@Component一样的。都能创建对象,但是这三个注解还有额外的功能
 * @Respotory,@Service,@Controller是给项目的对象分层的
 */
//使用value属性,指定对象名称
//@Component(value = "myStudent")
//省略value
@Component("myStudent")
//不指定对象名称,由spring提供默认名称,类名的首字母小写
//@Component
public class Student {
    private String name;
    private Integer age;

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

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

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

指定多种包的方式:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <!--声明组件扫描器(component-scan),组件就是java对象
        base-package:指定注解在你的项目中的包名
        component-scan工作方式:spring会扫描遍历base-package指定的包

        加入了component-scan标签,配置文件的变化:
          1.加入一个新的约束文件spring-context.xsd
          2.给这个新的约束文件起个命名空间的名称
    -->
    <context:component-scan base-package="com.allspark.ba01"/>

    <!--指定多个包的三种方式-->
    <!--第一种方式:使用多次组件扫描器,指定不同的包-->
    <context:component-scan base-package="com.allspark.ba01"/>
    <context:component-scan base-package="com.allspark.ba02"/>

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

    <!--第二种方式:指定父包-->
    <context:component-scan base-package="com.allspark"/>
</beans>

简单类型属性赋值:

package com.allspark.ba02;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;


@Component("myStudent")
public class Student {
    /**
     * @Value:简单类型的属性赋值
     * 属性:value是String类型的,表示简单类型的属性值
     * 位置:1.在属性定义的上面,无需set方法,推荐使用
     *      2.在set方法的上面
     */
    @Value(value = "张飞")
    private String name;
    @Value(value = "29")
    private Integer age;

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

    public void setAge(Integer age) {
        this.age = age;
    }
*/

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

引用数据类型的赋值:

@Autowired
package com.allspark.ba04;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;


@Component("myStudent")
public class Student {
    /**
     * @Value:简单类型的属性赋值
     * 属性:value是String类型的,表示简单类型的属性值
     * 位置:1.在属性定义的上面,无需set方法,推荐使用
     *      2.在set方法的上面
     */
    @Value(value = "张飞")
    private String name;
    @Value(value = "29")
    private Integer age;

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

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

@Resource
package com.allspark.ba07;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;


@Component("myStudent")
public class Student {
    @Value(value = "张飞")
    private String name;
    @Value(value = "29")
    private Integer age;

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


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

junit : 单元测试

一个工具类库,做测试方法使用的。
单元:指定的是方法, 一个类中有很多方法,一个方法称为单元。

使用单元测试
1.需要加入junit依赖。

junit
junit
4.11
test

2.创建测试作用的类:叫做测试类
src/test/java目录中创建类

3.创建测试方法

1)public 方法
2)没有返回值 void
3)方法名称自定义,建议名称是test + 你要测试方法名称
4)方法没有参数
5)方法的上面加入 @Test ,这样的方法是可以单独执行的。 不用使用main方法。

AOP面向切面编程

1.动态代理

实现方式:jdk动态代理,使用jdk中的Proxy,Method,InvocaitonHanderl创建代理对象。
jdk动态代理要求目标类必须实现接口

cglib动态代理:第三方的工具库,创建代理对象,原理是继承。 通过继承目标类,创建子类。
子类就是代理对象。 要求目标类不能是final的, 方法也不能是final的

jdk动态代理:
package com.allspark;

import com.allspark.util.ServiceTools;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyIncationHandler implements InvocationHandler {
    //目标对象
    private Object target;

    public MyIncationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //通过代理对象执行方法时,会调用执行这个invoke()
        System.out.println("执行MyIncationHandler中的invoke()");
        System.out.println("method名称:"+method.getName());
        String methodName=method.getName();
        Object res = null;
        if ("doSome".equals(methodName)){
            ServiceTools.doLog();//在目标方法之前,输出时间
            //执行目标类的方法,通过Method类实现
            res=method.invoke(target,args);//SomeServiceImpl.doSome()
            ServiceTools.doTrans();
        }else {
            res = method.invoke(target,args);//SomeServiceImpl.doOther()
        }


        //目标方法的执行结果
        return res;
    }
}

package com.allspark;

import com.allspark.handler.MyIncationHandler;
import com.allspark.service.SomeService;
import com.allspark.service.impl.SomeServiceImpl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class MyApp {
    public static void main(String[] args) {
        //调用doSome,doOther
//        SomeService service = new SomeServiceImpl();
//        service.doSome();
//        System.out.println("=================================");
//        service.doOther();

        //使用jdk的Proxy创建代理对象
        //创建目标对象
        SomeService target = new SomeServiceImpl();

        //创建InvocationHandler对象
        InvocationHandler handler = new MyIncationHandler(target);

        //使用Proxy创建代理
        SomeService proxy = (SomeService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),handler);

        //通过代理执行方法,会调用handler中的invoke()
        proxy.doSome();
        System.out.println("=========================================");
        proxy.doOther();
    }
}

2.动态代理的作用:

1)在目标类源代码不改变的情况下,增加功能。
2)减少代码的重复
3)专注业务逻辑代码
4)解耦合,让你的业务功能和日志,事务非业务功能分离。

3.Aop:面向切面编程 基于动态代理的,可以使用jdk,cglib两种代理方式。

Aop就是动态代理的规范化, 把动态代理的实现步骤,方式都定义好了, 让开发人员用一种统一的方式,使用动态代理。

4.AOP(Aspect Orient Programming)面向切面编程

Aspect: 切面,给你的目标类增加的功能,就是切面。 像上面用的日志,事务都是切面。
切面的特点: 一般都是非业务方法,独立使用的。
Orient:面向, 对着。
Programming:编程

oop: 面向对象编程

怎么理解面向切面编程 ?
1)需要在分析项目功能时,找出切面。
2)合理的安排切面的执行时间(在目标方法前, 还是目标方法后)
3)合理的安全切面执行的位置,在哪个类,哪个方法增加增强功能

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

2)JoinPoint:连接点 ,连接业务方法和切面的位置。 就某类中的业务方法
3)Pointcut : 切入点 ,指多个连接点方法的集合。多个方法
4)目标对象: 给哪个类的方法增加功能, 这个类就是目标对象
5)Advice:通知,通知表示切面功能执行的时间。

说一个切面有三个关键的要素:
1)切面的功能代码,切面干什么
2)切面的执行位置,使用Pointcut表示切面执行的位置
3)切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标方法之后。

5.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个注解。

6.学习aspectj框架的使用。

1)切面的执行时间, 这个执行时间在规范中叫做Advice(通知,增强)
在aspectj框架中使用注解表示的。也可以使用xml配置文件中的标签
1)@Before
2)@AfterReturning
3)@Around
4)@AfterThrowing
5)@After

2)表示切面执行的位置,使用的是切入点表达式。
com.service.impl
com.allspark.service.impl
cn.crm.allspark.service

​ execution(* …service..*(…))

举例:

execution(public * *(…))

指定切入点为:任意公共方法。

execution(* set*(…))

指定切入点为:任何一个以“set”开始的方法。

execution(* com.xyz.service..(…))

指定切入点为:定义在 service 包里的任意类的任意方法。

execution(* com.xyz.service….(…))

指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“…”出现在类名中时,后

面必须跟“*”,表示包、子包下的所有类。

execution(* …service..*(…))

aspectj的五个通知注解
@Before前置通知

方法有JoinPoin参数

  • 在目标方法执行之前执行。被注解为前置通知的方法,可以包含一个 JoinPoint 类型参数。该类型的对象本身就是切入点表达式。通过该参数,可获取切入点表达式、方法签名、目标对象等。
  • 不光前置通知的方法,可以包含一个 JoinPoint 类型参数,所有的通知方法均可包含该参数。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--把对象交给spring容器,由spring容器统一创建,管理对象-->
    <!--声明目标对象-->
    <!--spring就完成 SomeService someService = new SomeServiceImpl();-->
    <bean id="SomeService" class="com.allspark.ba01.SomeServiceImpl"/>

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

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

    aspectj-autoproxy:会把spring容器中的所有目标对象,一次性都生成代理对象
    -->
    <aop:aspectj-autoproxy/>
</beans>
/**
 * @Aspect:是aspectj框架中的注解。
 * 作用:表示当前类是切面类。
 * 切面类:是用来给业务方法增加功能的类,在这个类中有切面功能代码
 * 位置:在类定义的上面
 */
@Aspect
public class MyAspect {
    /**
     * 定义方法,方法是实现切面功能的。
     * 1.公共方法public
     * 2.方法没有返回值
     * 3.方法名称自定义
     * 4.方法可以有参数,也可以没有参数。
     *   如果有参数,参数不是自定义的,有几个参数类型可以使用
     */

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

    @Before(value = "execution(void *..SomeServiceImpl.do*(..))")
    public void  myBefore(){
        //就是切面要执行的方法
        System.out.println("3====前置通知,切面功能:在目标方法之前输出执行时间:"+new Date());
    }

    @Before(value = "execution(void *do*(..))")
    public void  myBefore2(){
        //就是切面要执行的方法
        System.out.println("4====前置通知,切面功能:在目标方法之前输出执行时间:"+new Date());
    }

}

public class MyTest01 {
    @Test
    public void test01() {
        String config = "applicationContext.xml";
        //创建容器对象
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        //从容器中获取目标对象
        SomeService proxy = (SomeService) ctx.getBean("SomeService");
        //proxy:com.sun.proxy.$Proxy8:jdk动态代理
        System.out.println("proxy:"+proxy.getClass().getName());
        //通过代理的对象执行方法,实现目标方法执行时,增强了功能
        proxy.doSome("lisi",20);
    }
}

jdk动态代理:

1.使用jdk反射包中的类实现创建代理对象的功能

2.要求:目标类必须实现接口

cglib动态代理:

1.使用第三方的工具库,实现代理对象的创建

2.要求:目标类必须能够继承,不能是final

3.原理:就是继承,子类就是代理

什么时候用aop技术:

1.当你要给一个系统中存在的类修改功能,但是原有的类的功能不完善,但是你没有源代码,使用aop增加功能

2.你要给项目中的多个类,增加一个相同的功能,使用aop

3.给业务方法增加事务,日志输出。

@AfterReturning:后置通知
package com.allspark.ba02;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import java.util.Date;

/**
 * @Aspect:是aspectj框架中的注解。
 * 作用:表示当前类是切面类。
 * 切面类:是用来给业务方法增加功能的类,在这个类中有切面功能代码
 * 位置:在类定义的上面
 */
@Aspect
public class MyAspect {
    /**
     * 定义方法,方法是实现切面功能的。
     * 1.公共方法public
     * 2.方法没有返回值
     * 3.方法名称自定义
     * 4.方法有参数,推荐是Object,参数名自定义
     */
    /**
     *@AfterReturning:后置通知
     *   属性:1.value 切入点表达式
     *        2.returning 自定义的变量,表示目标方法返回值的。
     *          自定义变量名必须和通知方法的形参名一样
     *   位置:在方法定义的上面
     * 特点:
     *  1.在目标方法之后执行的
     *  2.能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能
     *   Object res = doOther();
     *  3.可以修改这个返回值
     *
     * 后置通知的执行
     *     Object res = doOther();
     *     参数传递:传值,传引用
     *     myAfterReturning(res);
     *     System.out.println("res="+res)
     */
    @AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",returning = "res")
    public void myAfterReturning(Object res){
      //Object res:是目标方法执行后的返回值,根据返回值做你的切面的功能处理
        System.out.println("后置通知:在目标方法之后执行的,获取的返回值是:"+res);
        if (res.equals("abcd")){
            //做一些功能
        }else {
            //做其他功能
        }

      //修改目标方法的返回值,看一下是否会影响 最后的方法调用结果
        if (res!=null){
            res = "Hello Aspectj";
        }
    }

    @AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",returning = "res")
    public void myAfterReturning2(Object res){
        //Object res:是目标方法执行后的返回值,根据返回值做你的切面的功能处理
        System.out.println("后置通知:在目标方法之后执行的,获取的返回值是:"+res);
        //修改目标方法的返回值,看一下是否会影响 最后的方法调用结果
        //如果修改了res的内容,属性值等,是不是会影响最后的调用结果
    }

@Around环绕通知
package com.allspark.ba03;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

import java.util.Date;

/**
 * @Aspect:是aspectj框架中的注解。
 * 作用:表示当前类是切面类。
 * 切面类:是用来给业务方法增加功能的类,在这个类中有切面功能代码
 * 位置:在类定义的上面
 */
@Aspect
public class MyAspect {
    /**
     * 环绕通知方法的定义格式
     * 1.public
     * 2.必须有一个返回值,推荐使用Object
     * 3.方法名称自定义
     * 4.方法有参数,固定的参数ProceedingJoinPoint
     */

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

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

        //实现环绕通知
        Object result = null;
        System.out.println("环绕通知:在目标方法之前,输出时间:"+new Date());
        //1.目标方法的调用
        if ("zhangsan".equals(name)){
            //符合条件,调用目标方法
            result=pjp.proceed();//等同于method.invoke();Object result = doFirst();
        }

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

        //修改目标方法的执行结果,影响方法最后的调用结果
        if (result!=null){
            result = "Hello AspectJ AOP";
        }

        //返回目标方法的执行结果
        return result;
    }
}
@AfterThrowing异常通知
package com.allspark.ba04;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

import java.util.Date;

/**
 * @Aspect:是aspectj框架中的注解。
 * 作用:表示当前类是切面类。
 * 切面类:是用来给业务方法增加功能的类,在这个类中有切面功能代码
 * 位置:在类定义的上面
 */
@Aspect
public class MyAspect {
    /**
     * 异常通知方法的定义格式
     * 1.public
     * 2.没有返回值
     * 3.方法名称自定义
     * 4.方法有一个Exception,如果还有,是JoinPoint,
     */
    /**
     * @AfterThrowing:异常通知
     *    属性:1.value  切入点表达式
     *         2.throwing 自定义的变量,表示目标方法抛出的异常对象。
     *           变量名必须和方法的参数名一样
     *  特点:
     *    1.在目标方法抛出异常时执行的
     *    2.可以做异常的监控程序,监控目标方法执行时是不是有异常。
     *       如果有异常,可以发送邮件,短信进行通知
     *
     *  执行就是:
     *    try {
     *             SomeServiceImpl.doSecond(..)
     *         }catch (Exception e){
     *             myAfterThrowing(e);
     *         }
     */

    @AfterThrowing(value = "execution(* *..SomeServiceImpl.doSecond(..))",throwing = "ex")
    public void myAfterThrowing(Exception ex){
        System.out.println("异常通知:方法发生异常时,执行:"+ex.getMessage());
        //发送邮件,短信,通知开发人员

    }
}

@After最终通知
package com.allspark.ba05;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;

/**
 * @Aspect:是aspectj框架中的注解。
 * 作用:表示当前类是切面类。
 * 切面类:是用来给业务方法增加功能的类,在这个类中有切面功能代码
 * 位置:在类定义的上面
 */
@Aspect
public class MyAspect {
    /**
     * 最终通知方法的定义格式
     * 1.public
     * 2.没有返回值
     * 3.方法名称自定义
     * 4.方法没有参数,如果还有,是JoinPoint,
     */

    /**
     * @After:最终通知
     *   属性:value 切入点表达式
     *   位置:在方法的上面
     * 特点:
     *   1.总是会执行
     *   2.在目标方法之后执行的
     *
     *   try {
     *             SomeServiceImpl.doThird(..)
     *         }catch (Exception e){
     *
     *         }finally {
     *             myAfter()
     *         }
     *
     */

    @After(value = "execution(* *..SomeServiceImpl.doThird(..))")
    public void myAfter(){
        System.out.println("执行最终通知");
        //一般做资源清除工作的

    }
}

@Pointcut注解
package com.allspark.ba06;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

/**
 * @Aspect:是aspectj框架中的注解。
 * 作用:表示当前类是切面类。
 * 切面类:是用来给业务方法增加功能的类,在这个类中有切面功能代码
 * 位置:在类定义的上面
 */
@Aspect
public class MyAspect {

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

    @Before(value = "mypt()")
    public void myBefore(){
        System.out.println("前置通知,在目标方法之前先执行的");
    }

    /**
     * @Pointcut:定义和管理切入点,如果你的项目中有多个切入点表达式是重复的,可以复用的。
     *           可以使用@Pointcut
     *      属性:value  切入点表达式
     *      位置:在自定义方法的上面
     *   特点:
     *      当使用Pointcut定义在一个方法的上面,此时这个方法的名称就是切入点表达式的别名。
     *      其他的通知中,value属性就可以使用这个方法的名称,代替切入点表达式了
     */

    @Pointcut(value = "execution(* *..SomeServiceImpl.doThird(..))")
    public void mypt(){
        //无需代码
    }
}

cjlib动态代理

如果没有接口则为cjlib动态代理

package com.allspark;


import com.allspark.ba07.SomeServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest07 {
    @Test
    public void test01() {
        String config = "applicationContext.xml";
        //创建容器对象
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        //从容器中获取目标对象
        SomeServiceImpl proxy = (SomeServiceImpl) ctx.getBean("SomeService");

        /**
         * 目标类没有接口,使用cglib动态代理,spring会自动应用cjlib
         */

        System.out.println("proxy:"+proxy.getClass().getName());

        //通过代理的对象执行方法,实现目标方法执行时,增强了功能
        proxy.doThird();
    }

}

有接口也可以使用cjlib代理

   <!--
    如果你期望目标类有接口,使用cglib代理
    proxy-target-class="true"告诉框架,要使用cjlib动态代理
    -->
    <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创建。

整合的想法:使用spring的ioc核心技术,把mybatis框架中使用的对象交给spring统一创建和管理。spring是容器,存放你项目中要使用的

​ 各种对象,例如Service对象,Dao对象,工具类对象等等。

交给spring的mybatis对象:

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

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

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

applicationContext.xml

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

    
    <!--
       把数据库的配置信息,写在一个独立的文件,编译修改数据库的配置内容
       spring知道jdbc.properties文件的位置
    -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!--声明数据源DataSource,作用是连接数据库的-->
    <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <!--set注入给DruidDataSource提供数据库连接信息-->
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="maxActive" value="${jdbc.max}"/>
    </bean>

    <!--声明的是mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--set注入,把数据库连接池附给了dataSource属性-->
        <property name="dataSource" ref="myDataSource"/>
        <!--mybatis主配值文件的位置
           configLocation属性是Resource类型,读取配置文件
           它的赋值,使用value,指定文件的路径,使用classpath:表示文件的位置
        -->
        <property name="configLocation" value="classpath:mybatis.xml"/>
    </bean>

    <!--创建dao对象,使用SqlSessiongetMapper(StudentDao.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.allspark.dao"/>
    </bean>

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

mybatis.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--settings:控制mybatis全局行为-->
    <settings>
        <!--设置mybatis输出日志-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <!-- 设置别名-->
    <typeAliases>
    <!-- name:实体类所在的包名-->
        <package name="com.allspark.domain"/>
    </typeAliases>
    <mappers>
        <!--
        name:是包名,这个包中的所有mapper.xml一次都能加载
        -->
        <package name="com.allspark.dao"/>
    </mappers>
</configuration>

StudentService

package com.allspark.service;

import com.allspark.domain.Student;

import java.util.List;

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

}

StudentServiceImpl

package com.allspark.service.impl;

import com.allspark.dao.StudentDao;
import com.allspark.domain.Student;
import com.allspark.service.StudentService;

import java.util.List;

public class StudentServiceImpl implements StudentService {
    //引用类型Dao
    private StudentDao studentDao;

    public StudentDao getStudentDao() {
        return studentDao;
    }

    public void setStudentDao(StudentDao studentDao) {
        this.studentDao = studentDao;
    }

    public StudentServiceImpl(StudentDao studentDao) {
        this.studentDao = studentDao;
    }

    //使用set注入赋值
    @Override
    public int addStudent(Student student) {
        int nums = studentDao.insertStudent(student);
        return nums;
    }

    @Override
    public List<Student> queryStudents() {
        List<Student> students = studentDao.selectStudents();
        return students;
    }
}

StudentDao.xml

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--mapper接口的全路径-->
<mapper namespace="com.allspark.dao.StudentDao">

    <insert id="insertStudent" >
        insert into student values (#{id},#{name},#{email},#{age})
    </insert>

    <select id="selectStudents" resultType="com.allspark.domain.Student">
        select id,name,email,age from student order by id desc
    </select>
</mapper>

Spring事务

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

2.在什么时候想到使用事务
当我的操作,涉及得到多个表,或者是多个sql语句的insert,update,delete。需要保证。这些语句都是成功才能完成我的功能,或者都失败,保证操作是符合要求的。

在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.3问题中事务的处理方式,有什么不足
1)不同的数据库访问技术,处理事务的对象,方法不同,需要了解不同数据库访问技术使用事务的原理
2)掌握多种数据库中事务的处理逻辑。什么时候提交事务,什么时候回滚事务
3)处理事务的多种方法。

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

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

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

6.处理事务,需要怎么做,做什么
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_REQUIRED8

​ PROPAGATION_REQUIRES_NEW

​ PROPAGATION_SUPPORTS
​ 以上三个需要掌握的

​ PROPAGATION_MANDATORY
​ PROPAGATION_NESTED
​ PROPAGATION_NEVER
​ PROPAGATION_NOT_SUPPORTED

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

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

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

​ 受查异常:在你写代码中,必须处理的异常。例如IOException, SQLException

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

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

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

使用@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.在你的方法的上面加入@Trancational

2.适合大型项目,有很多的类,方法,需要大量的配置事务,使用aspectj框架功能,在spring配置文件中
声明类,方法需要的事务。这种方式业务方法和事务配置完全分离。

实现步骤: 都是在xml配置文件中实现。
1)要使用的是aspectj框架,需要加入依赖

org.springframework
spring-aspects
5.2.5.RELEASE

​ 2)声明事务管理器对象

​ 3)声明方法需要的事务类型(配置方法的事务属性【隔离级别,传播行为,超时】)

​ 4)配置aop:指定哪些哪类要创建代理。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.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提供数据库连接信息-->
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="maxActive" value="${jdbc.max}"/>
    </bean>

    <!--声明的是mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory的-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--set注入,把数据库连接池附给了dataSource属性-->
        <property name="dataSource" ref="myDataSource"/>
        <!--mybatis主配值文件的位置
           configLocation属性是Resource类型,读取配置文件
           它的赋值,使用value,指定文件的路径,使用classpath:表示文件的位置
        -->
        <property name="configLocation" value="classpath:mybatis.xml"/>
    </bean>

    <!--创建dao对象,使用SqlSession的getMapper(StudentDao.class)
         MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象。
    -->
    <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.allspark.dao"/>
    </bean>

    <!--声明service-->
    <bean id="buyService" class="com.allspark.service.impl.BuyGoodsServiceImpl">
        <property name="goodsDao" ref="goodsDao"/>
        <property name="saleDao" ref="saleDao"/>
    </bean>

    <!--声明事务处理处理:和源代码完全分离的-->
    <!--1.声明事务管理器对象-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="myDataSource"/>
    </bean>
    <!--2.声明业务方法它的事务属性(隔离级别,传播行为,超时时间)
        id:自定义名称,表示<tx:advice >和</tx:advice>之间的配置内容的
        transactionManager:事务管理器对象的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.allspark.execp.NotEnoughException"/>

            <!--使用通配符,指定很多的方法-->
            <tx:method name="add*" propagation="REQUIRES_NEW" />
            <!--指定修改方法-->
            <tx:method name="modify*" />
            <!--删除方法-->
            <tx:method name="remove*" />
            <!--查询方法,query,search,find-->
            <tx:method name="*" propagation="SUPPORTS" read-only="true" />
      </tx:attributes>
    </tx:advice>

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

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

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

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

private WebApplicationContext context;
public interface WebApplicationContext extends ApplicationContext

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

把创建的容器对象,放入到全局作用域

key: WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
value:this.context

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

package com.allspark.controller;

import com.allspark.domain.Student;
import com.allspark.service.StudentService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;

public class RegisterServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String strId = request.getParameter("id");
        String strName = request.getParameter("name");
        String strEmail = request.getParameter("email");
        String strAge = request.getParameter("age");

        //创建spring的容器对象
        //String config = "spring.xml";
        //ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        WebApplicationContext ctx = null;
        //获取ServletContext中的容器对象,创建好的容器对象,拿来就用
        /*String key = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
        Object attr = getServletContext().getAttribute(key);
        if(attr!=null){
            ctx = (WebApplicationContext) attr;
        }*/
        //使用框架中的方法,获取容器对象
        ServletContext sc = getServletContext();
        ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
        System.out.println("容器的对象的信息======" + ctx);

        //获取service
        StudentService service = (StudentService) ctx.getBean("studentService");
        Student student = new Student();
        student.setId(Integer.parseInt(strId));
        student.setName(strName);
        student.setAge(Integer.valueOf(strAge));
        student.setEmail(strEmail);
        service.addStudent(student);

        //给一个页面
        request.getRequestDispatcher("/result.jsp").forward(request, response);
    }
}

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>RegisterServlet</servlet-name>
        <servlet-class>com.allspark.controller.RegisterServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>RegisterServlet</servlet-name>
        <url-pattern>/reg</url-pattern>
    </servlet-mapping>
    <!--注册监听器
        监听器被创建对象后,会读取/WEB-INF/spring.xml
        为什么要读取文件:因为在监听器中要创建ApplicationContext对象,需要加载配置文件。
        /WEB-INF/applicationContext.xml就是监听器默认读取的spring配置文件路径

        可以修改默认的文件位置

        配置监听器:目的是创建容器对象,创建了容器对象,就能把spring.xml配置文件 中的所有对象都创建好。
        用户发起请求就可以直接使用对象了。
    -->
    <context-param>
        <!--contextConfigLocation:表示配置文件的路径
        -->
        <param-name>contextConfigLocation</param-name>
        <!--自定义配置文件的路径-->
        <param-value>classpath:spring.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
</web-app>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值