Spring框架的使用

第一章:Spring概述

1. Spring全家桶:

Spring、Spring MVC、Spring Boot、Spring Cloud

2. Spring:

出现在2002年左右,解决企业开发的难度,减轻对模块之间的管理,类和类之间的关系

Spring核心技术ioc,aop。能实现模块之间、类之间的解耦合

3. 依赖:

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

4. 特点

1)轻量

2)针对接口编程,解耦合

3)AOP编程的支持

4)方便集成各种优秀框架

5. 框架怎么学?

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

1)知道框架能做什么,Mybatis——访问数据库,对表中的数据进行增删改查

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

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

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

第二章:IoC控制反转

1. IoC:

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

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

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

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

正转: 由开发人员在代码中,使用new构造方法创建对象(new对象这种行为叫做正转),开发人员主动的能够管理对象

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

2. 为什么使用IoC ?

目的就是减少对代码的改动,也能实现不同的功能,实现解耦合

3. Java中创建对象的方式:

1)构造方法 new Student()

2)通过反射机制

3)序列化

4)克隆

5)IoC容器创建对象

6)动态代理

4. Spring实现步骤:

1.创建maven项目

2. 加入maven的依赖

spring依赖:

<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.5.RELEASE</version>
</dependency>

maven编译Java程序插件:

<plugins>
  <plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.1</version>
    <configuration>
      <source>1.8</source>
      <target>1.8</target>
    </configuration>
  </plugin>
</plugins>

3.创建类(创建接口和实现类)

接口:

package com.bjpowernode.service;

public interface SomeService {
    void doSome();
}

实现类:

package com.bjpowernode.impl;

import com.bjpowernode.service.SomeService;

public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome() {
        System.out.println("执行了SomeServiceImpl的doSome()方法");
    }
}

传统的执行SomeServiceImpl的doSome()方法:

package com.bjpowernode;

import com.bjpowernode.impl.SomeServiceImpl;
import com.bjpowernode.service.SomeService;
import org.junit.Test;

public class MyTest {
    @Test
    public void test01(){
        SomeService someService = new SomeServiceImpl();
        someService.doSome();
    }
}

4.创建Spring需要使用的配置文件

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

    <bean id="someService" class="com.bjpowernode.impl.SomeServiceImpl"/>
</beans>

1)声明bean,就是告诉spring要创建某个类的对象,一个bean标签声明一个对象。可以有多个bean标签

2)

id: 对象的自定义名称,唯一值/spring通过这个名称找到对象

相当于spring完成SomeService someservice = new SomeServiceIml;

class: 类的全限定名称(不能是接口,因为spring反射机制创建对象,必须使用类)

3)

spring是把创建好的对象放入到map中,spring框架有一个map存放对象的
springMap.put(id的值,对象);
例如:spring.put(someService,new SomeServiceImpl());

5.测试

测试调用doSome()方法:

public void test02(){
    //1.指定spring配置文件的名称
    String config = "beans.xml";
    //2.创建表示spring容器的对象,ApplicationContext
    //ApplicationContext就是表示spring容器,通过这个容器获取对象
    //ClassPathXmlApplicationContext从类路径中加载spring的配置文件
    //这里创建了someService对象,可以写个无参构造测试
    ApplicationContext ac = new ClassPathXmlApplicationContext(config);
    //从容器中获取某个对象,你要调用对象的方法
    SomeService someService= (SomeService) ac.getBean("someService");
    someService.doSome();
}

Spring默认创建对象的时间:在创建Spring容器时,会创建配置文件中的所有的对象,一个bean标签是一个对象

查看容器中定义对象的数量: int num = ac.getBeanDefinitionCount();

查看容器中每个定义对象的名称: String[] names = ac.getBeanDefinitionNames();

5. 给对象的属性赋值:

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

di的实现方式有两种:

1.在spring文件中,使用标签和属性完成,叫做基于XML的di实现

2.使用spring中的注解,完成属性赋值,叫做基于注解的di实现

di的语法分类:

1.set注入(设值注入):spring调用类的set方法,在set方法可以实现属性的赋值

2.构造注入,spring调用类的有参数构造方法,创建对象,在构造方法中完成赋值

1.设值注入

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

    <bean id="student" class="com.bjpowernode.ba01.Student">
        <property name="name" value="张三"/>
        <property name="age" value="20"/>
    </bean>
</beans>

property标签中的name属性:调用的是name属性对应的Set方法

因为只是使用set方法,所以类中即使没有email这个属性,只定义一个setEmail()方法也是可以正常执行的

当类中有引用类型的属性时呢?

Student类:

package com.bjpowernode.ba01;

public class Student {
    private String name;
    private int age;
    private School school;
    //Setter()方法.....
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", school=" + school +
                '}';
    }
}

School类:

package com.bjpowernode.ba01;

