Spring

一、Spring概述

Spring是一个框架,也是一个容器。容器中存放了Java对象。
作用:减轻对项目模块之间的管理,类和类之间的管理,帮助开发人员创建对象,管理对象之间的关系。
核心技术:ioc和aop。能实现模块之间,类之间的解耦合。

依赖:类a中使用了类b的属性或方法,叫做类a依赖类b。

优点:轻量、解耦合、aop编程支持、方便集成其他框架

spring默认创建对象

spring默认创建对象的时间:是创建spring的容器的时候创建配置文件中所有的对象。
spring默认创建对象的方法:默认调用无参构造。

Spring存放哪些对象

放入Spring容器中:dao类,service类,controller类,工具类。Spring中的对象都是单例的,在容器中叫这个名字的对象只有一个。
不放入Spring容器中:实体类对象,Servlet,监听器,过滤器。

如何把对象放入容器

1、使用xml配置文件
2、使用注解

使用

1、使用maven,加入依赖

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

2、创建接口,并在当前包下创建子包,实现此接口
3、在resources下创建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通过这个名称找到对象
        class:类的全限定名称,不能是接口。因为Spring是反射机制创建接口,必须使用类。

        Spring把创建好的对象放在Map中,spring框架中会有map存放对象。id值作key,value是对象
        例如:springMap.put("someService",new SomeServiceImpl());

        一个bean标签声明一个对象
    -->
    <bean id="someService" class="com.daihan.service.impl.SomeServiceImpl"/>
    <bean id="someService1" class="com.daihan.service.impl.SomeServiceImpl"/>
    <!--Spring创建一个非自定义类的对象-->
    <bean id="myDate" class="java.util.Date"/>
</beans>

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

-->

4、test目录下实现,通过ApplicationContext接口和它的实现类ClassPathXmlApplicationContext
实现一

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

        //2、创建表示spring容器的对象,ApplicationContext
        //ApplicationContext 表示spring容器,通过容器获取对象
        //ClassPathXmlApplicationContext 表示从类路径(target/class/)中加载spring的配置文件
        //此时 创建容器,spring创建对象
        ApplicationContext ac=new ClassPathXmlApplicationContext(config);

        //从容器中获取某个对象,需调用对象的方法
        //getBean("配置文件中的bean的id"),返回值是Object
        SomeService service= (SomeService) ac.getBean("someService");

        //使用spring创建好的对象
        service.doSome();
    }

实现二:获取Spring容器中 Java对象的信息(数量,名称)

    @Test
    public void test03(){
        String config="beans.xml";
        ApplicationContext ac=new ClassPathXmlApplicationContext(config);

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

二、IoC控制反转

控制反转:IoC是一种思想,指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。实质就是对象控制权的转移,通过外部容器实现对象的创建、属性赋值、依赖管理。
作用:使用IoC减少对代码的改动,也能实现不同的功能。实现解耦合。

控制:创建对象,对象属性的赋值,对象之间的关系管理。
反转:把创建对象的权限转移给代码之外的容器实现。
(容器:是一个服务器软件,一个框架例如spring)
(正转:由开发人员在代码中使用new构造方法创建对象,开发人员主动管理对象)

IoC的体现:例如Tomcat

IoC技术实现:DI (Dependency Injection)依赖注入,只需要在程序中提供要使用的对象名称就可以,至于如何在容器中创建、赋值、查找都是由容器内部实现。
Spring是使用DI实现IoC的功能,底层创建对象使用的是反射机制。(Spring是一个容器,管理对象,给属性赋值,底层是反射机制创建对象

给Java对象属性赋值

在Spring的配置文件中,给java对象属性赋值

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

di实现语法:
1、在spring配置文件,使用标签和属性完成,叫做基于xml的di实现
2、使用spring中的注解,完成属性赋值,叫做基于注解的di实现

di语法分类
1、set注入(设值注入):spring调用类的set方法,在set方法可以实现属性的赋值。(使用广泛)
2、构造注入,spring调用类的有参构造方法,创建对象。

实现步骤:
1、创建maven项目
2、加入maven依赖
    spring的依赖,版本5.2.5
    junit依赖
3、创建类(接口和他的实现类)
    和没有使用框架一样,就是普通的类
4、 创建Spring需要使用的配置文件
    声明类的信息,这些类由Spring创建和管理
    通过spring语法完成属性的赋值
5、测试Spring创建对象

总结: 经常改的就用配置文件,不经常修改的就用注解。注解为主,配置文件辅助。

基于配置文件(set设值注入)

Spring底层调用setxxx()方法,给属性赋值。例如它利用配置文件中 name=“age” 推测类中有setName方法,从而调用这个方法。所以无论Student类中是否有name属性,只要有setName方法就能成功调用。

简单类型的set注入

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 +
                '}';
    }
}

配置文件(src/main/resources目录下)

<?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对象
    di:给属性赋值
    1、set注入
        (spring调用setxxx()方法,给属性赋值。利用name="age"推测类中有setName方法,从而调用这个方法)
        1)简单类型的set注入
            <bean id="xx" class="yyy">
                一个property只能给一个属性赋值,多个属性多个property
                <property name="属性名字" value="属性值"/>

            </bean>
-->
    <bean id="myStudent" class="com.daihan.ba01.Student">
        <property name="age" value="20"/><!--setName("李四")-->
        <property name="name" value="李四"/>
    </bean>
