本笔记为本人在B站学习时整理而成,为了更好的学习,将其整理成笔记,以防忘记相关知识点。

Spring概述

概述

Spring:出现在2002年左右,降低企业级开发难度。帮助进行模块之间、类与类之间的管理,帮助开发人员创建对象,管理对象之间的关系。
2003年传入国内,被大量使用。
2017出现新的流行框架SpringBoot,核心思想与Spring相同。
核心技术:IoC、AOP,能使模块之间、类之间解耦合。
依赖:class A使用class B的属性或方法,称之为class A依赖class B。
官网:https://spring.io/

优点

(1)轻量:Spring的所需要的jar包都非常小,一般1M以下,几百kb。核心功能所需要的jar包总共3M左右。Spring框架运行占有资源少,运行效率高,不依赖其他jar。
(2) 针对接口编程,解耦合
(3) AOP 编程的支持
(4) 方便集成各种优秀框架

(2)(3)(4)的优点将会在接下来学习中体会。

Spring 体系结构

Spring 由 20 多个模块组成,它们可以分为数据访问/集成(Data Access/Integration)、 Web、面向切面编程(AOP, Aspects)、提供JVM的代理 (Instrumentation)、消息发送(Messaging)、 核心容器(Core Container)和测试(Test)。

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

框架怎么学: 框架是一个软件,其它人写好的软件。
1)知道框架能做什么, mybatis能访问数据库, 对表中的数据执行增删改查。
2)框架的语法, 框架要完成一个功能,需要一定的步骤支持的,
3)框架的内部实现, 框架内部怎么做。 原理是什么。
4)通过学习,可以实现一个框架。

IoC控制反转

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

Ioc 的实现
➢ 依赖查找:DL( Dependency Lookup ),容器提供回调接口和上下文环境给组件。
➢ 依赖注入:DI(Dependency Injection),程序代码不做定位查询,这些工作由容器自行完成。

Spring 框架使用依赖注入(DI)实现 IoC。

Spring 容器是一个超级大工厂,负责创建、管理所有的 Java 对象,这些 Java 对象被称为 Bean。

Spring 容器管理着容器中 Bean 之间的依赖关系,Spring 使用“依赖注入”的方式 来管理 Bean 之间的依赖关系。使用 IoC 实现对象之间的解耦合。

控制反转的理解

控制反转, 是一个理论,概念,思想。

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

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

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

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

底层实现是反射机制。

Spring的第一个程序

实现步骤:

(1)添加module,新建maven工程,选择maven-archetype-quickstart骨架模板

(2)填写工程坐标:

GroupId:com.kwxy
ArtifactId:ch01-hello-spring
Version:1.0-SNAPSHOT

(3)Module name设置为:ch01-hello-spring,点击finish,等待maven项目的构建,控制台显示BUILD SUCCESS字样,则构建成功

(4) 将src/mainsrc/test下的java目录右键Mark Directory as-->Sources Root

(5)在src/main下新建resources目录,右键Mark Directory as-->Resources Root

(6)删除默认创建的APP类文件 ,这些文件分别在main和test目录中

(7)删除pom.xml文件中无关的配置,将编译和运行jdk版本改为1.8

(8)在build标签中添加’指定资源位置’的配置,配置大致如下:

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.kwxy</groupId><artifactId>ch01-hello-spring</artifactId><version>1.0-SNAPSHOT</version><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencies><!--单元测试--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency></dependencies><build><resources><resource><directory>src/main/java</directory><!--所在的目录--><includes><!--包括目录下的.properties,.xml 文件都会扫描到--><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>false</filtering></resource></resources></build>
</project>

(9)添加Spring依赖(加完最好右键pom.xml->Maven->Reimport

<!--Spring的依赖-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.4.RELEASE</version>
</dependency>
<!--
添加spring-context依赖后,会自动关联以下jar
spring-aop
spring-beans
spring-core
spring-expression
-->

(10)创建业务接口和实现类

接口:

package com.kwxy.service;public interface SomeService {void doSome();
}

实现类:

package com.kwxy.service.impl;import com.kwxy.service.SomeService;public class SomeServiceImpl implements SomeService {/*** spring默认调用无参数构造方法创建对象。* 如果没有无参数构造方法,报错:No default constructor found*/public SomeServiceImpl() {System.out.println("SomeServiceImpl的无参数构造方法");}@Overridepublic void doSome() {System.out.println("执行了SomeServiceImpl的doSome()方法");}
}

(11)创建Spring配置文件

如同在Servlet中我们需要在web.xml中注册我们希望服务器自动创建管理的servlet对象一样,在Spring中也需要有类似的配置,来自动创建刚才的SomeServiceImpl对象。

在 src/main/resources/目录现创建一个 xml 文件,文件名可以随意,但 Spring 建议的名称为 applicationContext.xml

IDEA已经为我们设计好了Spring配置文件的模板:右击resources–>new–>XML configuration file–>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"></beans>

注意,Spring 配置文件中使用的约束文件为 xsd 文件。作用与Mybatis的sql映射文件的dtd约束文件类似,但xsd约束作用更强:
1)定义一个 xml 文档中都有什么元素
2)定义一个 xml 文档中都有什么属性
3)定义一个 xml 文档中元素可以有哪些子元素,以及元素的顺序
4)定义一个 xml 文档中元素和属性的数据类型

<beans/>是配置文件的根标签。在Spring中,java对象称之为bean。在这个标签下进行java对象的注册:

<bean id="someService" class="com.kwxy.service.SomeServiceImpl" scope="prototype"/>

1.声明java对象交给Spring创建和管理,这个步骤称之为声明bean

等同于:

SomeService someService = new com.kwxy.service.SomeServiceImpl();

然后将创建的对象是放入到Spring的容器(Map<id,对象>):

private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap(16);

factoryBeanObjectCache.put(“service”,someService);

一个bean标签声明一个对象。

2.<bean/>标签的属性:
class:类的全限定名称,不能是接口(Spring使用反射创建对象);

class可以是非自定义的对象,例如”java.util.Date“,依然可以被Spring创建对象

id:自定义的对象名称,要求是唯一值。 表示在Spring中的对象名称,通过这个名称可以从Spring中找到对象,获取对象。
scope:指定bean对象的作用域(对象的存在范围和可见性)。

scope的可取值为:

1)单例singleton , 默认值,表示叫这个名称的对象在spring容器中只有一个

2)原型prototype , 表示每次使用getBean()都创建一个新的对象

(12)新建测试类,在测试类中创建测试方法