public class School {
    private String name;
    private String address;
    //Setter()方法...
    @Override
    public String toString() {
        return "School{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="student" class="com.bjpowernode.ba01.Student">
        <property name="name" value="张三"/>
        <property name="age" value="20"/>
        <property name="school" ref="MySchool"/>
    </bean>

    <bean id="MySchool" class="com.bjpowernode.ba01.School">
        <property name="name" value="北京大学"/>
        <property name="address" value="北京"/>
    </bean>
</beans>

2.构造注入

构造注入使用constructor-arg 标签

创建Student和School类,并加入构造方法:

package com.bjpowernode.ba01;

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

    public Student(String myname, int myage, School myschool) {
        this.name = myname;
        this.age = myage;
        this.school = myschool;
    }

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

//=======================================================
package com.bjpowernode.ba01;

public class School {
    private String name;
    private String address;

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

创建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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="student" class="com.bjpowernode.ba01.Student">
        <constructor-arg name="myname" value="张三"/>
        <constructor-arg name="myage" value="20"/>
        <constructor-arg name="myschool" ref="MySchool"/>
    </bean>

    <bean id="MySchool" class="com.bjpowernode.ba01.School">
        <!--<property name="name" value="北京大学"/>
        <property name="address" value="北京"/>-->
        <constructor-arg name="schooladdress" value="北京"/>
        <constructor-arg name="schoolname" value="北京大学"/>
    </bean>
</beans>

也可以使用constructor-arg 标签的index属性,index可省略:

<bean id="student" class="com.bjpowernode.ba01.Student">
    <constructor-arg index="0" value="张三"/>
    <constructor-arg index="1" value="20"/>
    <constructor-arg index="2" ref="MySchool"/>
</bean>

创建测试类:

public class MyTest {
    @Test
    public void test01(){
        String config = "ba01/applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        Student student = (Student) ac.getBean("student");
        System.out.println(student);
    }
}

Student{name=‘张三’, age=20, school=School{name=‘北京大学’, address=‘北京’}}

一个constructor-arg标签表示构造方法的一个参数

constructor-arg标签中的属性:

name: 表示构造方法的形参名

index: 表示构造方法的参数的位置,参数从左往右的位置是从0开始的

value: 构造方法的形参类型是简单类型的,使用value

ref: 构造方法的形参类型是引用类型的,使用ref

3. 自动注入:

引用类型的自动注入:spring框架中根据某些规则可以给引用类型赋值,不用你再给引用类型赋值了

引用规则常用的是byName,ByType

byName:

按名称注入:Java类中引用类型的属性名和spring框架中(配置文件)bean标签的id名称一样,且数据类型是一致的,这样的容器中的bean,spring能够赋值给引用类型

1)创建Student类和School类

package com.bjpowernode.ba01;

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

	//setter方法...
    
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", school=" + school +
                '}';
    }
}
package com.bjpowernode.ba01;

public class School {
    private String name;
    private String address;

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

2)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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="student" class="com.bjpowernode.ba01.Student" autowire="byName">
        <property name="name" value="张三"/>
        <property name="age" value="20"/>
    </bean>

    <bean id="school" class="com.bjpowernode.ba01.School">
        <property name="address" value="北京"/>
        <property name="name" value="北京大学"/>
    </bean>
</beans>

3) 测试类:

public class MyTest {
    @Test
    public void test01(){
        String config = "ba01/applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        Student student = (Student) ac.getBean("student");
        System.out.println(student);
    }
}

Student{name=‘张三’, age=20, school=School{name=‘北京大学’, address=‘北京’}}

byType:

按类型注入:Java类中引用的数据类型和spring容器中(配置文件)bean标签中的class属性是 同源 关系的,这样的bean能够赋值给引用类型

同源: 就是一类的意思

1)Java类中引用类型的数据类型和bean的class的值是一样的

2)Java类中引用类型的数据类型和bean的class的值是父子类关系的

3)Java类中引用类型的数据类型和bean的class的值是接口和实现类关系的

在上述byName代码的基础之上:

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

    <bean id="student" class="com.bjpowernode.ba01.Student" autowire="byType">
        <property name="name" value="张三"/>
        <property name="age" value="20"/>
    </bean>

    <bean id="mySchool" class="com.bjpowernode.ba01.School">
        <property name="address" value="北京"/>
        <property name="name" value="北京大学"/>
    </bean>
    //假设MySchool这个类继承了School,且使用byType时,那么加入下面的bean标签会报错
    <bean id="mySchool" class="com.bjpowernode.ba01.MySchool">
        <property name="address" value="北京"/>
        <property name="name" value="北京大学"/>
    </bean>
</beans>

注意:自动类型注入xml中只能声明一个符合条件的,多于一个会报错

6. 为什么使用多配置文件:

当项目中的类很多,模块很多的时候,使用一个配置文件属实不方便

我们可以使用多配置文件来划分模块类型或类:

例如:项目中有 学生模块学校模块

学生模块:Spring-Student.xml

学校模块:Spring-School.xml

还需创建一个主配置文件:Total.xml

在Total.xml中写入import标签,并在import标签的resource属性中说明要链接的其他模块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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <import resource="classpath:ba01/Spring-Student.xml"/>
    <import resource="classpath:ba01/Spring-School.xml"/>
    
</beans>

也可以使用通配符的方式:Spring-*.xml

<import resource="classpath:ba01/Spring-*.xml"/>

使用这种方式时,主配置文件名称不能以Spring开头命名,否则会死循环,递归

7.基于注解的注入方式:

需要学习的注解:

1)@Component

2)@Respotory:用在持久层上面,放在dao的实体类上面,表示创建dao对象,dao对象是能访问数据库的

3)@Service:用在业务层类的上面的,放在service的实现类上面,创建service对象,service对象是做业务处理,可以有事务功能的

4)@Controller:用在控制器上面,放在控制器(处理器)类的上面,创建控制对象的,控制器对象能接受用户提交的参数,显示请求的处理结果

以上的功能是一样的,都能创建对象,但是这三个注解还有额外的功能,给项目对象分层的

5)@Value

6)@Autowired

7)@Resource

使用步骤:

1)创建实体类,并加入注解:

Student类:

package com.bjpowernode.ba01;

import org.springframework.stereotype.Component;
//加入注解,并命名
@Component(value = "myStudent")
public class Student {
    private String name;
    private int age;

    //Setter和Getter方法...

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

@Component(value = “myStudent”)

这句话的意思等同于:

<bean id="myStudent" class="com.bjpowernode.ba01.Student"/>

如果不指定value属性值,则获取bean的时候使用spring默认设置的名称:类名(首字母需小写)

2)在配置文件中声明扫描器:

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

    <context:component-scan base-package="com.bjpowernode.ba01"/>
    