</beans>
引用类型的set注入


Student中涉及School类,School类省略

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

    //引用类型
    private School school;

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

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

    public void setSchool(School school) {
        System.out.println("setSchool:+school");
        this.school = school;
    }

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

配置文件

<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">
<!--声明Student对象
    di:给属性赋值
    1、set注入
        (spring调用setxxx()方法,给属性赋值。利用name="age"推测类中有setName方法,从而调用这个方法)
        1)简单类型的set注入
            <bean id="xx" class="yyy">
                一个property只能给一个属性赋值,多个属性多个property
                <property name="属性名字" value="属性值"/>

            </bean>
        2)引用类型的set注入
            <bean id="xxx" class="yyy">
                <property name="属性名称" ref="bean的id(对象名称)">
            </bean>
-->
    <bean id="myStudent" class="com.daihan.ba02.Student">
        <property name="age" value="20"/>
        <property name="name" value="李四"/>
        <!--引用类型-->
        <property name="school" ref="mySchool"/>
    </bean>
    
    <!--声明School对象-->
    <bean id="mySchool" class="com.daihan.ba02.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"
       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">
<!--声明Student对象
    di:给属性赋值
    1、set注入
        (spring调用setxxx()方法,给属性赋值。利用name="age"推测类中有setName方法,从而调用这个方法)
        1)简单类型的set注入
            <bean id="xx" class="yyy">
                一个property只能给一个属性赋值,多个属性多个property
                <property name="属性名字" value="属性值"/>

            </bean>
        2)引用类型的set注入
            <bean id="xxx" class="yyy">
                <property name="属性名称" ref="bean的id(对象名称)">
            </bean>
    2、构造注入
        (Spring调用类的有参构造方法,在创建对象的同时完成赋值)
        使用:<constructor-arg>标签,一个<constructor-arg>表示构造方法一个参数。
        <constructor-arg>标签属性:
            name:表示构造方法的形参名
            index:表示构造方法参数位置,从左往右,0、1、
            value:构造方法形参是简单类型,使用value
            ref:构造方法形参是引用类型,使用ref


-->
    <!--使用name定位属性,给属性赋值-->
    <bean id="myStudent" class="com.daihan.ba03.Student">
        <constructor-arg name="name" value="Kramer"/>
        <constructor-arg name="age" value="20"/>
        <constructor-arg name="school" ref="mySchool"/>
    </bean>

    <!--使用index定位属性,给属性赋值(推荐)-->
    <bean id="myStudent2" class="com.daihan.ba03.Student">
        <constructor-arg index="0" value="Lee"/>
        <constructor-arg index="1" value="22"/>
        <constructor-arg index="2" ref="mySchool"/>
    </bean>

    <!--省略index-->
    <bean id="myStudent3" class="com.daihan.ba03.Student">
        <constructor-arg value="Lee"/>
        <constructor-arg value="22"/>
        <constructor-arg ref="mySchool"/>
    </bean>

    <!--声明School对象-->
    <bean id="mySchool" class="com.daihan.ba03.School">
        <property name="name" value="北京大学"/>
        <property name="address" value="北京海淀区"/>
    </bean>
</beans>
自动注入byName、byType

引用类型才有自动注入
配置文件

<?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框架根据某些规则可以给”引用类型“赋值
        常用的是:byName,byType
        1、byName 按名称注入:java类中引用类型的属性名和Spring容器中(配置文件)<bean>的id名称一样,
                            且数据类型一致,这样容器中的bean就能赋值给引用类型。
            语法:<bean id="xx" class="yyy" autowire="byName">
                    简单类型赋值...
                    //写完简单类型就行,引用类型Spring会根据名字去找
                </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">
                    简单类型赋值...
                    //写完简单类型就行,引用类型Spring会根据名字去找
                </bean>
    -->
    <!--byName示例-->
    <bean id="myStudent" class="com.daihan.ba04.Student" autowire="byName">
        <property name="age" value="20"/>
        <property name="name" value="李四"/>
    </bean>

    <!--byType示例-->
    <bean id="myStudent" class="com.daihan.ba05.Student" autowire="byType">
        <property name="age" value="20"/>
        <property name="name" value="张三"/>
    </bean>
    
    <!--声明School对象-->
    <bean id="school" class="com.daihan.ba04.School">
        <property name="name" value="清华大学"/>
        <property name="address" value="北京海淀区"/>
    </bean>
</beans>

测试代码省略

多配置文件的方式

优点:
1、文件大小得到控制,打开关闭效率高。
2、避免多人竞争带来的冲突。
分配方式:
1、按功能模块分类
2、按类的功能分类,数据库相关分一块,事务处理分一块,service功能分一块

注:使用通配符*时,必须保证配置文件在resources下同一文件夹中

示例:(配置文件建议在resources下同一文件夹中)
主配置文件

<?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-total表示主配置文件,用于包含其他配置文件,主配置文件一般不定义对象
    语法:<import resource="其他配置文件的类路径"/>
    关键字:"classpath:" 表示类路径(class文件所在目录,target/class目录下),在Spring配置文件中要指定其他文件的位置,
            则需要使用classpath告诉Spring到哪加载文件。
    -->
    <!--方法一:加载文件列表-->
    <!--
    <import resource="classpath:ba06/Spring-School.xml"/>
    <import resource="classpath:ba06/Spring-Sutdent.xml"/>
    -->

    <!--方法二:在包含关系的配置文件中,可以通配符(*:表示任意字符)-->
    <import resource="classpath:ba06/Spring-*.xml"/>