public class MyTest {@Testpublic void test01(){//定义Spring的配置文件, 配置文件是在类路径的根目录之下String config = "applicationContext.xml";//创建Spring的容器对象.根据Spring配置文件的位置,使用接口的不同实现类//1.如果Spring的配置文件是在类路径(classpath),使用ClassPathXmlApplicationContext//2.如果Spring的配置文件是放在项目的根之下(与src、target同级目录),使用FileSystemXmlApplicationContext//创建Spring容器,会读取配置文件中的bean标签,并创建对应的对象。//ClassPathXmlApplicationContext:表示从类路径中加载spring的配置文件ApplicationContext ac = new ClassPathXmlApplicationContext(config);//从容器中获取对象 使用getBean("<bean>的id")SomeService service = (SomeService) ac.getBean("someService");//调用业务方法service.doSome();}/*** 获取spring容器中 java 对象的信息*/@Testpublic void test02(){String config="applicationContext.xml";ApplicationContext ac = new ClassPathXmlApplicationContext(config);//使用spring提供的方法, 获取容器中定义的对象的数量int nums  = ac.getBeanDefinitionCount();System.out.println("容器中定义的对象数量:"+nums);//容器中每个定义的对象的名称String names [] = ac.getBeanDefinitionNames();for(String name:names){System.out.println(name);}}
}

Bean的装配

默认装配方式

当我们创建ApplicationContext对象时,Spring会读取配置文件的<bean/>并执行对应类的无参构造器。在这些类的无参构造器中加一条输出语句可以验证以上结论。

ApplicationContext 容器,会在容器对象初始化时,将其中的所有对象一次性全部装配好。

以后代码中若要使用到这些对象,只需从内存中直接获取即可。执行效率较高。但占用内存。

容器中Bean的作用域

当通过 Spring 容器创建一个 Bean 实例时,不仅可以完成 Bean 的实例化,还可以通过 scope 属性,为 Bean 指定特定的作用域。Spring 支持多种作用域。
(1)singleton:单例模式。即在整个 Spring 容器中,使用 singleton 定义的 Bean 将是单例的,叫这个名称的对象只有一个实例。默认为单例的。

(2)prototype:原型模式。即每次使用 getBean 方法获取的同一个的实例都是一个新的实例。

(3)request:对于每次 HTTP 请求,都将会产生一个不同的 Bean 实例。

(4)session:对于每个不同的 HTTP session,都将产生一个不同的 Bean 实例。

注意:

对于 scope 的值 request、session 只有在 Web 应用中使用 Spring 时,该作用域才有效。

对于 scope 为 singleton 的单例模式,该 Bean 是在容器被创建时即被装配好了;

对于 scope 为 prototype的原型模式,Bean 实例是在代码中使用该 Bean 实例时才进行装配的。

我们只是自动创建了对象,没有给对象的属性赋值。后面介绍两种给属性赋值的方法。

基于XML的DI

通过在xml配置文件对对象的属性进行赋值。

设值注入(掌握)

又称为set注入,通过调用类中属性的setter给属性赋值。

简单类型(java中的基本类型和String类型)

<bean/>标签下添加:<property name="属性名" value="简单类型的属性值" />

每一个property标签,完成一个属性的赋值。
注意:Spring执行property标签的原理,是执行name属性值对应的set方法。而并不关心set方法的具体实现和属性是否真的存在。

引用类型

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

例如:创建一个学校类:

package com.kwxy.domain;public class School {private  String name;private  String address;public void setName(String name) {this.name = name;}public void setAddress(String address) {this.address = address;}@Overridepublic String toString() {return "School{" +"name='" + name + '\'' +", address='" + address + '\'' +'}';}
}

在学生类中添加School类型的属性:

package com.kwxy.domain;public class Student {private String name;private int age;//引用类型private  School school;public Student() {System.out.println("Student的无参数构造方法");}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;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", school=" + school +'}';}
}

Spring配置文件中添加如下bean:

 <!--声明School--><bean id="mySchool" class="com.kwxy.domain.School"><property name="name" value="北京大学" /><property name="address" value="北京的海淀区" /></bean><!--ref作为属性--><bean id="myStudent" class="com.kwxy.domain.Student" ><property name="name" value="李四" /><property name="age" value="20" /><property name="school" ref="mySchool" /> <!--setSchool(mySchool) --></bean><!--ref作为子标签--><bean id="myStudent2" class="com.kwxy.domain.Student"><property name="name" value="张三" /><property name="age" value="22" /><property name="school"><ref bean="mySchool"/></property></bean>

构造注入(理解)

执行类的有参构造,在构造对象的同时给属性赋值。

1.给Student类添加有参构造器:

//定义有参数构造方法
public Student(String name, int age, School school) {System.out.println("Student有参数构造方法");this.name = name;this.age = age;this.school = school;}

2.对xml配置文件进行相关配置:

<!--使用name属性--><bean id="myStu" class="com.kwxy.domain.Student" ><constructor-arg name="age" value="22"/><constructor-arg name="name" value="张三" /><constructor-arg name="school" ref="mySch" /></bean><!--声明School--><bean id="mySch" class="com.kwxy.domain.School"><property name="name" value="江苏师范大学" /><property name="address" value="科文学院" /></bean>

<constructor-arg />标签中用于指定参数的属性有:

➢ name:指定参数名称,指的是构造方法中的形参。
➢ index:指明该参数对应着构造器的第几个参数,从 0 开始。不过,该属性不要也行,但要注意,若参数类型相同,或之间有包含关系,则需要保证赋值顺序要与构造器中的参数顺序一致。

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

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

引用类型的自动注入

对于引用类型属性的注入,也可不在配置文件中显示的注入。可以通过为<bean/>标签 设置 autowire 属性值,为引用类型属性进行隐式自动注入(默认是不自动注入引用类型属性)。

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

(1)byName:根据名称自动注入

(2)byType: 根据类型自动注入

byName 方式自动注入

当配置文件中被调用者 bean 的 id 值与代码中调用者 bean 类的属性名相同时,可使用 byName 方式,让容器自动将被调用者 bean 注入给调用者 bean。

容器是通过调用者的 bean 类的属性名与配置文件的被调用者 bean 的 id 进行比较而实现自动注入的。

例如,在Student类中,School类型的属性名是”school“,那么在xml配置文件中:

<bean id="mySchool" class="com.kwxy.domain.School" autowire="byName"><property name="name" value="北京大学" /><property name="address" value="北京的海淀区" />
</bean><bean id="myStudent" class="com.kwxy.domain.Student" ><property name="name" value="李四" /><property name="age" value="20" /><!--<property name="school" ref="mySchool" />-->
</bean>
byType 方式自动注入

使用 byType 方式自动注入,要求:配置文件中被调用者 bean 的 class 属性指定的类, 要与代码中调用者 bean 类的某引用类型属性类型同源。

即要么相同,要么有 is-a 关系(子 类,或是实现类)。但这样的同源的被调用 bean 只能有一个。多于一个,容器就不知该匹配哪一个了。