</beans>

3)创建测试类:

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);
    }
}

如何在扫描器中指定多个不同的包呢?

指定多个包的三种方式:

1)使用多次的组件扫描器,指定不同的包

<context:component-scan base-package="com.bjpowernode.ba01"/>
<context:component-scan base-package="com.bjpowernode.ba02"/>
<context:component-scan base-package="com.bjpowernode.ba03"/>

2)使用分隔符(;或,)分隔多个包名

<context:component-scan base-package="com.bjpowernode.ba01;
                                      com.bjpowernode.ba02;
                                      com.bjpowernode.ba03"/>

3)指定父包

<context:component-scan base-package="com.bjpowernode"/>

简单类型的属性赋值:

使用@Value

value属性:

String类型的,表示简单类型的属性值

value属性可省略

位置:

1.在属性定义的上面

package com.bjpowernode.ba01;

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

@Component("myStudent")
public class Student {
    @Value(value = "张三")
    private String name;
    @Value("25")
    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 +
                '}';
    }
}

2.在set方法的上面

package com.bjpowernode.ba01;

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

@Component(value = "myStudent")
public class Student {
    private String name;
    private int age;

    @Value(value = "张三")
    public void setName(String name) {
        this.name = name;
    }
    @Value(value = "25")
    public void setAge(int age) {
        this.age = age;
    }

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

引用属性的赋值:

使用@Autowired

有两种方式:byType和byName

位置:

1.在属性定义的上面

2.在set方法的上面

1.使用byType(默认就是byType)

1.创建School类并赋值:

package com.bjpowernode.ba01;

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

@Component(value = "mySchool")
public class School {
    @Value("北京大学")
    private String name;
    @Value("北京")
    private String address;

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

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

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

2.创建Student类并且给引用类型赋值:

package com.bjpowernode.ba01;

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 = "张三")
    private String name;
    @Value("25")
    private int age;
    @Autowired
    private School school;

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

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

    public void setSchool(School school) {
        this.school = school;
    }

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

3.创建测试类:

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{name=‘张三’, age=25, school=School{name=‘北京大学’, address=‘北京’}}

2. 使用使用byName

需要做的是:

1.在属性上面加入@Autowired

2.在属性上面加入@Qualifier(value = “bean的id”)

实现步骤:

1):创建School类:

package com.bjpowernode.ba01;

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

@Component(value = "mySchool")
public class School {
    @Value("北京大学")
    private String name;
    @Value("北京")
    private String address;

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

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

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

2)创建Student类:使用 @Qualifier(value = “mySchool”)

package com.bjpowernode.ba01;

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 = "张三")
    private String name;
    @Value("25")
    private int age;
    @Autowired
    @Qualifier(value = "mySchool")
    private School school;

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

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

    public void setSchool(School school) {
        this.school = school;
    }

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

3)创建测试类:

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{name=‘张三’, age=25, school=School{name=‘北京大学’, address=‘北京’}}

@Autowired的required属性

在上述使用byType给引用属性赋值的代码基础上:

如果**@Qualifier(value = “指定的bean的ID不存在”)** 的话,程序会报错并且停止运行,也就是下面的情况

   @Autowired
   @Qualifier(value = "我不存在哦!")
   private School school;

如何解决保存程序会执行结束的问题呢?

使用@Autowired的required属性:

默认和不写为true,表示引用类型赋值失败,程序报错并且终止运行

将其设置为false后,引用类型如果赋值失败,程序正常执行,引用类型的值为null

那么Autowired的required属性用true还是false呢?

使用true:能将问题爆露出来并能够及时得到解决

8.使用JDK中的注解给引用类型的属性赋值:

@Resource:来自jdk中的注解,spring框架中提供了对这个注解功能的支持,可以使用它给引用类型赋值

使用的也是自动注入原理,支持byName,byType,默认是byName

位置:

1.在属性的定义的上面,无需set方法,推荐使用

2.在set方法的上面

使用@Resource注解的,默认是Byname,如果byName找不到需要的bean,则自动使用byType

School类:

package com.bjpowernode.ba01;

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

@Component(value = "mySchool")
public class School {
    @Value("北京大学")
    private String name;
    @Value("北京")
    private String address;

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

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

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

Student类:

package com.bjpowernode.ba01;

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;

import javax.annotation.Resource;

@Component("myStudent")
public class Student {
    @Value(value = "张三")
    private String name;
    @Value("25")
    private int age;
    @Resource
    private School school;

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

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

    public void setSchool(School school) {
        this.school = school;
    }

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

测试类:

package com.bjpowernode;

import com.bjpowernode.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);
    }

}

结果仍会正常执行并赋值,因为使用@Resource注解的,默认是Byname,如果byName找不到需要的bean,则自动使用byType

如果只想使用byName方式:

@Resource(name = "mySchool")
private School school;

9.XML和注解的方式怎么选择呢?

注解的缺点:

1、很多朋友比如在使用spring注解时,会发现注解分散到很多类中,不好管理和维护;这个其实要借助工具,我目前使用的是IDEA,它在这方面表现的非常好;当然现在还有Spring的STS,也是不错的; 所以借助工具,能解决这个问题;

2、注解的开启/关闭必须修改源代码,因为注解是源代码绑定的,如果要修改,需要改源码,这个有这个问题,所以如果是这种情况,还是使用XML配置方式;比如数据源;

3、注解还一个缺点就是灵活性,在实现复杂的逻辑上,没有XML来的更加强大;注解就是要么用,要么不用,比如之前的jpa bean validation,要么全,要么没;遇到这种情况很痛苦;

4、还一种就是约定大于配置,但是在处理一些复杂的情况下,注解还是需要的(如Spring的数据验证/数据绑定注解很强大);