</beans>

Student配置文件

<?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">
    <!--Student模块所有bean声明-->
    <bean id="myStudent" class="com.daihan.ba06.Student" autowire="byType">
        <property name="age" value="30"/>
        <property name="name" value="张三"/>
    </bean>
</beans>

School配置文件

<?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">
    <!--School模块bean的声明-->
    <bean id="myschool" class="com.daihan.ba06.School">
        <property name="name" value="航空大学"/>
        <property name="address" value="北京海淀区"/>
    </bean>
</beans>

测试代码

public class MyTest06 {
    @Test
    public void test02(){
        //加载的主文件
        String config= "ba06/total.xml";
        ApplicationContext ac=new ClassPathXmlApplicationContext(config);

        //从容器中获取Student对象
        Student myStudent=(Student) ac.getBean("myStudent");
        System.out.println("student“"+myStudent);
    }
}

基于注解的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

创建对象

Student类

package com.daihan.ba01;

import org.springframework.stereotype.Component;

/**
 * @Component:创建对象
 *  属性:value 就是对象名称,也就是bean的id值
 *      value的值是唯一的,创建的对象在Spring容器中就一个
 *  位置:类的上面
 *
 *  @Component(value="myStudent")等同于
 *  <bean id="myStudent" class="com.daihan.ba01.Student"/>
 *
 *  Spring中和@Component功能一致,创建对象的注解还有:
 *  @Respotory (持久层):放在dao的实现类上面,表示创建dao对象,dao对象是能访问数据库的。
 *  @Service (业务层):放在Service的实现类上面,创建Service对象,做业务处理
 *  @Controller (控制器上):放在控制器上,创建控制器对象,控制器对象能接受用户提交的参数,显示请求的处理结果
 *  以上三个注解的使用语法和@Component一样。都能创建对象,但是这三个注解还有额外的功能。
 *  这三个注解是给项目的对象分层的,
 *
 */
//常规写法@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">

    <!--声明组件扫描器
        base-package:指定注解在项目中的包名
    -->
    <context:component-scan base-package="com.daihan.ba01"/>

    <!--指定多个包的三种方式-->
    <!--方式一:多次使用组件扫描器标签,指定不同的包-->
    <!--方式二:使用分隔符(;或者,)分隔多个包-->
    <context:component-scan base-package="com.daihan.ba01;com.daihan.ba02"/>
    <!--方式三:指定父包-->
    <context:component-scan base-package="com.daihan"/>
</beans>

主程序

package com.daihan;

import com.daihan.ba01.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class myTest {
    @Test
    public void test01(){
        String config="applicationContext.xml";
        ApplicationContext ac=new ClassPathXmlApplicationContext(config);

        Student student= (Student) ac.getBean("myStudent");
        System.out.println("student:"+student);
    }
}
简单类型属性赋值

Student类

package com.daihan.ba02;


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

//省略value写法:
@Component("myStudent")
public class Student {
    /**
     * @Value :简单啊类型的属性赋值
     * 属性:value 是String类型,表示简单类型的属性值
     * 位置: 1、在属性定义上面,无需set方法,推荐使用
     *       2、在set方法上面
     */
    @Value(value="Kramer")
    private String name;
    @Value(value="22")
    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 +
                '}';
    }
}

配置文件类似上一节代码
主程序代码类似上一节代码

引用类型属性赋值
byType

Student类

package com.daihan.ba03;

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

@Component("myStudent")
public class Student {

    @Value(value="Kramer")
    private String name;
    @Value(value="22")
    private Integer age;

    /**
     * 引用类型赋值
     * 1、@Autowired Spring框架提供的注解,实现引用类型赋值。
     * Spring通过注解给引用类型赋值使用的是自动注入原理。@Autowired默认使用的是byType
     *
     * 位置:1、在属性定义的上面,无需set方法,推荐使用
     *      2、在set方法上面
     */
    @Autowired
    private School school;

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

School类,省略,类似创建对象后,给简单类型赋值
配置文件同上,
主程序同上

byName

Student类

package com.daihan.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="Kramer")
    private String name;
    @Value(value="22")
    private Integer age;
    /**
     * 引用类型赋值
     * 1、@Autowired Spring框架提供的注解,实现引用类型赋值。
     * Spring通过注解给引用类型赋值使用的是自动注入原理。@Autowired默认使用的是byType
     *
     * 位置:1、在属性定义的上面,无需set方法,推荐使用
     *      2、在set方法上面
     *
     * 使用byName的方式:
     *  1、在属性上方加入@Autowired
     *  2、在属性上面加入@Qualifier(value="bean的id"):表示使用指定名称的bean完成赋值
     */
    @Autowired
    @Qualifier(value = "school")
    private School school;

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

@Autowired的required属性

required属性是boolean类型,默认是true(推荐)。表示引用类型赋值失败,则程序报错,并终止执行
改写成 @Autowired(required=false)之后,赋值失败的引用类型,会赋值为null

@Resource注解

加入依赖

<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.1</version>
</dependency>