    <!--byType(按类型注入) : java类中引用类型的数据类型和spring容器中(配置文件)<bean>的class属性是同源关系的,这样的bean能够赋值给引用类型同源就是一类的意思:1.java类中引用类型的数据类型和bean的class的值是一样的。2.java类中引用类型的数据类型和bean的class的值父子类关系的。3.java类中引用类型的数据类型和bean的class的值接口和实现类关系的语法:<bean id="xx" class="yyy" autowire="byType">简单类型属性赋值</bean>注意:在byType中, 在xml配置文件中声明bean只能有一个符合条件的,多余一个是错误的--><!--byType自动注入--><bean id="myStudent" class="com.kwxy.domain.Student" autowire="byType"><property name="name" value="晶哥" /><property name="age" value="22"/></bean><!--声明School--><!--<bean id="mySchool" class="com.kwxy.domain.School"><property name="name" value="清华大学" /><property name="address" value="北京的海淀区" /></bean>--><!--声明School的子类对象--><bean id="xiaoXueSchool" class="com.kwxy.domain.XiaoXueSchool"><property name="name" value="中兴小学" /><property name="address" value="北京的大兴区"/></bean>

为应用指定多个 Spring 配置文件

在实际应用里,随着应用规模的增加,系统中 Bean 数量也大量增加,导致配置文件变 得非常庞大、臃肿。

为了避免这种情况的产生,提高配置文件的可读性与可维护性,可以将 Spring 配置文件分解成多个配置文件。

包含关系的配置文件: 多个配置文件中有一个总文件,总配置文件将各其它子文件通过<import/>引入。

在 Java 代码中只需要使用总配置文件对容器进行初始化即可。

例如:有spring-school.xml、spring-student.xml配置文件和一个total.xml总配置文件。

在total.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"><!--包含关系的配置文件:spring-total表示主配置文件 : 包含其他的配置文件的,主配置文件一般是不定义对象的。语法:<import resource="其他配置文件的路径" />关键字:"classpath:" 表示类路径(class文件所在的目录),在spring的配置文件中要指定其他文件的位置, 需要使用classpath,告诉spring到哪去加载读取文件。--><!--加载的是文件列表--><!--<import resource="classpath:ba06/spring-school.xml" /><import resource="classpath:ba06/spring-student.xml" />--><!--在包含关系的配置文件中,可以通配符(*:表示任意字符),使用通配符的话,配置文件必须放在一个目录中即这个配置文件要放在resources下的一个目录中。这个目录自己去创建,比如下面的ba06。注意: 主的配置文件名称不能包含在通配符的范围内(不能叫做spring-total.xml)--><import resource="classpath:ba06/spring-*.xml" />
</beans>

注意: 主的配置文件名称不能包含在通配符的范围内。

基于注解的DI

通过以下四个步骤实现DI:

(1)创建Maven项目,在pom.xml 加入 AOP 依赖

<dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.2.4.RELEASE</version>
</dependency>

如果你在pom.xml中添加了spring-context,那此依赖自动包含spring-aop,无需再次添加。

(2)需要更换配置文件头,加入 spring-context.xsd 约束

约 束 在 %SPRING_HOME%\docs\spring-framework-reference\html\xsd-configuration.html 文件中。

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

如果使用IDEA进行编辑,这一步骤可以省略,在执行第四步时根据提示自动添加即可。

(3)在类中添加注解

package com.kwxy.domain;import org.springframework.stereotype.Component;/*** @Component: 创建对象的, 等同于<bean>的功能*     属性:value 就是对象的名称,也就是bean的id值,*          value的值是唯一的,创建的对象在整个spring容器中就一个*     位置:在类的上面**  @Component(value = "myStudent")等同于*   <bean id="myStudent" class="com.kwxy.domain.Student" />**  spring中和@Component功能一致,创建对象的注解还有:*  1.@Repository(用在持久层类的上面) : 放在dao的实现类上面,*               表示创建dao对象,dao对象是能访问数据库的。*  2.@Service(用在业务层类的上面):放在service的实现类上面,*              创建service对象,service对象是做业务处理,可以有事务等功能的。*  3.@Controller(用在控制器的上面):放在控制器(处理器)类的上面,创建控制器对象的,*              控制器对象,能够接受用户提交的参数,显示请求的处理结果。*  以上三个注解的使用语法和@Component一样的。 都能创建对象,但是这三个注解还有额外的功能。*  @Repository,@Service,@Controller是给项目的对象分层的。*  默认是单例对象*/
@Component("myStudent")
public class Student {private String name;private int age;public Student(String name, int age, School school) {this.name = name;this.age = age;this.school = school;}//引用类型private  School school;public Student() {System.out.println("Student的无参数构造方法");}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;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", school=" + school +'}';}
}

(4)声明组件扫描器。在Spring配置文件的<beans/>标签下:

    <!--声明组件扫描器(component-scan),组件就是java对象base-package:指定注解在你的项目中的包名。component-scan工作方式: spring会扫描遍历base-package指定的包,把包中和子包中的所有类,找到类中的注解,按照注解的功能创建对象,或给属性赋值。加入了component-scan标签,配置文件的变化:1.加入一个新的约束文件spring-context.xsd2.给这个新的约束文件起个命名空间的名称
--><context:component-scan base-package="com.kwxy.domain" /><!--指定多个包的三种方式--><!--第一种方式:使用多次组件扫描器,指定不同的包--><context:component-scan base-package="com.kwxy.domain"/><context:component-scan base-package="com.kwxy.domain2"/><!--第二种方式:使用分隔符(;或,)分隔多个包名--><context:component-scan base-package="com.kwxy.domain;com.kwxy.domain2" /><!--第三种方式:指定父包--><context:component-scan base-package="com.kwxy" />

定义 Bean 的注解@Component(掌握)

@Component: 创建类的对象,等同于<bean />,默认是单例对象
属性: value 表示对象的名称(bean的id)
位置: 在类定义的上面,表示创建此类的对象。

例如:@Component(value = "myStudent")等价于<bean id="myStudent" class="com.kwxy.domain.Student"/>

另外,Spring 还提供了 3 个创建对象的注解:
➢ @Repository 用于对 DAO 实现类进行注解
➢ @Service 用于对 Service 实现类进行注解
➢ @Controller 用于对 Controller 实现类进行注解 这三个注解与@Component 都可以创建对象

但这三个注解还有其他的含义,@Service 创建业务层对象,业务层对象可以加入事务功能,@Controller 注解创建的对象可以作为处理器接收用户的请求。
@Repository,@Service,@Controller 是对@Component 注解的细化,标注不同层的对象。即持久层对象,业务层对象,控制层对象。

@Component 不指定 value 属性,bean 的 id 是类名的首字母小写。

若想使用这些注解形式的DI,必须在spring配置文件中声明组件扫描器

<context:component-scan base-package="注解类所在包名" />

简单类型属性注入@Value(掌握)

@Component("myStudent")
public class Student {/*** @Value: 简单类型的属性赋值*   属性: value 是String类型的,表示简单类型的属性值*   位置: 1.在属性定义的上面,无需set方法,推荐使用。*         2.在set方法的上面*/@Value("李晶")private String name;private int age;public void setName(String name) {this.name = name;}@Value("23")public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}
}

byType 自动注入@Autowired(掌握)

需要在引用属性上使用注解@Autowired,该注解默认使用按类型自动装配Bean的方式。

使用该注解完成属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加 到 setter 上。

@Autowired:spring框架提供的注解,实现引用类型的赋值,默认使用byType自动注入;

当有多个类型匹配时,使用要注入的对象变量名称作为 bean 的 id,在 spring 容器查找,找到了也可以注入成功。找不到就报错。

即Autowired默认先按byType,如果发现找到多个bean,则,又按照byName方式比对,如果还有多个name相同,则报出异常。

当不确定注入哪个bean的时候,可以联合使用注解@Autowired 与@Qualifier指定注入某个bean的id。

例如School类:

@Component("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;}@Overridepublic String toString() {return "School{" +"name='" + name + '\'' +", address='" + address + '\'' +'}';}
}

Student类:

@Component("myStudent")
public class Student {/*** @Value: 简单类型的属性赋值*   属性: value 是String类型的,表示简单类型的属性值*   位置: 1.在属性定义的上面,无需set方法,推荐使用。*         2.在set方法的上面*/@Value("李晶")private String name;@Value("23")private int age;/*** 引用类型* @Autowired: spring框架提供的注解,实现引用类型的赋值。* spring中通过注解给引用类型赋值,使用的是自动注入原理 ,支持byName, byType* @Autowired:默认使用的是byType自动注入。**  位置:1)在属性定义的上面,无需set方法, 推荐使用*       2)在set方法的上面*/@Autowiredprivate  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;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", school=" + school +'}';}
}

为了防止空指针异常的产生,推荐@AutoWired注解加在带参构造器上。具体原因参见:阿丙博客

byName 自动注入@Autowired 与@Qualifier(掌握)

需要在引用属性上联合使用注解@Autowired 与@Qualifier。@Qualifier 的 value 属性用 于指定要匹配的 Bean 的 id 值。类中无需 set 方法,也可加到 set 方法上。

例如:Student要自动注入School类,只需在school属性前添加注解:

@Autowired
@Qualifier("mySchool")//与School类的@component注解value值相同
private School school;

@Autowired 还有一个属性 required,默认值为 true,表示当匹配失败后,会终止程序运行。若将其值设置为 false,则匹配失败,将被忽略,未匹配的属性值为 null。

JDK 注解@Resource 自动注入(掌握)

Spring提供了对jdk中@Resource注解的支持。

@Resource注解既可以按名称匹配Bean, 也可以按类型匹配 Bean。

默认是按名称注入。使用该注解,要求 JDK 必须是 6 及以上版本。

@Resource 可在属性上,也可在 set 方法上。

本质就是用@Resource一个注解代替@Autowired和@Qualifier两个注解。

(1)byType 注入引用类型属性

@Resource 注解若不带任何参数,采用默认按名称的方式注入,按名称不能注入bean, 则会按照类型进行 Bean 的匹配注入。
如果Student要自动注入School类,只需在school属性前添加注解:

@Resource
private School school;

(2)byName 注入引用类型属性

@Resource 注解指定其 name 属性,则 name 的值即为按照名称进行匹配的 Bean 的 id。

如果Student要自动注入School类,只需在school属性前添加注解:

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

如果JDK是11版本的,学到@Resource注解的时候,加上以下依赖,否则会报错。因为高版本中移除了改模块。

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

当一个接口有多个实现类时

当一个接口有多个实现时,直接使用@Autowired可能会报异常,原因Autowired默认按照byType注入,找到了多个符合条件的bean,但是不知道注入哪一个,就会报异常。

方式1:

联合使用注解@Autowired 与@Qualifier。@Qualifier 的 value 属性用 于指定要匹配的 Bean 的 id 值。

方式2:

@Resource 注解指定其 name 属性,则 name 的值即为按照名称进行匹配的 Bean 的 id。

注解与 XML 的对比

注解优点是:

(1)方便

(2)直观

(3)高效(代码少,没有配置文件的书写那么复杂)

其弊端也显而易见:以硬编码的方式写入到 Java 代码中,修改是需要重新编译代码的

XML方式优点是:

(1)配置和代码是分离的

(2)在 xml 中做修改,无需编译代码,只需重启服务器即可将新的配置加载

xml 的缺点是:编写麻烦,效率低,大型项目过于复杂。

xml与注解方式还有一个区别在于,xml方式可以通过bean标签创建一个类的多个对象,但注解方式并不能,因为一个类不能存在两个同名的注解。

AOP面向切面编程

AOP概述

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

AOP 底层,就是采用动态代理模式实现的。

采用了两种代理:JDK 的动态代理,与 CGLIB 的动态代理,AOP可以看作动态代理的规范化与标准化

面向切面编程,就是将交叉业务逻辑封装成切面,利用 AOP 容器的功能将切面织入到 主业务逻辑中。

所谓交叉业务逻辑是指,通用的、与主业务逻辑无关的代码,如安全检查、 事务、日志、缓存等。

若不使用 AOP,则会出现代码纠缠,即交叉业务逻辑与主业务逻辑混合在一起。这样, 会使主业务逻辑变的混杂不清。

例如,转账,在真正转账业务逻辑前后,需要权限控制、日志记录、加载事务、结束事 务等交叉业务逻辑,而这些业务逻辑与主业务逻辑间并无直接关系。

但,它们的代码量所占比重能达到总代码量的一半甚至还多。它们的存在,不仅产生了大量的“冗余”代码,还大大干扰了主业务逻辑—转账。

AOP的相关术语

(1) 切面(Aspect)
切面泛指交叉业务逻辑。上例中的事务处理、日志处理就可以理解为切面。常用的切面 是通知(Advice)。实际就是对主业务逻辑的一种增强。
(2) 连接点(JoinPoint)
连接点指可以被切面织入的具体方法。通常业务接口中的方法均为连接点。
(3) 切入点(Pointcut)
切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。 被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。
(4) 目标对象(Target)
目标对象指将要被增强的对象。即包含主业务逻辑的类的对象。上例中的 StudentServiceImpl 的对象若被增强,则该类称为目标类,该类对象称为目标对象。当然,不被增强,也就无所谓目标不目标了。
(5) 通知(Advice)
通知是切面的一种实现,可以完成简单织入功能(织入功能就是在这里完成的)。Advice 也叫增强。上例中的 MyInvocationHandler 就可以理解为是一种通知。换个角度来说,通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。

切入点定义切入的位置,通知定义切入的时间。

AOP的实现

AOP的技术实现框架:

Spring:Spring的AOP实现较为笨重,一般用在事务处理。

aspectJ:一个开源,专门做AOP的框架,隶属于Eclipse基金会。

Spring框架集成了aspectJ的功能。

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

1)使用xml配置文件,一般用于配置全局事务;

2)使用注解。一般在项目开发中使用这种方式。

AspectJ 对 AOP 的实现

AspectJ 的通知类型

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

AspectJ 中常用的通知有五种类型,体现在五个不同的添加在切面的注解:
(1)前置通知 @Before
(2)后置通知 @AfterReturning
(3)环绕通知 @Around
(4)异常通知 @AfterThrowing
(5)最终通知 @After

AspectJ 的切入点表达式

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

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

execution ( [modifiers-pattern]  访问权限类型ret-type-pattern 返回值类型[declaring-type-pattern]  全限定性类名 name-pattern(param-pattern) 方法名(参数类型和参数个数) [throws-pattern]  抛出异常类型 )

切入点表达式要匹配的对象就是目标方法的方法名。所以,execution 表达式中明显就是方法的签名。注意,表达式中加[ ]的部分表示可省略部分,各部分间用空格分开。

在其中可以使用以下符号:
*:0至多个任意字符
..:用在方法参数中,表示任意多个参数;用在包名后,表示当前包及其子包
+:用在类名后,表示当前类及其子类;用在接口名后,表示当前接口及其实现类

例如:

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

上面表达的意思是返回值任意, 指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点

技巧:

execution(访问权限 方法返回值 方法声明(参数) 异常类型) 方法返回值和方法声明是必需的。

其实类似于声明一个方法: public void 方法名(参数) throws 异常

AspectJ 的开发环境

(1)引入AspectJ依赖:

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

(2)引入约束(第4、7、8行)

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

在IDEA中开发,可以省略这一步。在添加aop标签时会自动引入约束文件。

AspectJ 基于注解的 AOP 实现

AspectJ 基于注解的 AOP 实现步骤

(1)定义业务接口与实现类

package com.kwxy.service;public interface SomeService {void doSome(String name, int age);
}
package com.kwxy.service.impl;import com.kwxy.service.SomeService;public class SomeServiceImpl implements SomeService {@Overridepublic void doSome(String name, int age) {System.out.println("SomeSeviceImpl的业务方法doSome");}
}

(2)定义切面类

package com.kwxy.aspect;import org.aspectj.lang.annotation.Before;import java.util.Date;@Aspect
public class MyAspect {/*** @Before: 前置通知注解*   属性:value ,是切入点表达式,表示切面的功能执行的位置。*   位置:在方法的上面* 特点:*  1.在目标方法之前先执行的*  2.不会改变目标方法的执行结果*  3.不会影响目标方法的执行。*/@Before(value = "execution(public void com.kwxy.service.impl.SomeServiceImpl.doSome(..))")public void myBefore(){//就是你切面要执行的功能代码System.out.println("前置通知, 切面功能:在目标方法之前输出执行时间:"+ new Date());}
}

(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.kwxy.service.impl.SomeServiceImpl"/><!--声明切面类对象--><bean id="myAspect" class="com.kwxy.aspect.MyAspect"/><!--声明自动代理生成器:使用aspectj框架内部的功能,创建目标对象的代理对象。创建代理对象是在内存中实现的, 修改目标对象的内存中的结构。 创建为代理对象所以目标对象就是被修改后的代理对象.aspectj-autoproxy:会把spring容器中的所有的目标对象,一次性都生成代理对象。--><aop:aspectj-autoproxy/>
</beans>

<aop:aspectj-autoproxy/>通过扫描找到@Aspect 定义的切面类,再由切面类根据切入点找到目标类的目标方法,再由通知类型找到切入的时间点。

(4)创建测试方法测试,获取代理对象根据目标对象的id。

@Test
public void test01(){//定义Spring的配置文件, 配置文件是在类路径的根目录之下String config = "applicationContext.xml";ApplicationContext ac = new ClassPathXmlApplicationContext(config);//从容器中获取对象 使用getBean("<bean>的id")SomeService proxy = (SomeService) ac.getBean("someService");System.out.println(proxy.getClass().getName());//调用业务方法proxy.doSome("李晶",23);
}
/*输出结果
com.sun.proxy.$Proxy8
前置通知, 切面功能:在目标方法之前输出执行时间:Wed Jul 22 19:33:30 CST 2020
SomeSeviceImpl的业务方法doSome
*/
AspectJ 通知注解

通知注解:在切面类中修饰方法的注解,这些注解体现了通知类型。例如上面例子中@Before就是一个通知注解。通知注解修饰的方法称之为通知方法。
一共有五种通知类型,就对应了五种通知注解。下面一一介绍。

@Before前置通知-方法有JoinPoint参数

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

@Before(value = "execution(public void com.kwxy.service.impl.SomeServiceImpl.doSome(..))")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);}}/*
测试方法输出结果:
com.sun.proxy.$Proxy8
连接点的方法定义:void com.kwxy.service.SomeService.doSome(String,int)
连接点的方法名称:doSome
李晶
23
*/