5、通用配置还是走XML吧,比如事务配置,比如数据库连接池等等,即通用的配置集中化,而不是分散化,如很多人使用@Transactional来配置事务,在很多情况下这是一种太分散化的配置;

6、XML方式比注解的可扩展性和复杂性维护上好的多,比如需要哪些组件,不需要哪些;在面对这种情况,注解扫描机制比较逊色,因为规则很难去写或根本不可能写出来;

注解的好处:

1、XML配置起来有时候冗长,此时注解可能是更好的选择,如jpa的实体映射;注解在处理一些不变的元数据时有时候比XML方便的多,比如springmvc的数据绑定,如果用xml写的代码会多的多;

2、注解最大的好处就是简化了XML配置;其实大部分注解一定确定后很少会改变,所以在一些中小项目中使用注解反而提供了开发效率,所以没必要一头走到黑;

3、注解相对于XML的另一个好处是类型安全的,XML只能在运行期才能发现问题。

10. 读取配置文件:

1)首先在resorurce文件夹下创建配置文件:

test.properties

myname=张三
myage=20
myschool=北京大学
myaddress=北京

2)在spring的主配置文件中加入context:property-placeholder标签:

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

    <context:component-scan base-package="com.bjpowernode.ba01"/>
    <context:property-placeholder location="classpath:test.properties"/>
</beans>

3)在Student类和School类中使用:

//Student类
package com.bjpowernode.ba01;

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;

import javax.annotation.Resource;

@Component("myStudent")
public class Student {
    @Value(value = "${myname}")
    private String name;
    @Value("${myage}")
    private int age;
    @Resource(name = "mySchool")
    private School school;


    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", school=" + school +
                '}';
    }
}
//School类
package com.bjpowernode.ba01;

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

@Component(value = "mySchool")
public class School {
    @Value("${myschool}")
    private String name;
    @Value("${myaddress}")
    private String address;

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

4)测试类:

package com.bjpowernode;

import com.bjpowernode.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{name=‘张三’, age=25, school=School{name=‘北京大学’, address=‘北京’}}

第三章:AOP面向切面编程

1. 什么是AOP:

简单了解AOP实现步骤

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

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

AOP(Aspect Oriented Programming)面向切面编程

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

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

2. 动态代理的作用:

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

2)减少代码的重复

3)专注业务逻辑代码

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

3. 怎样理解切面编程?

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

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

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

4. 术语:

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

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

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

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

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

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

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

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

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

6. AOP的实现

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

AOP的实现框架:

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

Spring主要在事务处理时使用AOP,项目中很少使用Spring的AOP实现,因为Spring的AOP比较笨重

2.Aspect J:一个开源的专门做AOP的框架,Spring框架中集成了Aspect框架

7. 学习使用Aspect J框架

7.2 切面的执行时间

切面的执行时间,这个执行时间在规范中叫做Advice(通知,增强 的意思)

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

1)@Before

2)@AfterReturning

3)@Around

4)@AfterThrowing

5)@After

7.2 AspectJ表达式:

在这里插入图片描述

在这里插入图片描述

举例:

1.匹配所有目标类的public方法
execution(public * (…))
2.匹配所有以To为后缀的方法
execution( To(…))
3.匹配Waiter接口中的所有方法
execution( com.aop.learn.service.Writer.(…))
4.匹配Waiter接口中及其实现类的方法
execution( com.aop.learn.service.Writer+.(…))
5.匹配 com.aop.learn.service 包下所有类的所有方法
execution( com.aop.learn.service.(…))
6.匹配 com.aop.learn.service 包,子孙包下所有类的所有方法
execution( com.aop.learn.service…(…))
7.匹配 包名前缀为com的任何包下类名后缀为ive的方法,方法必须以Smart为前缀
execution( com….ive.Smart(…))
8.匹配 save(String name,int age) 函数
execution( save(String,int))
9.匹配 save(String name,) 函数 第二个参数为任意类型
execution( save(String,))
10.匹配 save(String name,…) 函数 除第一个参数固定外,接受后面有任意个入参且入参类型不限
execution( save(String,…))
11.匹配 save(String+) 函数 String+ 表示入参类型是String的子类
execution(* save(String+))

7.3 使用步骤:

1.新建maven项目

2.加入依赖

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

<!-- aspectj依赖 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.2.5.RELEASE</version>
</dependency>

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

3.创建目标类:接口和它的实现类,

要做的是给类中的方法增加功能

//接口
package com.bjpowernode.service;

public interface SomeService {
    void doSome(String name,Integer age);
}
//实现类
package com.bjpowernode.service;

public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome(String name, Integer age) {
        System.out.println("成功执行doSome()方法");
    }
}

4.创建切面类:普通类

1)在类的上面加入@Aspect

2)在类中定义方法,方法就是切面要执行的功能代码 在方法的上面加入aspectj的通知注解,例如@Before 有需要指定切入点表达式execution()

package com.bjpowernode.service;

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

import java.util.Date;

@Aspect
public class MyAspect {

    @Before(value =
            "execution(public void com.bjpowernode.service.SomeServiceImpl.doSome(String,Integer))")
    public void myBefore(){
        System.out.println("在执行doSome()方法之前打印出当前时间:"+new Date());
    }
}

5.创建spring的配置文件:声明对象,把对象交给容器统一管理

1)声明目标对象

2)声明切面类对象

3)声明aspectj框架中的自动代理生成器标签。

​ 自动代理生成器:用来完成代理对象的自动创建功能的。

<?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: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/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="someService" class="com.bjpowernode.service.SomeServiceImpl"/>
    <bean id="myAspect" class="com.bjpowernode.service.MyAspect"/>

    <aop:aspectj-autoproxy/>