来自JDK
Student类

package com.daihan.ba06;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;

@Component("myStudent")
public class Student {
    @Value(value="Kramer")
    private String name;
    @Value(value="22")
    private Integer age;

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

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

School类

package com.daihan.ba06;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("mySchool")
public class School {
    @Value("四川大学")
    private String name;
    @Value("成都市")
    private String address;

    public void setAddress(String address) {
        this.address = address;
    }

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

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

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

    <!--声明组件扫描器
        base-package:指定注解在项目中的包名
    -->
    <context:component-scan base-package="com.daihan.ba06"/>

</beans>
package com.daihan;

import com.daihan.ba06.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class myTest06 {
    @Test
    public void test01(){
        String config="applicationContext06.xml";
        ApplicationContext ac=new ClassPathXmlApplicationContext(config);

        Student student= (Student) ac.getBean("myStudent");
        System.out.println("student:"+student);
    }
}

三、AOP面向切面编程

AOP:
面向切面编程,面向切面编程是从动态代理较低考虑程序运行过程。
AOP底层就是采用动态代理模式实现的。采用了两种:JDK动态代理(有接口),CGLIB动态代理(无接口)。
AOP就是动态代理的规范化

面向切面编程:
将交叉业务逻辑封装成切面,利用AOP容器功能将切面织入主业务逻辑中。(交叉业务逻辑是指通用的,与主业务无关的代码,例如安全检查、事务、日志等)

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

AOP的实现:
1、Spring:

Spring内部实现了aop规范,能做aop的工作。
Spring主要在事务处理时使用aop
我们项目开发时很少使用Spring的aop,因为Spring的aop很笨重

2、aspectJ:(主)

一个专门做aop的框架。Spring框架中集成了aspectJ的功能
aspectJ实现aop:
1、使用xml的配置文件,事务的时候使用
2、使用注解,项目中一般使用注解。一共用五个

什么时候使用AOP:
1、当需要给已存在的某个类增加功能,但又没有源代码的时候
2、给项目中多个类增加相同共能的时候
3、给业务方法增加例如事务、日志等功能的时候

初步使用(jdk)

动态代理:
在程序的执行过程中,创建代理对象。通过代理对象执行方法,给目标类的方法增加额外功能。(功能增强)

jdk动态代理实现步骤:
1、创建目标类,SomeServiceImpl目标类,给它的doSome,doOther方法增加输出时间和事务
2、创建InvocationHandler接口的实现类,在这个类实现给目标方法增加功能。
3、创建jdk中 类Proxy,创建代理对象,实现创建对象能力。

SomeService接口

package com.daihan.service;

public interface SomeService {
    void doSome();
    void doOther();
}

SomeServiceImpl目标类

package com.daihan.service.impl;

import com.daihan.service.SomeService;

//Service类代码不修改的前提下,增加其他功能
public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome() {
        System.out.println("执行业务方法doSome");
    }

    @Override
    public void doOther() {
        System.out.println("执行业务方法doOther");
    }
}

要增加的方法封装进ServiceTools

package com.daihan.util;

import java.util.Date;

public class ServiceTools {
    public static void doLog(){
        System.out.println("执行时间:"+new Date());
    }

    public static void doTrans(){
        System.out.println("提交执行结果");
    }
}

使用jdk动态代理,创建InvocationHandler接口的实现类

package com.daihan.handler;

import com.daihan.util.ServiceTools;

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

public class MyInvocationHandler implements InvocationHandler {
    //目标对象
    private Object target;//本例中,目标对象会是SomeServiceImpl类

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

    //通过代理对象执行方法时,会调用执行这个invoke()
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result=null;

        //doSome()执行之前加上时间
        ServiceTools.doLog();

        //执行目标类方法,通过Method类实现。args是所要调用方法的参数列表
        result=method.invoke(target,args);//表示SomeServiceImpl.doSome()或SomeServiceImpl.doOther()

        //doSome()执行之后加上提交事务
        ServiceTools.doTrans();

        //返回目标方法执行结果
        return result;
    }
}

主程序

package com.daihan;

import com.daihan.handler.MyInvocationHandler;
import com.daihan.service.SomeService;
import com.daihan.service.impl.SomeServiceImpl;

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

public class MyApp {
    public static void main(String[] args) {

        //使用jdk的Proxy创建代理对象
        //1、创建目标对象
        SomeService target=new SomeServiceImpl();
        //2、创建InvocationHandler对象
        InvocationHandler handler=new MyInvocationHandler(target);
        //3、使用Proxy创建代理
        SomeService proxy= (SomeService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), handler);
        //4、通过代理执行方法,会自动调用handler中的invoke()方法
        proxy.doOther();

    }
}

aspectJ框架使用(掌握)

实现方式:1、使用注解实现(主)。2、使用xml配置文件的标签实现(做事务时用)。

1、概念
aspect:切面,表示给业务方法增加的功能,例如日志输出,权限检查
pointcut:切入点,是一个或多个JoinPoint的集合,表示切面功能执行的位置
advice:通知,也叫增强,表示切面执行的时间,在方法前、后等等

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

通知注解修饰的方法叫做通知方法。被增强功能的叫目标方法。

3、切面执行的位置。使用的切入点表达式:

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								抛出异常类型
?													可选的部分
以上表达式共四个部分:
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
	**包名类名+方法名算一个部分**
	方法返回值 方法声明(参数)是必须的