不光前置通知的方法,可以包含一个 JoinPoint 类型参数,所有的通知方法均可包含该参数。

@AfterReturning 后置通知-注解有 returning 属性

在目标方法执行之后执行。由于是目标方法之后执行,所以可以获取到目标方法的返回值。该注解的 returning 属性就是用于指定接收方法返回值的变量名的。

所以,被注解为后 置通知的方法,除了可以包含 JoinPoint 参数外,还可以包含用于接收返回值的变量。该变 量最好为 Object 类型,因为目标方法的返回值可能是任何类型。

例如:在业务接口中定义一个有返回值的抽象方法:

String doOther(String name,int age);

再在业务类中实现:

@Override
public String doOther(String name, int age) {System.out.println("SomeSeviceImpl的业务方法doOther");return "abcd";
}

然后在切面类中定义一个切面:

@AfterReturning(value = "execution(public String com.kwxy.service.impl.SomeServiceImpl.doOther(..))", returning = "result")
public void myAfterReturning(JoinPoint jp, Object result) {//修改目标方法的返回值if (result != null) {String st = (String) result;result = st.toUpperCase();}System.out.println("后置通知,在目标方法之后执行的。能够获取到目标方法的执行结果:" + result);
}

测试方法:

@Test
public void test03(){//定义Spring的配置文件, 配置文件是在类路径的根目录之下String config = "applicationContext.xml";ApplicationContext ac = new ClassPathXmlApplicationContext(config);//从容器中获取对象 使用getBean("<bean>的id")SomeService proxy = (SomeService) ac.getBean("someService");System.out.println(proxy.getClass().getName());//调用业务方法String res = proxy.doOther("李晶", 23);System.out.println("测试方法中的结果:" + res);
}/*
测试方法输出结果:
com.sun.proxy.$Proxy8
SomeSeviceImpl的业务方法doOther
后置通知,在目标方法之后执行的。能够获取到目标方法的执行结果:ABCD
测试方法中的结果:abcd
*/
@Around 环绕通知-增强方法有 ProceedingJoinPoint 参数

在目标方法执行之前之后执行。
被注解为环绕增强的方法要有返回,Object 类型。并 且方法可以包含一个 ProceedingJoinPoint 类型的参数。

接口 ProceedingJoinPoint 继承于JoinPoint,因此可以根据它获取方法的信息。

其有一个 proceed()方法,用于执行目标方法。若目标方法有返回值,则该方法的返回值就是目标方法的返回值。

最后,环绕增强方法将其返回值返回。该增强方法实际是拦截了目标方法的执行。

接口增加方法:

String doFirst(String name,int age);

在实现类中实现该方法:

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

增加切面:

@Around(value = "execution(* *..SomeServiceImpl.doFirst(..))")public Object myAround(ProceedingJoinPoint pjp) throws Throwable {//ProceedingJoinPoint能获取连接点方法的定义,参数等信息String name = "";Object args[] = pjp.getArgs();if (args.length > 1) {name = (String) args[0];}Object result = null;System.out.println("环绕通知:在目标方法之前加入日志");//控制目标方法是否执行if ("zs".equals(name)) {//执行目标方法result = pjp.proceed(); //doFirst  result = method.invoke(target, args);}System.out.println("环绕通知:在目标方法之后加入事务处理");//返回目标方法的执行结果(可以是修改后的结果)//修改目标方法的执行结果if (result != null) {result = "Hello AspectJ";}return result;}

环绕通知能够控制目标方法是否执行,创建测试方法:

在上述代码中,如果name是zs,则执行,否则不执行。

@Test
public void test03(){//定义Spring的配置文件, 配置文件是在类路径的根目录之下String config = "applicationContext.xml";ApplicationContext ac = new ClassPathXmlApplicationContext(config);//从容器中获取对象 使用getBean("<bean>的id")SomeService proxy = (SomeService) ac.getBean("someService");System.out.println(proxy.getClass().getName());//调用业务方法String res = proxy.doFirst("李晶", 23);System.out.println("测试方法中的结果:" + res);
}
/*
测试结果:
com.sun.proxy.$Proxy8
环绕通知:在目标方法之前加入日志
环绕通知:在目标方法之后加入事务处理
测试方法中的结果:null
*/

将name改为zs后,运行结果为:

/*
com.sun.proxy.$Proxy8
环绕通知:在目标方法之前加入日志
SomeSeviceImpl的业务方法doFirst
环绕通知:在目标方法之后加入事务处理
测试方法中的结果:Hello AspectJ
*/
@AfterThrowing 异常通知-注解中有 throwing 属性

在目标方法抛出异常后执行。该注解的 throwing 属性用于指定所发生的异常类对象。

当然,被注解为异常通知的方法可以包含一个参数 Throwable,参数名称为 throwing 指定的名称,表示发生的异常对象。

在效果上相当于一个try…catch语句。目标方法的方法体在try语句块中,而切面方法的方法体放在了catch子句中。

@After最终通知

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

在执行效果上,相当于将切面方法的方法体放在了try…catch…finally…语句的finally子句中。

@Pointcut 定义切入点

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

其用法是,将@Pointcut 注解在一个方法之上,以后所有的 execution 的 value 属性值均 可使用该方法名作为切入点。

代表的就是@Pointcut 定义的切入点。这个使用@Pointcut 注解的方法一般使用 private 的标识方法,即没有实际作用的方法。 方法体内部也无需添加代码。

例如:

 @After(value = "mypt()")public void myAfter(){System.out.println("最终通知,总是会被执行的,可以做程序最后要做的工作,例如资源回收,内存释放");}/*** @Pointcut: 定义和管理切入点*     属性:value 切入点表达式*     位置:在自定义的方法上面。*      作用:@Pointcut定义在方法的上面, 这个方法的名称就是切入点的别名*     其他的通知注解的value属性可以使用方法名称,表示切入点。*/@Pointcut(value = "execution(* *..SomeServiceImpl.doThird(..))")private void mypt(){//无需代码}
设置AspectJ 实现 AOP的方式

在Spring配置文件中,通过<aop:aspectj-autoproxy/>的proxy-target-class属性设置选择通过JDK动态代理还是cglib动态代理实现AOP。

<!--声明自动代理生成器:使用aspectj把spring容器中目标类对象生成代理proxy-target-class="true"表示使用cglib动态代理目标类有接口,默认使用jdk动态代理。目标类没有接口,默认时候cglib动态代理目标类有接口,也可以使用cglib动态代理,需要设置proxy-target-class="true"
--><!-- <aop:aspectj-autoproxy proxy-target-class="true" />--><aop:aspectj-autoproxy/>

Spring集成myBatis

概述

Spring集成myBatis,其本质工作就是:将使用mybatis框架时用到的一些需要自己创建的对象,交由Spring统一管理。

把mybatis框架和spring集成在一起,像一个框架一样使用。

用的技术是:ioc 。

为什么是ioc:能把mybatis和spring集成在一起,像一个框架, 是因为ioc能创建对象。

可以把mybatis框架中的对象交给spring统一创建, 开发人员从spring中获取对象。

开发人员就不用同时面对两个或多个框架了, 就面对一个spring。

说明:

/*
mybatis使用步骤,对象
1.定义dao接口 ,StudentDao
2.定义mapper文件 StudentDao.xml
3.定义mybatis的主配置文件 mybatis-config.xml
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对象
Factory创建需要读取主配置文件我们会使用独立的连接池类替换mybatis默认自己带的, 把连接池类也交给spring创建。主配置文件:1.数据库信息<environment id="mydev"><transactionManager type="JDBC"/><dataSource type="POOLED"><!--数据库的驱动类名--><property name="driver" value="com.mysql.jdbc.Driver"/><!--连接数据库的url字符串--><property name="url" value="jdbc:mysql://localhost:3306/ssm"/><!--访问数据库的用户名--><property name="username" value="root"/><!--密码--><property name="password" value="root"/></dataSource>
2. mapper文件的位置<mappers><mapper resource="com/kwxy/dao/StudentDao.xml"/></mappers>
*/

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

(1)数据源dataSource。就是保存数据库连接信息的对象。在实际业务开发中,我们放弃使用Mybatis自带的数据库连接池,而采用阿里的Druid,更加高效;

(2)生成sqlSession对象的sqlSessionFactory;

(3)Dao接口的实现类对象。

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

Spring集成myBatis创建项目的流程

(1)新建 mysql数据库,准备数据。( student表)

建表语句

DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(80) DEFAULT NULL,`email` varchar(100) DEFAULT NULL,`age` int(11) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1003 DEFAULT CHARSET=utf8;

添加数据

INSERT INTO `student` VALUES ('1001', '李晶', 'lijing@163.com', '23');
INSERT INTO `student` VALUES ('1002', '李慧', 'lihui@qq.com', '25');
mysql> select *from student;
+------+--------+----------------+------+
| id   | name   | email          | age  |
+------+--------+----------------+------+
| 1001 | 李晶   | lijing@163.com |   23 |
| 1002 | 李慧   | lihui@qq.com   |   25 |
+------+--------+----------------+------+

(2)新建maven的module

(3)加入依赖

​ 1)spring依赖
​ 2)mybatis的依赖
​ 3)mybatis-spring依赖, 这个jar是从mybatis官网下载的, mybatis提供在spring中创建对象的类。
​ 4)mysql的驱动
​ 5)druid,数据库连接池的依赖

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.kwyx</groupId><artifactId>spring_mybatis</artifactId><version>1.0-SNAPSHOT</version><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencies><!--单元测试--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency><!--Spring的依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.4.RELEASE</version></dependency><!--spring的事务--><dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>5.2.4.RELEASE</version></dependency><!--spring访问数据库--><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.4.RELEASE</version></dependency><!--aspectj的依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.2.4.RELEASE</version></dependency><!--mybatis依赖--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.1</version></dependency><!--mybatis整合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>5.1.9</version></dependency><!--数据库连接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.12</version></dependency></dependencies><build><resources><resource><directory>src/main/java</directory><!--所在的目录--><includes><!--包括目录下的.properties,.xml 文件都会扫描到--><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>false</filtering></resource></resources></build>
</project>