</beans>

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

public class MyTest {

    @Test
    public void test(){
        String config = "applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        SomeService someService = (SomeService) ctx.getBean("someService");
        someService.doSome("张三",20);
    }
}

在执行doSome()方法之前打印出当前时间:Fri Jul 03 15:43:09 CST 2020
成功执行doSome()方法

8. AspectJ的注解

@Before

[上述7.3使用步骤](###7.3 使用步骤:)

@JoinPoint

如果由此参数,则必须放到第一位

在上述中的代码基础上,可以显示方法的信息:

package com.bjpowernode.service;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;

import java.util.Date;

@Aspect
public class MyAspect {

    @Before(value =
            "execution(public void com.bjpowernode.service.SomeServiceImpl.doSome(String,Integer))")
    public void myBefore(JoinPoint jp){
        System.out.println(jp.getSignature());
        System.out.println(jp.toString());
        System.out.println("在执行doSome()方法之前打印出当前时间:"+new Date());
    }
}

void com.bjpowernode.service.SomeService.doSome(String,Integer)
execution(void com.bjpowernode.service.SomeService.doSome(String,Integer))
在执行doSome()方法之前打印出当前时间:Fri Jul 03 16:47:33 CST 2020
成功执行doSome()方法

@AfterReturning

属性:

1.value:切入点表达式

returning:自定义的变量,表示目标方法的返回值的。

自定义变量名必须和通知方法的形参名一样

位置: 在方法定义的上面

特点:

1.在目标方法之后执行的

2.能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能

3.可以修改这个返回值

接口的实现类:

package com.bjpowernode.service;

public class SomeServiceImpl implements SomeService {
    @Override
    public String doSome(String name, Integer age) {
        System.out.println("成功执行doSome()方法");
        return "abc";
    }
}

切面类:

package com.bjpowernode.service;

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

@Aspect
public class MyAspect {

    @AfterReturning(
            value = "execution(public String com.bjpowernode.service.SomeServiceImpl.doSome(String,Integer))",
            returning = "res"
    )
    public void myBefore(Object res){
        //这里修改了res的值
        res = "你好";
        System.out.println("后置通知:在目标方法之后执行的,获取的返回值是"+res);
    }
}

测试类:

package com.bjpowernode;

import com.bjpowernode.service.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {

    @Test
    public void test(){
        String config = "applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        SomeService someService = (SomeService) ctx.getBean("someService");
        String str = someService.doSome("张三",20);
        System.out.println("str==="+str);
    }
}

成功执行doSome()方法
后置通知:在目标方法之后执行的,获取的返回值是你好
str===abc

可以得出:如果在切面类中修改了返回值res的值,不影响结果

@Around

属性: value 切入点表达式

位置: 在方法的定义上面

环绕通知方法定义格式:

1.public

2.必须有一个返回值,推荐使用Object

3.方法名称自定义

4.方法有参数,固定的参数 ProceedingJoinPoint

特点:

1.@Around是功能最强的通知

2.在目标方法的前和后都能增强功能

3.控制目标方法是否被调用执行

4.修改原来的目标方法的执行结果。 影响最后的调用结果

环绕通知,等同于jdk动态代理的,InvocationHandler接口

参数:

ProceedingJoinPoint 等同于 Method

作用: 执行目标方法的

返回值: 就是目标方法的执行结果,可以被修改

接口实现类:

package com.bjpowernode.service;

public class SomeServiceImpl implements SomeService {
    @Override
    public String doSome(String name, Integer age) {
        System.out.println("成功执行doSome()方法");
        return "abc";
    }
}

切面类:

package com.bjpowernode.service;

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

@Aspect
public class MyAspect {

    @Around(
            value = "execution(public String com.bjpowernode.service.SomeServiceImpl.doSome(String,Integer))"
    )
    public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
        Object result = null;
        System.out.println("我是前置功能");
        //目标方法调用
        result = pjp.proceed();//相当于method.invoke(); Object result = doFirst();
        System.out.println("我是后置功能");
        result = "你好";
        return result;
    }
}

测试类:

package com.bjpowernode;

import com.bjpowernode.service.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {

    @Test
    public void test(){
        String config = "applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        SomeService someService = (SomeService) ctx.getBean("someService");
        String str = someService.doSome("张三",20);
        System.out.println("str==="+str);
    }
}

我是前置功能
成功执行doSome()方法
我是后置功能
str===你好

执行的结果被使用@Around的切面类改变了

可以得出:如果在切面类中修改了返回值res的值,影响结果

@After

最终方法通知定义格式:

1.public

2.没有返回值

3.方法名称的定义

4.方法没有参数,如果还有是JoinPoint

属性:

value 切入点表达式

位置:在方法的上面

特点: 总是会执行,实在目标方法之后执行的

类似于:try…catch…finly

package com.bjpowernode.service;

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

@Aspect
public class MyAspect {

    @After(
            value = "execution(public String com.bjpowernode.service.SomeServiceImpl.doSome(String,Integer))"
    )
    public void myAround(){
        System.out.println("我是后置功能");
    }
}

@PointCut

定义和管理切入点,如果项目种有多个切入点表达式是重复的,可以服用的,可以使用@PointCut

属性:value 切入点表达式

位置:在自定义的方法上面

特点:

当使用@Pointcut定义在一个方法的上面,此时这个方法的名称就是切入点表达式的别名。

其他通知中,value属性就可以使用这个方法的名称代替切入点表达式了

package com.bjpowernode.service;

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

@Aspect
public class MyAspect {

    @After(value = "myPoint()")
    public void myAround(){
        System.out.println("我是后置功能");
    }

    @Pointcut(value = "execution(public String com.bjpowernode.service.SomeServiceImpl.doSome(String,Integer))")
    public void myPoint(){}
}