四个部分之间用空格进行分隔。

有pattern 可以使用通配符:
*:表示0至任意多字符
…:用在方法参数中表示任意多个参数;用在包名后,表示当前包及其子包路径
例如:execution(public * *(...))指定切入点为任意公共方法
execution(* set*(...))指定切入点为任意以set开头的方法

@Before

在目标方法执行之前执行。被注解为前置通知的方法,可以包含一个JoinPoint类型参数。该类型的对象本身就是切入点表达式。通过该参数,可以获取切入点表达式、方法签名、目标对象等。

前置操作、加入依赖

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

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

第一步,创建业务接口及其实现类

package com.daihan.ba01;

public interface SomeService {
    public void doSome(String name,Integer age);
}
package com.daihan.ba01;

public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome(String name,Integer age) {
        //给doSome方法增加一个功能,在doSome方法执行前,输出方法执行时间
        System.out.println("=======doSome()执行=========");
    }
}

第二步、创建切面类

package com.daihan.ba01;

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、方法可以有参数,也可以没有参数
     *          如果有参数,参数不是自定义的,有几个参数类型可以使用
     */


    /**
     * @Before 前置通知注解
     *  属性:value,是切入点表达式,表示切入点执行功能的位置
     *      位置:在方法上面
     *  特点:1、在目标方法之前执行
     *      2、不会改变目标方法执行结果
     *      3、不会影响目标方法的执行。
     *
     */
    @Before(value="execution(public void *..SomeServiceImpl.doSome(String ,Integer ))")
    public void myBefore(){
        //切面要执行的功能代码
        System.out.println("前置通知,在目标方法执行前输出执行时间:"+new Date());
    }
}

第三步、创建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" 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容器同一创建-->
    <!--声明目标对象-->
    <bean id="someService" class="com.daihan.ba01.SomeServiceImpl"/>
    <!--声明切面类对象-->
    <bean id="myAspect" class="com.daihan.ba01.MyAspect"/>
    <!--声明自动代理生成器:使用aspectj框架内部功能,创建目标代理的代理对象
        创建代理对象实在内存中实现的,修改目标对象的内存中的结构。将其创建为代理对象
        所以目标对象就是被修改后的代理对象

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

第四步、测试

package com.daihan;

import com.daihan.ba01.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest01 {
    @Test
    public void test01(){
        String config="applicationContext.xml";
        ApplicationContext context = new ClassPathXmlApplicationContext(config);

        //从容器中获取目标对象
        SomeService proxy = (SomeService) context.getBean("someService");

        //通过代理对象执行方法,实现目标方法执行时增强功能
        proxy.doSome("lisi",20);
    }
}

插入知识点JoinPoint

  指定通知方法中的参数:JoinPoint
  JoinPoint:要加入切面功能的业务方法
       作用:可以在通知方法(通知注解例如@Before修饰的方法)中获取方法执行时的信息,例如方法名称,方法实参
       JoinPoint参数指是由框架赋予的,**必须是第一个参数位置**

原有切面类,根据业务稍加修改

    @Before(value="execution(public void *..SomeServiceImpl.doSome(String ,Integer ))")
    public void myBefore(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());
    }

@AfterReturing

第一步,创建业务接口及其实现类

package com.daihan.ba02;

public interface SomeService {
    public void doSome(String name,Integer age);
    public String doOther(String name,Integer age);
}
package com.daihan.ba02;

public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome(String name,Integer age) {
        //给doSome方法增加一个功能,在doSome方法执行前,输出方法执行时间
        System.out.println("=======doSome()执行=========");
    }

    @Override
    public String doOther(String name, Integer age) {
        System.out.println("=======doOther()执行=========");
        return "abcd";
        //在代码执行后,执行业务,例如事务提交
    }
}

第二步、创建切面类

package com.daihan.ba01;

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

import java.util.Date;
@Aspect
public class MyAspect {
    /**
     * 后置通知定义方法,方法是实现切面功能
     * 方法定义要求:
     *      1、公共的方法public
     *      2、方法没有返回值
     *      3、方法名称自定义
     *      4、方法有参数,参数推荐是Object,参数名自定义
     */

    /**
     * @AfterReturning :后置通知
     * 属性:1、value 切入点表达式
     *      2、returning 自定义变量,表示目标方法返回值
     *          自定义变量名必须和通知方法的形参名一样
     *  位置:在方法定义的上面
     *  特点:1、在目标方法执行之后执行
     *       2、能获取目标方法的返回值,可以根据这个返回值做不同的处理功能。
     *          即 本例Object res=doOther(),通知方法就可以使用res了
     *       3、可以修改返回值
     *
     *  后置通知的执行
     *      先Objext res=doOther();
     *      再myAfterReturning(res);
     */
    @AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",returning = "res")
    public void myAfterReturning(Object res){
        //Object res就是目标方法执行后的返回值,根据返回值做切面的功能
        System.out.println("后置通知:在目标方法之后执行"+res);

        //后置通知方法不会改变String
        if(res!=null)res="111";
    }
}

疑问:后置通知方法是否能改变引用类型例如自定义Student类对象的属性值
第三步、创建Spring配置文件

同上

第四步、测试

package com.daihan;