(4)新建实体类Student

package com.kwyx.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;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}@Overridepublic String toString() {return "Student{" +"id=" + id +", name='" + name + '\'' +", email='" + email + '\'' +", age=" + age +'}';}
}

记得在pom.xml中添加资源插件!

(5)新建Dao接口和sql映射文件

package com.kwyx.dao;import com.kwyx.domain.Student;import java.util.List;public interface StudentDao {List<Student> selectAll();int insertStudent(Student student);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kwxy.dao.StudentDao"><select id="selectAll" resultType="com.kwxy.domain.Student">select id,name,email,age from student</select><insert id="insertStudent">insert into student values (#{id},#{name},#{email},#{age})</insert>
</mapper>

(6)新建mybatis主配置文件mybatis-config.xml

由于使用阿里的数据库连接池,所以不需要<environments/>标签

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!--settings:控制mybatis全局行为--><settings><!--设置mybatis输出日志--><setting name="logImpl" value="STDOUT_LOGGING"/></settings><!--设置别名--><typeAliases><!--name:实体类所在的包名--><package name="com.kwxy.domain"/></typeAliases><!-- sql mapper(sql映射文件)的位置--><mappers><!--name:是包名, 这个包中的所有mapper.xml一次都能加载--><package name="com.kwxy.dao"/></mappers>
</configuration>

(7)新建Service接口和实现类, 在实现类中有Dao的属性

在实际项目中,我们在对数据库前需要一些其他的业务代码,例如逻辑判断、身份认证等,这些放在Service中

package com.kwyx.service;import com.kwyx.domain.Student;import java.util.List;public interface StudentService {List<Student> selectAll();int insertStudent(Student student);
}
package com.kwyx.service.impl;import com.kwyx.service.StudentService;
import com.kwyx.dao.StudentDao;
import com.kwyx.domain.Student;import java.util.List;public class StudentServiceImpl implements StudentService {private StudentDao studentDao;//使用ioc,设值注入,在配置文件中给dao赋值public void setStudentDao(StudentDao studentDao) {this.studentDao = studentDao;}@Overridepublic List<Student> selectAll() {return studentDao.selectAll();}@Overridepublic int insertStudent(Student student) {return studentDao.insertStudent(student);}
}

(8)创建数据库连接配置文件jdbc.properties

jdbc.url=jdbc:mysql://localhost:3306/ssm
jdbc.user=root
jdbc.password=root
jdbc.maxActive=20

不需要配置数据库驱动

(9)新建Spring的配置文件applicationContext.xml(重要)

1)声明Druid的数据源DruidDataSource对象
2)声明SqlSessionFactoryBean,创建SqlSessionFactory对象
3)声明MyBatis的扫描器MapperScannerConfigurer,创建Dao接口的实现类对象
4)声明自定义的Service ,把3)中的Dao对象注入赋值给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"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"><!--读取jdbc配置文件location:指定属性配置文件的路径"classpath:":关键字表示类文件,也就是class文件所在的目录--><context:property-placeholder location="classpath:jdbc.properties"/><!--声明数据源DataSource--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><!--读取属性配置文件的key的值,使用 ${key}--><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.user}"/><property name="password" value="${jdbc.password}"/><property name="maxActive" value="${jdbc.maxActive}"/></bean><!--DruidDataSource myDataSource = new DruidDataSource();myDataSource.setUrl();myDataSource.setUsername();myDataSource.setPassword();myDataSource.init();--><!--声明SqlSessionFactoryBean,创建SqlSessionFactory对象--><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><!--数据源--><property name="dataSource" ref="dataSource"/><!--指定mybatis的主配置文件--><property name="configLocation" value="classpath:mybatis-config.xml"/></bean><!--声明MyBatis的扫描器,创建Dao接口的实现类对象--><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><!--指定SqlSessionFactory对象,能获取SqlSession--><property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/><!--指定Dao接口的包名,框架会把这个包中的所有接口一次创建出Dao对象--><property name="basePackage" value="com.kwxy.dao"/></bean><!--从spring中获取SqlSessionFacotory,因为spring是一个容器(Map)SqlSessionFactory factory  = map.get("sqlSessionFactory");SqlSession session = factory.openSession();for(接口:com.kwxy.dao){Dao对象 =  session.getMapper(接口)//把创建好的对象放入到spring容器中spring的Map.put( 接口名的首字母小写, Dao对象 )}创建好的dao对象放入到spring的容器中的。 dao对象的默认名称是 接口名首字母小写--><!--声明Service--><bean id="studentService" class="com.kwxy.service.impl.StudentServiceImpl"><property name="studentDao" ref="studentDao"/></bean>
</beans>

(10)新建测试类, 从spring容器中获取Service,调用Service的方法,完成数据库的操作