没有接口使用的是cglib动态代理(了解)

使用

<aop:aspectj-autoproxy proxy-target-class="true"/>

可以告诉框架要使用cglib代理,程序执行时效率较高一些

第四章:Spring集成Mybatis

用到的技术是IOC

使用步骤:

1)创建maven项目

2)加入maven的依赖

<!--单元测试-->
    <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.1</version>
    </dependency>
    <!--mybatis和spring集成的依赖-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.1</version>
    </dependency>
      <!--mysql驱动-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.9</version>
    </dependency>
    <!--阿里公司的数据库连接池-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.12</version>
    </dependency>
<resources>
    <resource>
        <directory>src/main/java</directory>
        <includes>
            <include>**/*.xml</include>
        </includes>
        <filtering>true</filtering>
    </resource>
</resources>

<plugins>
    <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
            <source>1.8</source>
            <target>1.8</target>
        </configuration>
    </plugin>
</plugins>

3)创建实体类

package com.bjpowernode.domain;

public class Student {
    private Integer id;
    private String name;
    private String email;
    private Integer age;

    public Student() {
    }

    public Student(Integer id, String name, String email, Integer age) {
        this.id = id;
        this.name = name;
        this.email = email;
        this.age = age;
    }
	//setter和getter...

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

4)创建dao接口和mapper文件

package com.bjpowernode.dao;

import com.bjpowernode.domain.Student;

import java.util.List;

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

<?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.bjpowernode.dao.StudentDao">

    <insert id="insertStudent">
        insert into student values(#{id},#{name},#{email},#{age})
    </insert>
    <select id="selectStudents" resultType="com.bjpowernode.domain.Student">
        select id,name,email,age from student order by desc
    </select>

</mapper>

5)创建mybatis主配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    <properties resource="jdbc.properties"/>
    <!--日志-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    <!--设置别名-->
    <typeAliases>
        <package name="com.bjpowernode.domain"/>
    </typeAliases>

    <mappers>
        <package name="com.bjpowernode.dao"/>
    </mappers>

</configuration>

6)创建service接口和实现类,属性是dao

//接口
package com.bjpowernode.service;

import com.bjpowernode.domain.Student;

import java.util.List;

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

//实现类
package com.bjpowernode.service.impl;

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

import java.util.List;

public class StudentServiceImpl implements StudentService {

    StudentDao studentDao = null;

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

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

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

7)创建spring的配置文件:声明mybatis的对象交给spring创建

1.数据源

2.SqlSessionFactory

3.Dao对象

4.声明自定义的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">
    <!--声明数据源DataSource,作用是连接数据库的-->
    <bean id="myDatasource" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <property name="url" value="jdbc:mysql://localhost:3306/java" />
        <property name="username" value="root" />
        <property name="password" value="333" />
        <property name="maxActive" value="20" />
    </bean>
    <!--声明mybatis中提供的SqlSessionFactoryBean类,这个类的内部创建SqlSessionFactory的-->

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" >
        <!--set注入,把数据库连接池付给了dataSource属性-->
        <property name="dataSource" ref="myDatasource"/>
        <!--mybatis主配置文件的位置
            configLocation属性是Resource类型的,读取配置文件
            它的赋值,使用value,使用classpath:指定文件的位置
        -->
        <property name="configLocation" value="classpath:Mybatis.xml"/>
    </bean>

    <!--创建dao对喜爱给你没使用SqlSession的getMapper(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.bjpowernode.dao"/>
    </bean>
    
    <bean id="studentService" class="com.bjpowernode.service.impl.StudentServiceImpl">
        <property name="studentDao" ref="studentDao"/>
    </bean>
</beans>

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

 @Test
    public void Test04(){
        String config = "applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        StudentServiceImpl service = (StudentServiceImpl) ctx.getBean("studentService");
        List<Student> studentList = service.queryStudents();
        for (Student student : studentList) {
            System.out.println(student);
        }
    }

使用属性配置文件:

在spring主配置文件的beans标签中声明属性配置文件的位置:

<context:property-placeholder location="classpath:jdbc.properties"/>

第五章:Spring事务

1.什么是事务:

事务是指一组SQL语句的集合,集合中有多条SQL语句,可能是insert,update,select,delete

我们希望这些多个SQL语句都能成功,或者都是失败,这些SQL语句的执行是一致的,作为一个整体执行

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

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

3.事务的处理:

不同数据库处理事务方式不足之处:

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

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

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

如何解决不足:

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

4.处理事务需要怎么做:

4.1告诉Spring你使用的是那种数据库

4.2说明需要事务的类型:

1.事务的隔离级别:有四个值

名称结果脏读不可重复读幻读
Read UnCommitted(读未提交)什么都不解决
Read Committed(读提交)解决了脏读的问题
Repeatable Read(重复读)(mysql的默认级别)解决了不可重复读
Serializable(序列化)解决所有问题

READ UNCOMMITTED(读未提交数据): 允许事务读取未被其他事务提交的变更数据,会出现脏读、不可重复读和幻读问题。
READ COMMITTED(读已提交数据): 只允许事务读取已经被其他事务提交的变更数据,可避免脏读,仍会出现不可重复读和幻读问题。
REPEATABLE READ(可重复读): 确保事务可以多次从一个字段中读取相同的值,在此事务持续期间,禁止其他事务对此字段的更新,可以避免脏读和不可重复读,仍会出现幻读问题。
SERIALIZABLE(序列化): 确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作,可避免所有并发问题,但性能非常低。

2.事务的超时时间:

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

3.事务的传播行为:

所谓事务传播行为是指,处于不同事务中的方法在相互调用时,执行期间事务的维护情 况。如,A 事务中的方法 doSome()调用 B 事务中的方法 doOther(),在调用执行期间事务的 维护情况,就称为事务传播行为。事务传播行为是加在方法上的。

PROPAGATION_REQUIRED
PROPAGATION_REQUIRES_NEW
PROPAGATION_SUPPORTS

以上三个需要掌握

PROPAGATION_MANDATORY
PROPAGATION_NESTED
PROPAGATION_NEVER
PROPAGATION_NOT_SUPPORTED

1) PROPAGATION_REQUIRED:
指定的方法必须在事务内执行。若当前存在事务,就加入到当前事务中;若当前没有事 务,则创建一个新事务。这种传播行为是最常见的选择,也是 Spring 默认的事务传播行为。 如该传播行为加在 doOther()方法上。若 doSome()方法在调用 doOther()方法时就是在事 务内运行的,则 doOther()方法的执行也加入到该事务内执行。若 doSome()方法在调用 doOther()方法时没有在事务内执行,则 doOther()方法会创建一个事务,并在其中执行。

2) PROPAGATION_SUPPORTS
指定的方法支持当前事务,但若当前没有事务,也可以以非事务方式执行。

3)PROPAGATION_REQUIRES_NEW
总是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务执行完毕

4.3事务提交事务,回滚事务的时机

1)当你的业务方法执行成功,没有异常抛出,之后Spring会提交事务

2)当你的业务方法运行时异常或ERROR,Spring会进行回滚

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

5. 举例:

1.创建两个数据表:

在这里插入图片描述

在这里插入图片描述

2.加入maven依赖

<!--单元测试-->
    <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.1</version>
    </dependency>
    <!--mybatis和spring集成的依赖-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.1</version>
    </dependency>
      <!--mysql驱动-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.9</version>
    </dependency>
    <!--阿里公司的数据库连接池-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.12</version>
    </dependency>
<resources>
    <resource>
        <directory>src/main/java</directory>
        <includes>
            <include>**/*.xml</include>
        </includes>
        <filtering>true</filtering>
    </resource>