import com.daihan.ba02.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest02 {
    @Test
    public void test01(){
        String config="applicationContext02.xml";
        ApplicationContext context = new ClassPathXmlApplicationContext(config);

        //从容器中获取目标对象
        SomeService proxy = (SomeService) context.getBean("someService");

        //通过代理对象执行方法,实现目标方法执行时增强功能
        String str=proxy.doOther("张三",20);
        System.out.println(str);
    }
}

@Around

 第一步,创建业务接口及其实现类
package com.daihan.ba03;

public interface SomeService {
    public void doSome(String name,Integer age);
    public String doOther(String name,Integer age);
    public String doFirst(String name,Integer age);
}
package com.daihan.ba03;

public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome(String name,Integer age) {
        //给doSome方法增加一个功能,在doSome方法执行前,输出方法执行时间
        System.out.println("=======doSome()执行=========");
    }

    @Override
    public String doOther(String name, Integer age) {
        System.out.println("=======doOther()执行=========");
        return "abcd";
        //在代码执行后,执行业务,例如事务提交
    }

    @Override
    public String doFirst(String name, Integer age) {
        System.out.println("=======业务方法doFirst======");
        return "doFirst";
    }
}

第二步、创建切面类

package com.daihan.ba03;

import org.aspectj.lang.ProceedingJoinPoint;
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 切入点表达式
      *     位置:方法定义上面
      *     特点:功能最强的通知
      *          在目标方法前后都能增加功能
      *          控制目标方法是否被调用执行
      *          修改原来目标方法的执行结果,影响最后的调用结果
      *     环绕通知等同于jdk动态代理的InvocationHandler接口
      *
      *     参数:ProceedingJoinPoint等同于jdk动态代理中的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("张三".equals(name)){
             //符合条件 调用方法
             result=pjp.proceed();//等同于jdk中method。invoke();

         }
         //result=pjp.proceed();//等同于jdk中method。invoke();
         System.out.println("在目标方法执行后提交事务");
         //2、在目标方法前/后加功能
         //加在目标方法前,就写在result=pjp.proceed();上面

        //返回目标方法执行结果
         return result;
    }
}

第三步、创建Spring配置文件

同上

第四步、测试

package com.daihan;

import com.daihan.ba03.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest03 {
    @Test
    public void test01(){
        String config="applicationContext03.xml";
        ApplicationContext context = new ClassPathXmlApplicationContext(config);

        //从容器中获取目标对象
        SomeService proxy = (SomeService) context.getBean("someService");

        //通过代理对象执行方法,实现目标方法执行时增强功能
        String str=proxy.doFirst("张三1",20);
        //System.out.println(str);
    }
}
@Around可以修改目标方法执行结果,影响方法最后的调用结果

切面类:

package com.daihan.ba03;

import org.aspectj.lang.ProceedingJoinPoint;
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 切入点表达式
      *     位置:方法定义上面
      *     特点:功能最强的通知
      *          在目标方法前后都能增加功能
      *          控制目标方法是否被调用执行
      *          修改原来目标方法的执行结果,影响最后的调用结果
      *     环绕通知等同于jdk动态代理的InvocationHandler接口
      *
      *     参数:ProceedingJoinPoint等同于jdk动态代理中的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("张三".equals(name)){
             //符合条件 调用方法
             result=pjp.proceed();//等同于jdk中method。invoke();

         }
         //result=pjp.proceed();//等同于jdk中method。invoke();
         System.out.println("在目标方法执行后提交事务");
         
         //2、在目标方法前/后加功能
         //注:加在目标方法前,就写在result=pjp.proceed();上面

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

        //返回目标方法执行结果
         return result;
    }
}

@AfterThrowing

1、在目标方法抛出异常时执行
2、可以做异常的监控程序
有两个属性:
value:切入点表达式
throwing:切面方法参数public void myAfterThrowing(Exception ex){...}

@After

1、最终通知,总是会执行的,在目标方法之后执行
2、public、没有返回值、方法名自定义、没有参数 有的话就是JoinPoint

属性:
value:切入点表达式

@Pointcut

不是通知注解,是一个辅助注解。
Pointcut 定义和管理切入点,如果项目中有多个切入点表达式是重复的,可以复用的,则可以使用Pointcut。
属性(value=切入点表达式)
位置 自定义方法上面
特点:当使用@Pointcut在方法上面时,这个方法的名称就是切入点表达式的别名。其他通知中value属性就可以直接使用这个方法的名称代替切入点表达式。方法内无需代码。

CGLIB