public class MyTest {@Testpublic void test01() {ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");StudentService studentService = (StudentService) ac.getBean("studentService");List<Student> students = studentService.selectAll();for (Student student : students) {System.out.println("学生 :" + student);}}/**结果* JDBC Connection [com.mysql.jdbc.JDBC4Connection@662b4c69] will not be managed by Spring* ==>  Preparing: select id,name,email,age from student* ==> Parameters:* <==    Columns: id, name, email, age* <==        Row: 1001, 李晶, lijing@163.com, 23* <==        Row: 1002, 李慧, lihui@qq.com, 25* <==      Total: 2* Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@53aad5d5]* 学生 :Student{id=1001, name='李晶', email='lijing@163.com', age=23}* 学生 :Student{id=1002, name='李慧', email='lihui@qq.com', age=25}*/@Testpublic void test02() {ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");StudentService studentService = (StudentService) ac.getBean("studentService");Student student = new Student(3, "张飞", "zhangfei@163.com", 24);int nums = studentService.insertStudent(student);System.out.println("添加了 " + nums + " 位学生");}/**结果* JDBC Connection [com.mysql.jdbc.JDBC4Connection@662b4c69] will not be managed by Spring* ==>  Preparing: insert into student values (?,?,?,?)* ==> Parameters: 3(Integer), 张飞(String), zhangfei@163.com(String), 24(Integer)* <==    Updates: 1* Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@76f2bbc1]* 添加了 1 位学生*/
}

Spring与事务

理论知识

(1)什么是事务

讲mysql的时候,提出了事务。 事务是指一组sql语句的集合, 集合中有多条sql语句,

可能是insert , update ,select ,delete, 我们希望这些多个sql语句都能成功,

或者都失败, 这些sql语句的执行是一致的,作为一个整体执行。

(2)在什么时候想到使用事务

当我的操作,涉及得到多个表,或者是多个sql语句的insert,update,delete。

需要保证这些语句都是成功才能完成我的功能,或者都失败,保证操作是符合要求的。

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

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

(3)通常使用JDBC访问数据库, 还是mybatis访问数据库怎么处理事务

jdbc访问数据库,处理事务 Connection conn ; conn.commit(); conn.rollback();
mybatis访问数据库,处理事务, SqlSession.commit(); SqlSession.rollback();
hibernate访问数据库,处理事务, Session.commit(); Session.rollback();

(4)上面事务的处理方式,有什么不足

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

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

处理事务需要多种方法

(5)怎么解决不足

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

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

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

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

事务管理器接口

事务管理器是 PlatformTransactionManager 接口对象。其主要用于完成事务的提交、回滚,及获取事务的状态信息。
PlatformTransactionManager 接口有两个常用的实现类:
➢ DataSourceTransactionManager:使用 JDBC 或 MyBatis 进行数据库操作时使用。
➢ HibernateTransactionManager:使用 Hibernate 进行持久化数据时使用。

Spring 的回滚方式

Spring 事务的默认回滚方式是:发生运行时异常和 error 时回滚,发生受查(编译)异常时提交。不过,对于受查异常,程序员也可以手工设置其回滚方式。

运行时异常,是 RuntimeException 类或其子类,即只有在运行时才出现的异常。

如, NullPointerException、ArrayIndexOutOfBoundsException、IllegalArgumentException 等均属于运 行时异常。

这些异常由 JVM 抛出,在编译时不要求必须处理(捕获或抛出)。

但,只要代码 编写足够仔细,程序足够健壮,运行时异常是可以避免的。

受查异常,也叫编译时异常,即在代码编写时要求必须捕获或抛出的异常,若不处理, 则无法通过编译。

如 SQLException,ClassNotFoundException,IOException 等都属于受查异常。 RuntimeException 及其子类以外的异常,均属于受查异常。

事务定义接口

事务定义接口TransactionDefinition中定义了事务描述相关的三类常量:事务隔离级别、 事务传播行为、事务默认超时时限,及对它们的操作。

(1)定义了五个事务隔离级别常量

这些常量均是以 ISOLATION_开头。即形如 ISOLATION_XXX。

➢ DEFAULT:采用 DB 默认的事务隔离级别。MySql 的默认为 REPEATABLE_READ; Oracle 默 认为 READ_COMMITTED。
➢ READ_UNCOMMITTED:读未提交。未解决任何并发问题。
➢ READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。
➢ REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读 。
➢ SERIALIZABLE:串行化。不存在并发问题。

(2)定义了七个事务传播行为常量

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

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

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

使用 Spring 的事务注解管理事务

(1)开启注解驱动,告诉Spring框架现在使用注解处理事务,在spring配置文件applicationContext中添加如下代码:

<!--开启注解驱动,开启事务支持,以tx开头,可以看出是用于事务的-->
<tx:annotation-driven transaction-manager="transactionManager"/>

(2)声明事务管理器

 <!--声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/>
</bean>

可视为固定写法,其中property标签的ref是配置文件中数据源对象的id属性值。

(3)业务层 public 方法加入事务注解

/**** rollbackFor:表示发生指定的异常一定回滚.*   处理逻辑是:*     1) spring框架会首先检查方法抛出的异常是不是在rollbackFor的属性值中*         如果异常在rollbackFor列表中,不管是什么类型的异常,一定回滚。*     2) 如果你的抛出的异常不在rollbackFor列表中,spring会判断异常是不是RuntimeException,*         如果是一定回滚。**//* @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,readOnly = false,rollbackFor = {NullPointerException.class})*///使用的是事务控制的默认值, 默认的传播行为是REQUIRED,默认的隔离级别DEFAULT//默认抛出运行时异常,回滚事务。@Transactional@Overridepublic int insertStudent(Student student) {return studentDao.insertStudent(student);}

@Transactional 的所有可选属性如下所示:

➢ propagation:用于设置事务传播属性。该属性类型为 Propagation 枚举,默认值为Propagation.REQUIRED。
➢ isolation:用于设置事务的隔离级别。该属性类型为 Isolation 枚举,默认值为 Isolation.DEFAULT。
➢ readOnly:用于设置该方法对数据库的操作是否是只读的。该属性为 boolean,默认值 为 false。
➢ timeout:用于设置本操作与数据库连接的超时时限。单位为秒,类型为 int,默认值为 -1,即没有时限。在实际业务开发中一般不设置
➢ rollbackFor:指定需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若只有 一个异常类时,可以不使用数组。
➢ rollbackForClassName:指定需要回滚的异常类类名。类型为 String[],默认值为空数组。 当然,若只有一个异常类时,可以不使用数组。
➢ noRollbackFor:指定不需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若 只有一个异常类时,可以不使用数组。
➢ noRollbackForClassName:指定不需要回滚的异常类类名。类型为 String[],默认值为空 数组。当然,若只有一个异常类时,可以不使用数组。

需要注意的是,@Transactional 若用在方法上,只能用于 public 方法上。

对于其他非 public 方法,如果加上了注解@Transactional,虽然 Spring 不会报错,

但不会将指定事务织入到该方法中。因为 Spring 会忽略掉所有非 public 方法上的@Transaction注解。

若@Transaction 注解在类上,则表示该类上所有的方法均将在执行时织入事务。

使用 AspectJ 的 AOP 配置管理事务

一般大型项目使用。在不更改源代码的条件下管理事务。代码和事务的配置完全是分离的,不需要在代码上加注解,全部是在xml中配置。

(1)添加Maven依赖

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

(2)声明事务管理器

 <!--声明事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/>
</bean>

(3)配置事务通知

为事务通知设置相关属性。用于指定要将事务以什么方式织入给哪些方法。