</resources>

<plugins>
    <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
            <source>1.8</source>
            <target>1.8</target>
        </configuration>
    </plugin>
</plugins>

3.创建实体类Goods和Sale

Goods类:

package com.bjpowernode.domain;

public class Goods {
    private Integer id;
    private String name;
    private Integer amount;
    private Float price;

   //setter和getter
}

Sale类:

package com.bjpowernode.domain;

public class Sale {
    private Integer id;
    private Integer gid;
    private Integer nums;

    //setter和getter
}

4.创建dao和mapper

GoodsDao类及mapper文件

package com.bjpowernode.dao;

import com.bjpowernode.domain.Goods;

public interface GoodsDao {
    //更新库存
    int updateGoods(Goods goods);
    //查询商品的信息
    Goods selectGoods(Integer id);
}
<?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.bjpowernode.dao.GoodsDao">
    <select id="selectGoods" resultType="com.bjpowernode.domain.Goods">
        select id,name,amount,price from goods where id=#{gid}
    </select>
    <update id="updateGoods">
        update goods set amount = amount - #{amount} where id=#{id}
    </update>
</mapper>

SaleDao类及mapper文件

package com.bjpowernode.dao;

import com.bjpowernode.domain.Sale;

public interface SaleDao {
    //增加销售记录
    int insertSale(Sale sale);
}
<?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.bjpowernode.dao.SaleDao">
    <insert id="insertSale">
        insert into sale(gid,nums) values(#{gid},#{nums})
    </insert>
</mapper>x

5.Myabtis主配置文件

<?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>
    <!--设置别名-->
    <typeAliases>
        <package name="com.bjpowernode.domain"/>
    </typeAliases>

    <mappers>
        <package name="com.bjpowernode.dao"/>
    </mappers>

</configuration>

6.创建service接口以及实现类

//Service接口
package com.bjpowernode.service;

public interface BuyGoodService {
    void buy(Integer goodsId,Integer nums);
}
//实现类
package com.bjpowernode.service.impl;

import com.bjpowernode.dao.GoodsDao;
import com.bjpowernode.dao.SaleDao;
import com.bjpowernode.domain.Goods;
import com.bjpowernode.domain.Sale;
import com.bjpowernode.execp.NotEnoughException;
import com.bjpowernode.service.BuyGoodService;

public class BuyGoodServiceImpl implements BuyGoodService {

    private SaleDao saleDao;
    private GoodsDao goodsDao;

    @Override
    public void buy(Integer goodsId, Integer nums) {
        System.out.println("方法的开始");
        //记录销售信息
        Sale sale = new Sale();
        sale.setGid(goodsId);
        sale.setNums(nums);
        saleDao.insertSale(sale);
        //更新库存
        Goods goods = goodsDao.selectGoods(goodsId);
        if ( goods == null){
            throw new NullPointerException("编号是"+goodsId+"的商品不存在");
        }else if ( goods.getAmount() < nums ){
            //库存不足
            throw new NotEnoughException("编号是"+goodsId+"的商品库存不足");
        }
        Goods buyGoods = new Goods();
        buyGoods.setId(goodsId);
        buyGoods.setAmount(nums);
        goodsDao.updateGoods(buyGoods);
        System.out.println("方法的完成");
    }

    //Setter和Getter
}

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

    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!--声明数据源DataSource,作用是连接数据库的-->
    <bean id="myDatasource" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <property name="url" value="jdbc:mysql://localhost:3306/java" />
        <property name="username" value="root" />
        <property name="password" value="333" />
        <property name="maxActive" value="20" />
    </bean>
    <!--声明mybatis中提供的SqlSessionFactoryBean类,这个类的内部创建SqlSessionFactory的-->

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" >
        <!--set注入,把数据库连接池付给了dataSource属性-->
        <property name="dataSource" ref="myDatasource"/>
        <!--mybatis主配置文件的位置
            configLocation属性是Resource类型的,读取配置文件
            它的赋值,使用value,使用classpath:指定文件的位置
        -->
        <property name="configLocation" value="classpath:Mybatis.xml"/>
    </bean>

    <!--创建dao对喜爱给你没使用SqlSession的getMapper(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.bjpowernode.dao"/>
    </bean>
    
    <bean id="buyService" class="com.bjpowernode.service.impl.BuyGoodServiceImpl">
        <property name="goodsDao" ref="goodsDao"/>
        <property name="saleDao" ref="saleDao"/>
    </bean>
</beans>
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/java
jdbc.user=root
jdbc.pwd=333

8.创建测试类

package com.bjpowernode;

import com.bjpowernode.service.impl.BuyGoodServiceImpl;
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 ctx = new ClassPathXmlApplicationContext(config);
        BuyGoodServiceImpl service = (BuyGoodServiceImpl) ctx.getBean("buyService");
        service.buy(1001,10);
    }
}