上述代码有接口的时候,底层使用的就是jdk动态代理方式。(有接口也能用CGLIB动态代理,配置文件 <aop:aspectj-autoproxy/>改为 <aop:aspectj-autoproxy proxy-target-class="true"/>
上述代码没有接口的时候,底层使用的就是CGLIB动态代理。

四、Spring集成MyBatis

把Mybatis框架和Spring框架集成在一起,像一个框架一样使用。使用的技术是ioc。为什么ioc能集成Mybatis和Spring呢?是因为ioc能创建对象,可以把Mybatis中的对象交给Spring统一创建,开发人员从Spring中获取对象。

复习Mybatis使用步骤

1、定义dao接口,StudentDao
2、定义mapper文件,StudentDao.xml
3、定义mybatis主配置文件
4、创建dao代理对象

StudentDao dao=SqlSession.getMapper(StudentDao.class);
List<Student> students=dao.selectStudents();

要使用dao对象,需要getMapper()方法,使用getMapper()方法,需要条件:
1、获取SqlSession对象,需要SqlSessionFactory的openSession()方法。
2、创建SqlSessionFactory对象。通过读取Mybatis主配置文件,能创建SqlSessionFactory对象。
总之,需要SqlSessionFactory对象,使用Factory能获取SqlSession对象,有了SqlSession对象就能有dao,目的就是获取dao对象。

通过以上说明,我们需要让Spring创建一下对象
1、独立的连接池类对象,使用阿里的druid连接池。
2、SqlSessionFactory对象
3、创建dao对象
需要学习的就是上面三个对象的创建语法。使用xml的bean标签

具体实现

步骤:
1、创建maven项目
2、加入依赖
1)Spring依赖
2)Mybatis依赖
3)mysql驱动
4)Spring事务的依赖
5)Mybatis和Spring集成的依赖
3、创建实体类
4、创建dao接口和mapper文件
5、创建Mybatis主配置文件
6、创建Setvice接口和实现类,属性是dao
7、创建Spring的配置文件,声明Mybatis的对象交给Spring创建
1)数据源:DataSource
2)SqlSessionFactory
3)Dao对象
4)声明自定义Service
8、创建测试类
maven依赖

 <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!--Spring核心ioc-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <!--做Spring事务-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <!--Mybatis-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.6</version>
    </dependency>
    <!--Spring、Mybatis集成的依赖-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.1</version>
    </dependency>
    <!--Mysql驱动-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.20</version>
    </dependency>
    <!--阿里公司的数据库连接池-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.12</version>
    </dependency>
  </dependencies>

  <build>
    <!--目的是把src/main/java目录中的xml文件包含到输出结果中。输出的classes目录中-->
    <resources>
      <resource>
        <directory>src/main/java</directory><!--所在的目录-->
        <includes><!--包括目录下的.properties,.xml文件都会扫描到-->
          <include>**/*.properties</include>
          <include>**/*.xml</include>
        </includes>
        <filtering>false</filtering>
      </resource>
    </resources>

    <!--指定jdk版本-->
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.0</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

dao包:
接口

package com.daihan.dao;

import com.daihan.domain.Student;
import java.util.List;

public interface StudentDao {
    int insertStudent(Student student);
    List<Student> selectStudents();
}

xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.daihan.dao.StudentDao">
    <insert id="insertStudent" >
        insert into student value(#{id},#{name},#{email},#{age})
    </insert>
    <select id="selectStudents" resultType="com.daihan.domain.Student">
        select id,name,email,age from student order by id desc
    </select>
</mapper>

实体包
Student

service包
StudentService 接口

package com.daihan.servicce;

import com.daihan.domain.Student;
import java.util.List;

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

StudentService 实现类

package com.daihan.servicce;

import com.daihan.dao.StudentDao;
import com.daihan.domain.Student;

import java.util.List;

public class StudentServiceImpl implements StudentService {

    //引用类型dao
    private StudentDao studentDao;

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

    @Override
    public int addStudent(Student student) {
        int i = studentDao.insertStudent(student);
        return i;
    }

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

main/resources目录下
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" xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">

    <!--声明数据源DataSource,作用是连接数据库的-->
    <bean id="myDataSource"  class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <!--set注入给DruidDataSource提供连接数据库的信息-->
        <property name="url" value="jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC&amp;useUnicode=true&amp;characterEncoding=utf8"/><!--setUrl()-->
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    </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.daihan.dao"/>
    </bean>

</beans>

数据库配置文件

<?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打印日志-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    <!--设置别名-->
    <typeAliases>
        <package name="com.daihan.domain"/>
    </typeAliases>


    <mappers>
        <!--<mapper resource="com/daihan/dao/StudentDao.xml"/>-->
        <!--
            name :是包名.
            这个包下所有xml一次性找到
        -->
        <package name="com.daihan.dao"/>
    </mappers>
</configuration>

五、Spring事务

概念

一、什么是事务
事务是指一组sql语句的聚合,我们希望这些sql语句能整体成功或整体失败。

二、什么时候想到使用事务
涉及多个表,或者多个sql语句的insert,update,deletede。需要保证这些语句全都成功或失败。
Java代码中写程序,控制事务,此时事务应该放在那里?放在service类的业务方法上,业务方法调用多个dao方法执行sql语句

三、通常使用JDBC访问数据库,还是Mybatis访问数据库,怎么处理事务
JDBC访问数据库,处理事务 Connection conn; conn.commit() ; conn.rollback();
mybatis访问数据库,处理事务,SqlSession.commit();SqlSession.rollback();

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

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

五、怎么解决不足
Spring提供了一种处理事务的统一模型,能使用一种统一的步骤,方式完成多种不同数据库访问技术的事务处理。
使用Spring事务处理机制,可以完成Mybatis、Hibernate等的访问数据库事务处理。

六、处理事务,需要怎么做,做什么
Spring处理事务的模型,使用步骤是固定的,把事务使用的信息提供给Spring就行了。
1、事务内部提交,回滚事务,使用的事务管理器对象,代替程序员完成commit,rolback
事务管理器是一个接口和他的众多实现类。
接口:PlatformTransactionManager,定义了事务重要方法commit,rollback
实现类:Spring把每一种数据库访问技术对应的事务处理类都创建好了。
使用:需要告诉Spring使用的是哪种数据库访问技术,通过声明数据库访问技术对应的事务管理器实现类。
例如:使用Mybatis访问数据库则声明 <bean id=" xxx" class="...DataSourceTransactionManager">

2、业务方法需要做什么样的事务,需要说明事务的类型。
说明方法需要的事务:
1)事务的隔离级别:实际有4个值。
ISOLATION_DEFAULT :Mysql默认ISOLATION_REPEATABLE_READ
ISOLATION_READ_UNCONMITTED:读未提交
ISOLATION_READ_COMMITED:读已提交
ISOLATION_REPEATABLE_READ:可以重复读
ISOLATION_SERIALIZABLE:串行化

2)事务的超时时间:
表示一个方法最长执行时间,超出时间,事务回滚。单位是秒,整数值,默认-1