<!--声明业务方法它的事务属性(隔离级别,传播行为,超时时间)id:自定义名称,表示 <tx:advice> 和 </tx:advice>之间的配置内容的transaction-manager:事务管理器对象的id--><tx:advice id="buyAdvice" transaction-manager="transactionManager"><tx:attributes><!--tx:method:给具体的方法配置事务属性,method可以有多个,分别给不同的方法设置事务属性name:方法名称,1)完整的方法名称,不带有包和类。2)方法可以使用通配符,* 表示任意字符propagation:传播行为,枚举值isolation:隔离级别rollback-for:你指定的异常类名,全限定类名。 发生异常一定回滚--><tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"rollback-for="java.lang.NullPointerException"/><!--使用通配符,指定很多的方法--><tx:method name="add*" propagation="REQUIRES_NEW" /><!--指定修改方法--><tx:method name="modify*" /><!--删除方法--><tx:method name="remove*" /><!--查询方法,query,search,find--><tx:method name="*" propagation="SUPPORTS" read-only="true" /></tx:attributes></tx:advice>

(4)配置aop,配置增强器

指定哪些哪类要创建代理

<!--配置aop--><aop:config><!--配置切入点表达式:指定哪些包中类,要使用事务id:切入点表达式的名称,唯一值expression:切入点表达式,指定哪些类要使用事务,aspectj会创建代理对象--><aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/><!--配置增强器:关联adivce和pointcutadvice-ref:通知,上面tx:advice哪里的配置pointcut-ref:切入点表达式的id--><aop:advisor advice-ref="studentService" pointcut-ref="servicePt" /></aop:config>

Spring与Web

这一章主要介绍了一个核心知识点:解决不同Servlet中重复创建ApplicationContext对象,造成内存浪费的问题,即重复创建Spring容器。

解决这个问题的一个思路是,创建一个ServletContextListener,在ServletContext初始化的时候创建ApplicationContext对象,并将它保存在ServletContext中。

这样,在每个servlet中,只要调用当前servlet的ServletContext对象getAttribute方法就可以获取这个webapp中共享的一个ApplicationContext对象。

spring-web框架已经帮我们创建好了这样一个监听器。我们只需要在web.xml注册这个监听器就可以使用了。

(1)maven创建web模块,加入servlet,jsp依赖,拷贝之前spring和mybatis中所用的依赖

<!--以下是新增依赖,其它依赖到之前做的项目中拷贝-->
<!-- servlet依赖 -->
<dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope>
</dependency>
<!-- jsp依赖 -->
<dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.2.1-b03</version> <scope>provided</scope>
</dependency>
<!--spring-web依赖:有监听器-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>5.2.4.RELEASE</version>
</dependency>

(2)需要为监听器提供Spring的配置文件路径信息

<!--注册spring框架提供的监听器在监听器启动的时候,会寻找/WEB-INF/applicationContext.xml,为什么找这个文件?在监听器的初始方法中,会创建spring的容器对象, 在创建容器对象时,需要读取配置文件监听器默认是找/WEB-INF/applicationContext.xml。
-->
<!--自定义spring配置文件的位置和名称-->
<context-param><param-name>contextConfigLocation</param-name><param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

(3)在Sevlet中获取ApplicationContext对象

WebApplicationContext ctx = null;
String key = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
Object attr = getServletContext().getAttribute(key);
if( attr != null){ctx = (WebApplicationContext)attr;
}

webApplicationContext是ApplicationContext的子类,是在web项目中使用的Spring容器对象。

为了不使用框架给出的难记的key值获取webApplicationContext,这个框架还提供了一个工具类。

使用工具类获取webApplicationContext:

//获取ServletContext中的容器对象,spring提供了一个方法,获取容器对象
WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());

Spring学习笔记相关推荐

  1. 【Spring学习笔记-MVC-13.2】Spring MVC之多文件上传

    作者:ssslinppp       1. 摘要 前篇文章讲解了单文件上传<[Spring学习笔记-MVC-13]Spring MVC之文件上传>http://www.cnblogs.co ...

  2. Spring学习笔记(三) AOP_annotation,AOP_XML

    在学习课程以前,听说AOP有种很神秘的感觉,好像很好深的技术.其实原理很简单,使用动态代理的方式给程序增加逻辑.与此相似的有struts2中的filter拦截器. 再讲AOP之前先把需求说一下: 同S ...

  3. spring学习笔记06-spring整合junit(出现的问题,解决的思路)

    spring学习笔记06-spring整合junit(出现的问题,解决的思路) 文章目录 spring学习笔记06-spring整合junit(出现的问题,解决的思路) 3.1测试类中的问题和解决思路 ...

  4. spring学习笔记01-BeanFactory和ApplicationContext的区别

    spring学习笔记01-BeanFactory和ApplicationContext的区别 BeanFactory 和 ApplicationContext 的区别           BeanFa ...

  5. spring学习笔记02-spring-bean创建的细节问题

    spring学习笔记02-spring-bean创建的细节问题 三种创建Bean对象的方式 Bean的作用范围 Bean的生命周期 <?xml version="1.0" e ...

  6. spring学习笔记03-spring-DI-依赖注入详解(通过xml配置文件来配置依赖注入)

    spring学习笔记03-spring-DI-依赖注入详解 1.概念 2.构造函数注入 3.set方法注入 4.集合的注入 需要被注入的实体对象 package com.itheima.service ...

  7. Spring学习笔记:配置单数据源

    Spring学习笔记:配置单数据源 一.Spring Boot默认数据源类型 Springboot默认支持4种数据源类型,定义在 org.springframework.boot.autoconfig ...

  8. Spring学习笔记:第一个Spring Boot程序HelloWorld

    Spring学习笔记:第一个Spring Boot程序HelloWorld 一.跟着 Spring 了解技术趋势 1.看看 Spring 5.x 的改变暗示了什么 2.Spring Boot 和 Sp ...

  9. Spring学习笔记之MyBatis

    系列文章目录 Spring学习笔记 之 Springhttps://blog.csdn.net/weixin_43985478/article/details/124411746?spm=1001.2 ...

  10. 【Spring学习笔记 九】Spring声明式事务管理实现机制

    什么是事务?事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用,关乎数据准确性的地方我们一定要用到事务,防止业务逻辑出错. 什么是事务管理,事务管理对于企业应用而言至 ...

最新文章

  1. 学习笔记(六)——JavaScript(三)
  2. VTK:可视化之BackgroundColor
  3. 关系的三类完整性约束
  4. 电脑如何进行长截图?
  5. CF probabilities 自制题单
  6. 小型游戏《笑傲江湖之精忠报国》全过程_01
  7. 蔡学镛谈Java学习
  8. 碎碎念No.03 我这个程序员终于去看了心理医生做了心理咨询
  9. virtual box linux 安装增强功能,在linux系统中安装virtualbox增强功能(增强包)的详细步骤...
  10. 使用MATLAB工具箱TOOLBOX_calib标定摄像头过程(双目标定)
  11. 浙江大学14届计算机学院孙晓宇,郑州外国语学校2011年保送生录取名单
  12. 高效C++ Effective C++
  13. 一个链表L 一个链表P 包含升序排列的整数 操作PrintLots(L,P)将打印L中那些由P所指定的位置上的元素
  14. Firefox流失近5000万用户,世界第三大浏览器正在消亡
  15. 东莞东城用“智慧大脑”给城市“打补丁”
  16. iOS高级面试题及部分答案
  17. Android开发样式问题总结【持续更新】
  18. 安装jupyter步骤
  19. 怎么把HTML文件拉出来,怎么把网页HTML格式的文件
  20. React Native 实现chat 即时聊天(第1天)

热门文章

  1. Unity 资源包导入报错 “Substance engine failed to load“
  2. sqlserver去空格函数
  3. excel中如何解锁锁定单元格
  4. 求四边形最大内接矩形,一种不规则多边形的最大内接矩形的快速近似求解方法与流程...
  5. 虚函数表和虚函数指针
  6. Windows 10 让所有程序默认为“以管理员身份运行”并且取消“确认”按钮
  7. 计算机组成原理变形补码计算
  8. 怎么给win10进行分区?
  9. 微信开发者工具封装request请求
  10. 1 常用邮箱SMTP/POP3地址及端口