如果购买的编号不存在或者是购买的数量超过最大值事务仍然会提交到购买记录表

看以下的Spring框架提供的处理方案

6.Spring框架提供的事务处理方案:

通过@Transactional注解方式,可将事务织入到相应的public方法中,实现事务管理。

在spring配置文件中指定数据源,告诉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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       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">

    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!--声明数据源DataSource,作用是连接数据库的-->
    <bean id="myDatasource" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <property name="url" value="jdbc:mysql://localhost:3306/java" />
        <property name="username" value="root" />
        <property name="password" value="333" />
        <property name="maxActive" value="20" />
    </bean>
    <!--声明mybatis中提供的SqlSessionFactoryBean类,这个类的内部创建SqlSessionFactory的-->

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" >
        <!--set注入,把数据库连接池付给了dataSource属性-->
        <property name="dataSource" ref="myDatasource"/>
        <!--mybatis主配置文件的位置
            configLocation属性是Resource类型的,读取配置文件
            它的赋值,使用value,使用classpath:指定文件的位置
        -->
        <property name="configLocation" value="classpath:Mybatis.xml"/>
    </bean>

    <!--创建dao对喜爱给你没使用SqlSession的getMapper(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.bjpowernode.dao"/>
    </bean>
    
    <bean id="buyService" class="com.bjpowernode.service.impl.BuyGoodServiceImpl">
        <property name="goodsDao" ref="goodsDao"/>
        <property name="saleDao" ref="saleDao"/>
    </bean>
	<!--声明事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--指定数据源-->
        <property name="dataSource" ref="myDatasource"/>
    </bean>
		<!--开启事务注解驱动,告诉spring试着用注解管理事务,创建代理对象-->
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

接口实现类:使用@Transactional注解

package com.bjpowernode.service.impl;

import com.bjpowernode.dao.GoodsDao;
import com.bjpowernode.dao.SaleDao;
import com.bjpowernode.domain.Goods;
import com.bjpowernode.domain.Sale;
import com.bjpowernode.execp.NotEnoughException;
import com.bjpowernode.service.BuyGoodService;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

public class BuyGoodServiceImpl implements BuyGoodService {

    private SaleDao saleDao;
    private GoodsDao goodsDao;
	//这里直接使用@Transactional也可以,默认值就是
    @Transactional(
            propagation = Propagation.REQUIRED,
            isolation = Isolation.DEFAULT,
            readOnly = false,
            rollbackFor = {
                    NullPointerException.class,NotEnoughException.class
            }
    )
    @Override
    public void buy(Integer goodsId, Integer nums) {
        System.out.println("方法的开始");
        //记录销售信息
        Sale sale = new Sale();
        sale.setGid(goodsId);
        sale.setNums(nums);
        saleDao.insertSale(sale);
        //更新库存
        Goods goods = goodsDao.selectGoods(goodsId);
        if ( goods == null){
            throw new NullPointerException("编号是"+goodsId+"的商品不存在");
        }else if ( goods.getAmount() < nums ){
            //库存不足
            throw new NotEnoughException("编号是"+goodsId+"的商品库存不足");
        }
        Goods buyGoods = new Goods();
        buyGoods.setId(goodsId);
        buyGoods.setAmount(nums);
        goodsDao.updateGoods(buyGoods);
        System.out.println("方法的完成");
    }

   //setter&getter...


}

这样发生错误时,spring会自动回滚

7.aspectj提供的事务处理

首先加入依赖:

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

spring配置文件加入:

<!--事务管理器对象-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="myDatasource"/>
    </bean>
<!--声明业务方法它的事务类型(隔离行为,传播行为,超时时间)
    id:自定义名称,表示标签之间的配置内容
    transaction-manager:事务管理器对象的ID
-->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
    <!--tx:method:给具体的方法配置事务属性,method可以有多个,分别给不同的方法设置事务属性
        name:方法名称,不带有包和类,可以使用通配符,*表示任意字符
        propagation:传播行为,枚举值
        isolation:隔离级别
        no-rollback-for:指定的异常类名,全限定类名,发生异常一定回滚
    -->
    <tx:attributes>
        <tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
                   no-rollback-for="java.lang.NullPointerException,com.bjpowernode.execp.NotEnoughException"/>
    </tx:attributes>
</tx:advice>
<!--配置AOP-->
<aop:config>
    <!--配置切入点表达式:指定哪些包中得嘞,要使用事务
        id:切入点表达式的名称,为一只
        expression:切入点表达式,指定哪些类要使用事务,aspectj会创建代理对象
    -->
    <aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>

    <!--配置增强器:关联advice和pointcut-->
    <aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt"/>
</aop:config>
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值