3)事务的传播行为:
控制业务方法是不是有事务的,是什么样的事务
7个传播行为,表示业务方法调用时,事务在方法之间是如何使用的。掌握三个
1)PROPAGATION_REQUIRED(默认):方法必须在事务内执行。事务开始,执行方法,事务结束。
2)PROPAGATION_REQUIRES_NEW:总是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务完成
3)PROPAGATION_SUPPORTS:指定方法支持当前事务,但若当前没有事务,也可以以非事务方法执行

4)PROPAGATION_MANDATORY
5)PROPAGATION_NESTED
6)PROPAGATION_NEVER
7)PROPAGATION_MOT_SUPPORTED

3、Spring提交事务,回滚事务的时机
1)当业务方法执行成功,没有异常抛出,当方法执行完毕。Spring在方法执行后提交事务。调用事务管理器的commit
2)业务方法抛出运行时异常,Spring执行回滚。调用事务管理器的rollback
3)非运行时异常,主要是受查异常时,提交事务。

总结Spring事务

1、管理事务的是 事务管理和他的实现类
2、Spring的事务是一个统一模型
1)指定方法需要的隔离级别,传播行为,超时

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

1、注解方案(中小项目)

Spring自己使用AOP实习那给业务方法增加事务功能,使用@Transactional注解增加事务。
@Transactional注解是Spring框架自己的注解,放在public方法上面,表示当前方法具有事务。
可以给注解属性赋值,表示具体隔离级别,传播行为,异常信息等。
属性:
propagation:(枚举)设置事务传播行为
isolation:(枚举)设置事务隔离级别
readOnly:(布尔)设置方法对数据库操作是否是只读。
timeout:(int)超时时间。一般不设置
rollbackFor:(Class[])指定要回滚的异常类

使用步骤

1、声明事务管理器对象 <bean id="xx" class="DataSourceTransactionManager">
2、开启事务注解驱动,告诉Spring要使用注解的方式管理事务
Spring使用aop机制,创建@Transactional所在的类代理对象,给方法加入事务功能。
Spring给业务方法加入事务:
在业务方法执行之前,开启事务,在业务方法之后提交或回滚事务,使用aop环绕通知。
3、在方法上面加注解

实现

做事务的环境项目,步骤
1、创建maven项目
2、加入依赖
1)Spring依赖
2)Mybatis依赖
3)mysql驱动
4)Spring事务的依赖
5)Mybatis和Spring集成的依赖
3、创建实体类
Good,Sale
4、创建dao接口和mapper文件
SaleDao接口,GoodDao接口
SaleDao.xml,GoodDao.xml
5、创建Mybatis主配置文件
6、创建Setvice接口和实现类,属性是dao
7、创建Spring的配置文件,声明Mybatis的对象交给Spring创建
1)数据源:DataSource
2)SqlSessionFactory
3)Dao对象
4)声明自定义Service
8、创建测试类

无事务情况:D:\JAVACODE\Spring\ch08-Spring-trans
增加事务具体实现: D:\JAVACODE\Spring\ch09-Spring-trans -anno

2、AspectJ框架功能(大型项目)

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

步骤:在配置文件实现

1、要使用AspectJ框架,需要加入依赖

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

2、声明事务管理器对象 <bean id="xx" class="DataSourceTransactionManager">
3、声明方法需要的事务类型(配置方法的事务属性(隔离级别,传播,超时))
4、配置aop:指定哪些类要创建代理

具体 D:\JAVACODE\Spring\ch10-Spring-trans-aspectj

六、Spring与Web

web项目是在Tomcat服务器上运行的,Tomcat一启动,项目一直运行的。
在我们的web项目中容器对象只需要创建一次,把这个容器对象放入全局作用域ServletContext中。
实现:
使用监听器,当全局作用域对象被创建时 创建容器,存入ServletContext。
监听器作用:
1、创建容器对象,执行
String config="applicationContext.xml"; ApplicationContext ac=new ClassPathXmlApplicationContext(config);
2、把容器对象放入ServletContextServletContext.setAttribute(key,ac)
监听器可以自己创建,也可以使用框架提供好的ContextLoaderListener。

private WebApplicationContext context;
public interface WebApplicationContext extends ApplicationContext

ApplicationContext:javaSE项目中使用的容器
WebApplicationContext :web项目中使用的容器

具体 D:\JAVACODE\Spring\ch11-Spring-web

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值