一、IOC介绍
控制反转(IoC,Inversion of Control)是一个概念,一种思想。它的目的是指导我们设计出更加松耦合的程序,更加有利于程序维护。指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。
控制与反转:
控制(Control):对象的创建
手动创建对象每次使用都需要 new,可能导致重复创建耗费资源且不方便切换实现类(耦合度高)。
反转(Inversion):对象的创建由开发者在类中手动 new 反转到由 Spring 容器创建
所有创建出来的实例都交给 Spring 统一管理,它是面向切面编程(AOP)的一个前提。
二、开发前准备
1.下载Apache Maven(MAC篇)
Windows & Linux 请参考:https://www.runoob.com/maven/maven-setup.html
官方下载地址:https://maven.apache.org/download.cgi
① 官网下载 Maven 到自定义位置并解压
解压后我们会得到一个这样的文件 ⬇️
② 解压后配置环境变量
打开终端输入命令 vim ~/.bash_profile
,然后增加以下内容 ,此处的maven安装路径改为自己的即可
export MAVEN_HOME=/Users/quokka/maven/apache-maven-3.8.6
export PATH=$PATH:$MAVEN_HOME/bin
③ 执行命令刷新环境变量
source ~/.bash_profile
④ 安装后的检查
mvn -version
⑤ .zshrc
的配置
如果每次重新打开终端都需要重新 source ~/.bash_profile
才能让环境变量生效,则需要在 .zshrc
中进行配置。
编辑.zshrc:
vim ~/.zshrc
添加命令:
source ~/.bash_profile
2.设置Maven仓库
实际开发中我们一般都使用自己下载的 Maven ,不使用 IDEA 工具自带的 Maven ,这就需要将我们上面下载的 Maven 配置到 IDEA 工具中。
1)在IDEA上设置
① 设置运行参数(不做该操作会导致 Maven 骨架生成速度慢到可怕😨)
将如下参数填入对应位置即可
-DarchetypeCatalog=internal
② 全局配置 Maven 基础信息
这里我们需要在自己的 Maven 安装目录同级下创建一个
repository
文件夹作为 Maven 仓库,所有我们需要的依赖都会存放在这个文件夹之中。
完成后依次点击【Apply】、【OK】即可。
2)本地Maven配置文件设置
打开本地 Maven 安装路径中如下路径文件:
apache-maven-3.8.6/conf/settings.xml
① 设置本地仓库路径到我们之前创建好的本地repository仓库路径位置
<localRepository>你的本地仓库路径</localRepository>
② 从国外仓库下载很慢,我们设置为国内镜像
<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
3.创建Maven项目
① 新建项目
② 新建一个 Maven 项目(以Maven版web项目为例)
完事儿点击【Create】等待 Maven 骨架形成即可。
上面我选择的是 Web 项目的 Maven 模版,根据开发项目的不同我们可以选择不同的模版 ⬇️
Maven 模版(internal) | 描述 |
---|---|
appfuse-basic-spring | 创建一个基于Hibernate,Spring和Spring MVC的Web应用程序的原型 |
appfuse-basic-struts | 创建一个基于Hibernate,Spring和Struts 2的Web应用程序的原型 |
appfuse-basic-tapestry | 创建一个基于Hibernate, Spring 和 Tapestry 4的Web应用程序的原型 |
appfuse-core | 创建一个基于 Hibernate and Spring 和 XFire的jar应用程序的原型 |
appfuse-modular-jsf | 创建一个基于 Hibernate,Spring和JSF的模块化应用原型 |
appfuse-modular-spring | 创建一个基于 Hibernate, Spring 和 Spring MVC 的模块化应用原型 |
appfuse-modular-struts | 创建一个基于 Hibernate, Spring 和 Struts 2 的模块化应用原型 |
appfuse-modular-tapestry | 创建一个基于 Hibernate, Spring 和 Tapestry 4 的模块化应用原型 |
maven-archetype-j2ee-simple | 一个简单的J2EE的Java应用程序 |
maven-archetype-marmalade-mojo | 一个Maven的 插件开发项目 using marmalade |
maven-archetype-mojo | 一个Maven的Java插件开发项目 |
maven-archetype-portlet | 一个简单的portlet应用程序 |
maven-archetype-quickstart | javaSE项目 |
maven-archetype-site-simple | 简单的网站生成项目 |
maven-archetype-site | 更复杂的网站项目一般用来做父工程 |
maven-archetype-webapp | 一个简单的Java Web应用程序 |
jini-service-archetype | Archetype for Jini service project creation |
softeu-archetype-seam | JSF+Facelets+Seam Archetype |
softeu-archetype-seam-simple | JSF+Facelets+Seam (无残留) 原型 |
softeu-archetype-jsf | JSF+Facelets 原型 |
jpa-maven-archetype | JPA 应用程序 |
spring-osgi-bundle-archetype | Spring-OSGi 原型 |
confluence-plugin-archetype | Atlassian 聚合插件原型 |
jira-plugin-archetype | Atlassian JIRA 插件原型 |
maven-archetype-har | Hibernate 存档 |
maven-archetype-sar | JBoss 服务存档 |
wicket-archetype-quickstart | 一个简单的Apache Wicket的项目 |
scala-archetype-simple | 一个简单的scala的项目 |
lift-archetype-blank | 一个 blank/empty liftweb 项目 |
lift-archetype-basic | 基本(liftweb)项目 |
cocoon-22-archetype-block-plain | http://cocoapacorg2/maven-plugins/ |
cocoon-22-archetype-block | http://cocoapacorg2/maven-plugins/ |
cocoon-22-archetype-webapp | http://cocoapacorg2/maven-plugins/ |
myfaces-archetype-helloworld | 使用MyFaces的一个简单的原型 |
myfaces-archetype-helloworld-facelets | 一个使用MyFaces和Facelets的简单原型 |
myfaces-archetype-trinidad | 一个使用MyFaces和Trinidad的简单原型 |
myfaces-archetype-jsfcomponents | 一种使用MyFaces创建定制JSF组件的简单的原型 |
gmaven-archetype-basic | Groovy的基本原型 |
gmaven-archetype-mojo | Groovy mojo 原型 |
③ 此时的目录结构不全,我们需要自行添加。
④ 添加 src/main/java
文件夹:在main目录上右击New -> Directory -> 选择出现的 java 文件夹即可
⚠️ 旧版本的 IDEA可能没有自动提示,我们就需要手动创建一个普通 java 文件夹,然后选中创建好的 java 文件右击进行关联设置即可。
⑤ 添加 src/main/resources
文件夹:(已存在就跳过此步骤) 在main目录上右击New -> Directory -> 选择出现的 java 文件夹即可
⚠️ 旧版本的 IDEA可能没有自动提示,我们就需要手动创建一个普通 resources 文件夹,然后同上面的关联方式选中创建好的 resources 文件右击进行关联为 Resources Root
即可。
⑥ 添加 src/test/java
文件夹:在src目录上右击New -> Directory -> 选择出现的 test/java 文件夹即可
⚠️ 旧版本的 IDEA可能没有自动提示,我们就需要手动创建一个普通 test/java 文件夹,然后同上面的关联方式选中创建好的 test/java 文件右击进行关联为 Test Sources Root
即可。
⑦ 添加 src/test/resources
文件夹:在src目录上右击New -> Directory -> 选择出现的 test/resources 文件夹即可
⚠️ 旧版本的 IDEA可能没有自动提示,我们就需要手动创建一个普通 test/resources 文件夹,然后同上面的关联方式选中创建好的 test/resources 文件右击进行关联为 Test Resources Root
即可。
⑧ 最终目录结构
4.引入Maven依赖pom.xml
1)导入Spring依赖
打开 pom.xml
导入 spring 的依赖(spring-context)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
2)导入junit测试包
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
5.创建Spring配置文件
Dao层(持久层)中的对象我们都在 applicationContext.xml 文件中进行配置,交由 Spring 管理。
在 src/main/resources/
目录下创建一个 xml 文件,文件名可以随意,但 Spring 建议的名称为 applicationContext.xml
。
创建好的xml文件如下
三、XML方式实现IoC
1.具体演示
① 定义Dao层接口与实现类
测试接口:
public interface TestDao {
void save();
}
测试实现类:
public class TestDaoImpl implements TestDao {
public TestDaoImpl() {
super();
System.out.println("TestDaoImpl无参构造");
}
@Override
public void save(){
System.out.println("执行save()方法");
}
}
② 在配置文件中将dao配置进去
<bean id="TestDaoImpl" class="com.quokka.dao.impl.TestDaoImpl"/>
<bean/>
:用于定义一个实例对象,一个实例对应一个 bean 标签。id
:该属性是 Bean 实例的唯一标识,程序通过 id 属性访问 Bean,Bean 与 Bean 间的依赖关系也是通过 id 属性关联的。class
:用类的全路径名指定该 Bean 所属的类。(⚠️ 注意这里只能是类,不能是接口)
③ 定义测试类
public class AppTest {
@Test
public void test01(){
//1 指定spring配置文件的位置和名称
String resource = "classpath:applicationContext.xml";
//2 读取配置文件,创建spring容器对象
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(resource);
//3 使用id从spring容器中获取对象
TestDaoImpl testDaoImpl = (TestDaoImpl) applicationContext.getBean("TestDaoImpl");
//执行对象的业务方法
testDaoImpl.save();
}
}
当然我们也可以根据类型获取 ⬇️
public class AppTest {
@Test
public void test01(){
//1 指定spring配置文件的位置和名称
String resource = "applicationContext.xml";
//2 读取配置文件,创建spring容器对象
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(resource);
//3 根据类型获取
TestDaoImpl testDaoImpl = applicationContext.getBean(TestDaoImpl.class);
//执行对象的业务方法
testDaoImpl.save();
}
}
测试结果:
TestDaoImpl无参构造
执行save()方法
这样我们就使用 Spring 的 IoC 实现了解耦合。
getBean方法:
//通过指定 id 获取对象的实例,需要手动强转 public Object getBean(String name) throws BeansException; //通过指定类型获取对象的实例,不需要强转 public <T> T getBean(Class<T> requiredType);
⚠️ 注意:同一个类型下只能有一个对象实例。
2.ApplicationContext接口
1)配置文件在不同路径的获取容器对象方式
ApplicationContext 接口用于加载 Spring 的配置文件,在程序中充当 “容器” 的角色,其常用实现类有两个。
- 若 Spring 配置文件存放在项目的类路径下(建议统一放在 resoueces 文件夹下),即该配置在 src 中。则使用 ClassPathXmlApplicationContext 实现类进行加载配置文件。(推荐)
- 若 Spring 配置文件存放在项目的根路径下,即该配置文件与 src 目录同级。则使用 FileSystemXmlApplicationContext 实现类来加载配置文件。
2)ApplicationContext容器中对象的装配时机
ApplicationContext 容器会在容器对象初始化时,将其中的所有对象一次性全部装配好。以后代码中若要使用到这些对象,只需从内存中直接获取即可。执行效率较高,但占用内存。
即下面的语句会将配置文件中的所有对象一次性创建出来,存储在 ApplicationContext 容器中⬇️
//2 读取配置文件,创建spring容器对象
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(resource);
3.Bean的装配
容器根据代码要求创建 Bean 对象后再传递给代码的过程,称为 Bean 的装配。
1)默认装配方式
Spring 调用 Bean 类的无参构造器,创建空值的实例对象。即 Spring 框架是默认调用类的无参构造方法来创建对象,如果定义了有参构造,就需要在类中显示的定义无参构造方法。
2)容器中Bean的作用域
当通过 Spring 容器创建一个 Bean 实例时,不仅可以完成 Bean 的实例化,还可以在<bean>标签中通过 scope
属性,为 Bean 指定特定的作用域,Spring 支持多种作用域。⬇️
scope属性值 | 描述 |
---|---|
singleton |
单例模式。即在整个 Spring 容器中使用 singleton 定义的 Bean 将是单例的,叫这个名称的对象只有一个实例。(默认就是单例的) |
prototype |
原型模式。即每次使用 getBean 方法获取的同一个<bean />的实例都是一个新的实例。 |
request |
对于每次 HTTP 请求,都将会产生一个不同的 Bean 实例。 |
session |
对于每个不同的 HTTP session,都将产生一个不同的 Bean 实例。 |
- 对于 scope 的值 request、session 只有在 Web 应用中使用 Spring 时,该作用域才有效。
- 对于 scope 为 singleton 的单例模式,该 Bean 是在容器被创建时即被装配好了。
- 对于 scope 为 prototype 的原型模式,Bean 实例是在代码中使用该 Bean 实例时才进行装配的。
四、基于XML的DI
1.依赖注入介绍
① 什么是依赖?
如 classA 类中含有 classB 的实例,在 classA 中调用 classB 的方法完成功能,则 classA 对 classB 有依赖。
public class ClassA {
private ClassB classB;
public void wayA(){
classB.wayB();
}
}
class ClassB{
public void wayB(){
System.out.println("ClaccB中的way()方法");
}
}
② 什么是注入?
Bean 实例在调用无参构造器创建对象后,就要对 Bean 对象的属性进行初始化。初始化是由容器自动完成的,称为注入。注入方式的有多种,其中常用的有两类:set方法注入、构造方法注入。
③ IoC的实现:
依赖注入(Dependency Injection,简称DI)是指程序运行过程中若需要调用另一个对象协助时,无须在代码中创建被调用者实例。而是依赖于外部容器,由外部容器创建后传递给程序。
④ Spring使用依赖注入(DI)实现IoC:
Spring 容器是一个超级大工厂,负责创建和管理所有的 Java 对象,这些 Java 对象被称为 Bean。Spring 容器管理着容器中 Bean 之间的依赖关系,这个关系就是 Spring 使用 “依赖注入” 的方式来实现的,从而实现 IoC 使得对象之间解耦和。
2.set方法注入
set 方法注入是指通过 Setter 方法传入被调用者的实例。这种注入方式简单、直观,因而在 Spring 的依赖注入中大量使用。
1)简单类型
这里指的简单类型是指 8个基本类型 + String。
① 定义普通类
public class Student {
private String name;
private int age;
// Setter (必须提供两个属性的 Setter 方法)
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
//toString()
}
② 加入对象到配置文件中
<bean id="Student" class="com.quokka.pojo.Student">
<!-- 简单类型的属性赋值 -->
<property name="name" value="张三" /> <!-- Spring容器实例化时自动调用setName("张三") -->
<property name="age" value="18" /> <!-- Spring容器实例化时自动调用setAge(18) -->
</bean>
property
标签:注入属性值。name
属性:需要注入值的属性名,与类中的属性名相同。value
属性:值的类型为基本类型 + String。
③ 测试代码
@Test
public void test02(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student = applicationContext.getBean(Student.class);
System.out.println(student);
}
测试结果:
Student{name='张三', age=18}
2)引用类型
当指定 Bean 的某属性值为另一 Bean 的实例时,通过 ref
指定它们间的引用关系,且 ref 的值必须为某 Bean 的 id 值。
① 定义普通类
public class Student {
private String name;
private int age;
private Teacher teacher;
// Setter
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
//toString()
}
public class Teacher {
private String name;
//Setter
public void setName(String name) {
this.name = name;
}
@Override
//toString()
}
② 加入对象到配置文件中
<bean id="Student" class="com.quokka.pojo.Student">
<!-- 简单类型的属性赋值 -->
<property name="name" value="张三" /> <!-- setName("张三") -->
<property name="age" value="18" /> <!-- setAge(18) -->
<!-- 引用类型的属性赋值 -->
<property name="teacher" ref="Teacher"/> <!-- setTeacher(Teacher) -->
</bean>
<bean id="Teacher" class="com.quokka.pojo.Teacher">
<property name="name" value="张老师"/>
</bean>
property
标签:注入属性值。name
属性:需要注入值的属性名,与类中的属性名相同。value
属性:值的类型为基本类型 + String。ref
属性:值的类型为引用类型(不包含包装类和String),通过在 Spring 容器中已存在的 Bean 对象的 id 属性赋值。
③ 测试代码
@Test
public void test02(){
Student student = applicationContext.getBean(Student.class);
System.out.println(student);
}
测试结果:
Student{name='张三', age=18, teacher=Teacher{name='张老师'}}
3.构造方法注入
构造方法注入是指在构造调用者实例的同时,完成被调用者的实例化,即使用构造器设置依赖关系。
① 定义普通类
public class Student {
private String name;
private int age;
private Teacher teacher;
/**
* 有参构造,Spring容器实例化时调用该方法
*/
public Student(String name, int age, Teacher teacher) {
this.name = name;
this.age = age;
this.teacher = teacher;
}
@Override
//toString()
}
public class Teacher {
private String name;
public void setName(String name) {
this.name = name;
}
@Override
//toString()
}
② 加入对象到配置文件中
<bean id="Student" class="com.quokka.pojo.Student">
<!-- 通过构造器的参数注入值 -->
<constructor-arg name="name" value="张三"/>
<constructor-arg name="age" value="18"/>
<constructor-arg name="teacher" ref="Teacher"/>
</bean>
<bean id="Teacher" class="com.quokka.pojo.Teacher">
<property name="name" value="张老师"/>
</bean>
constructor-arg
标签:通过构造器的参数注入值。name
属性:参数名,与构造方法中的参数名相同。value
属性:值的类型为基本类型及其 String 类型。ref
属性:值的类型为引用类型(不包含包装类和String),通过在 Spring 容器中已存在的 Bean 对象的 id 属性赋值。
③ 测试代码
@Test
public void test02(){
Student student = applicationContext.getBean(Student.class);
System.out.println(student);
}
测试结果:
Student{name='张三', age=18, teacher=Teacher{name='张老师'}}
五、连接池
1.连接池介绍
⓵ 传统数据库连接对象的使用:
每次都与数据库建立网络连接,并提供用户名和密码,然后底层通过一系列复杂的处理,才能最终得到连接对象。我们在使用完连接之后,要进行资源的释放(关闭连接)。关闭连接的原因是数据库能够提供的连接数是有限的,如果不将连接关闭,当连接数用完之后,就无法再提供连接对象了。在获取连接到关闭连接这个过程是很耗费系统资源和时间的,对于访问比较密集的程序而言,极大的影响了程序的执行效率。
⓶ 连接池思想:(优化连接对象的使用)
一次性创建一定数量的连接对象,然后将这些对象放到一个容器中(连接池对象)。使用连接时,从该容器中随机获取一个连接对象完成操作。释放资源时,将连接对象返回到这个容器中,而不是真正的关闭连接。在整个过程中,连接对象自从创建之后就不再关闭,起到对连接的复用的目的,从而提高了程序的执行效率(单位时间内所能处理的请求数)。
⓷ 常见的连接池:
- 阿里巴巴的德鲁伊(druid)
- c3p0
2.配置连接池Bean
在 applicationContext.xml 配置文件中配置数据源 ⬇️
<!-- 配置数据源(连接池对象) -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/quokka_mysql"/>
<property name="username" value="root"/>
<property name="password" value="quokka123456"/>
</bean>
当然我们为了能够动态更换数据库,也可以使用从外部配置文件引入的方式,然后使用 spEL
语法引入即可 ⬇️
① 准备外部文件(以db.properties为例)
该文件放在
src/main/resources/
目录下即可
db.properties 文件内容如下⬇️
# 数据库驱动
jdbc.driver=com.mysql.cj.jdbc.Driver
# 数据库链接url
jdbc.url=jdbc:mysql://localhost:3306/你的数据库名
# 数据库用户名
jdbc.username=root
# 数据库密码
jdbc.password=你的数据库密码
② 引入外部文件并配置数据源
<!-- 引入外部配置文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 配置数据源(连接池对象) -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
外部导入静态资源记得刷新 Maven ⬇️
六、DBUtils
1.DBUtils概述
DBUtils 是 Apache 的一款用于简化数据访问层(Dao层)代码的工具类,是对 JDBC 的封装。
核心对象:
QueryRunner runner = new QueryRunner(DataSource ds)
- DataSource 实现类是连接池对象
核心方法:
int update(Connection conn,String sql语句,Object ... ?对应的参数值)
:执行增删改操作。query(String sql语句, ResultSetHandler hl,Object ... ?对应的参数值)
:执行查询操作。
2.Spring使用XML方式整合DBUtils
下面演示基于 Spring 的 xml 配置,实现 student 表的 CRUD 案例。
① 准备数据库表
CREATE TABLE `student` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`age` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
② pom.xml导入相关依赖
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version>
</dependency>
<!-- 德鲁伊连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.14</version>
</dependency>
<!-- DBUtils -->
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.7</version>
</dependency>
③ 准备数据库连接的properties文件
在 src/main/resources
下创建 db.properties
文件,内容如下
# 数据库驱动
jdbc.driver=com.mysql.cj.jdbc.Driver
# 数据库链接url
jdbc.url=jdbc:mysql://localhost:3306/你自己的数据库
# 数据库用户名
jdbc.username=root
# 数据库密码
jdbc.password=你自己的数据库密码
④ 在applicationContext.xml配置文件中配置数据源
<!-- 引入外部配置文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 配置数据源(连接池对象) -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- (第三方类)DBUtils的QueryRunner对象 -->
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
<!-- 构造方法注入 -->
<constructor-arg name="ds" ref="dataSource"/>
</bean>
<bean id="studentDao" class="com.quokka.dao.impl.StudentDaoImpl">
<!-- setter方法依赖注入 -->
<property name="queryRunner" ref="queryRunner"/>
</bean>
<bean id="studentService" class="com.quokka.service.impl.StudentServiceImpl">
<!-- setter方法依赖注入 -->
<property name="studentDao" ref="studentDao"/>
</bean>
依赖关系如下 ⬇️
⑤ 创建Student类
public class Student {
private String id;
private String name;
private String age;
//Getter and Setter
@Override
//toString()
}
⑥ 创建StudentDao及其实现类
- 需要注入 QueryRunner 对象
- QueryRunner 对象创建的时候需要注入 DataSource
public class StudentDaoImpl implements StudentDao {
//依赖DBUtils的QueryRunner对象
private QueryRunner queryRunner;
public void setQueryRunner(QueryRunner queryRunner) {
this.queryRunner = queryRunner;
}
@Override
public void save(Student student) {
try{
String sql = "insert into student values(null,?,?)";
queryRunner.update(sql,student.getName(),student.getAge());
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException("保存失败!");
}
}
@Override
public Student getById(int id){
try{
String sql = "select * from student where id=?";
return queryRunner.query(sql,new BeanHandler<Student>(Student.class),id);
}catch (SQLException e){
e.printStackTrace();
throw new RuntimeException("获取失败!");
}
}
@Override
public List<Student> getAll() {
try{
String sql = "select * from student";
return queryRunner.query(sql, new BeanListHandler<>(Student.class));
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException("查询所有失败!");
}
}
@Override
public void delete(int id) {
try{
String sql = "delete from student where id=?";
queryRunner.update(sql,id);
}catch (SQLException e){
e.printStackTrace();
throw new RuntimeException("删除失败!");
}
}
@Override
public void update(Student student) {
try{
String sql = "update student set age=? where name=?";
queryRunner.update(sql,student.getAge(),student.getName());
}catch (SQLException e){
e.printStackTrace();
throw new RuntimeException("修改失败!");
}
}
}
⑦ 创建StudentService及其实现类
- 需要注入 StudentDao
public class StudentServiceImpl implements StudentService {
private StudentDao studentDao;
public void setStudentDao(StudentDao studentDao) {
this.studentDao = studentDao;
}
@Override
public void save(Student student) {
studentDao.save(student);
}
@Override
public Student getById(int id) {
return studentDao.getById(id);
}
@Override
public List<Student> getAll() {
return studentDao.getAll();
}
@Override
public void delete(int id) {
studentDao.delete(id);
}
@Override
public void update(Student student) {
studentDao.update(student);
}
}
⑦ 编写测试类
public class AppTest {
@Test
public void testServiceSave(){
//获取工厂对象
BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取对象实例
StudentService studentService = beanFactory.getBean(StudentService.class);
Student student = new Student();
student.setName("王武");
student.setAge("22");
//增加记录
studentService.save(student);
}
@Test
public void testServiceGet(){
//获取工厂对象
BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取对象实例
StudentService studentService = beanFactory.getBean(StudentService.class);
//查询字段
Student student = studentService.getById(2);
System.out.println(student);
}
@Test
public void testServiceGetAll(){
//获取工厂对象
BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取对象实例
StudentService studentService = beanFactory.getBean(StudentService.class);
//查询所有字段
List<Student> studentList = studentService.getAll();
for (Student student : studentList) {
System.out.println(student);
}
}
@Test
public void testServiceUpdate(){
//获取工厂对象
BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取对象实例
StudentService studentService = beanFactory.getBean(StudentService.class);
//修改记录
Student student = new Student();
student.setName("王武");
student.setAge("28");
studentService.update(student);
}
@Test
public void testServiceDelete(){
//获取工厂对象
BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取对象实例
StudentService studentService = beanFactory.getBean(StudentService.class);
//修改记录
studentService.delete(3);
}
}
⑧ 最终目录结构
七、Spring的注解开发
使用 Spring 的纯 xml 配置方式时,每添加一个 Dao 或者是 Service,都需要将其交给 Spring 进行管理。即需要在 Spring 的配置文件中配置一个 bean 标签,影响开发效率,同时会导致配置文件臃肿。因此 Spring 提供了一些注解用来代替<bean>的配置,可以大大提高开发效率。注解开发是一种趋势,注解代替 xml 配置文件 ,简化配置。
但是也不是使用纯注解的方式进行开发,一般情况下都是注解 + xml配置的方式结合使用。
1.Spring常用注解
注解 | 说明 |
---|---|
@Component | 使用在类上用于实例化 Bean |
@Controller | 使用在 web 层类上用于实例化 Bean |
@Service | 使用在 service 层类上用于实例化 Bean |
@Repository | 使用在 dao 层类上用于实例化 Bean |
@Autowired | 使用在字段上用于根据类型依赖注入 |
@Qualifiler | 结合@Autowired一起使用,根据名称进行依赖注入 |
@Value | 注入简单类型(基本类型 + String)属性 |
⓵ @Component
@Component 注解用于替代之前的 bean 标签,使用在类上用于实例化 Bean。它有三个别名注解分别如下:
- @Controller:使用在 web 层类上用于实例化 Bean
- @Service:使用在 service 层类上用于实例化 Bean
- @Repository:使用在 dao 层类上用于实例化 Bean
使用示范:
方式一:自定义唯一标识名
@Component("accountDao") //accountDao是实例在IoC容器中的唯一标识
public class AccountDaoImpl implements AccountDao {
@Override
public void save() {
System.out.println("Dao中的save()方法");
}
}
方式二:使用默认唯一标识名
@Component //若不指定值,id就是类名的首字母小写,即accountDaoImpl
public class AccountDaoImpl implements AccountDao {
@Override
public void save() {
System.out.println("Dao中的save()方法");
}
}
方式三:使用别名注解(推荐)
@Repository
public class AccountDaoImpl implements AccountDao {
@Override
public void save() {
System.out.println("Dao中的save()方法");
}
}
⓶ @Autowired/@Qualifiler/@Value
使用注解的方式进行开发不需要提供 Setter 方法。
- @Autowired:使用在字段上用于根据类型依赖注入(已在 Spring 容器中的引用类型)
- @Qualifiler:结合@Autowired一起使用,根据名称进行依赖注入
- @Value:注入简单类型(基本类型 + String)属性
使用示范:
示例一:引用类型依赖注入
@Service
public class AccountServiceImpl implements AccountService {
@Autowired //默认按类型匹配,如果匹配到多个结果,则将字段名作为ID进行唯一匹配
@Qualifier("accountDaoImpl") //搭配@Qualifier注解就可以按照指定的value属性值匹配唯一的结果
private AccountDao accountDao;
@Override
public void save() {
accountDao.save();
}
}
示例二:简单类型注入
@Service
public class AccountServiceImpl implements AccountService {
@Value("张三")
private String name;
@Value("18")
private int age;
@Override
public void save() {
accountDao.save();
}
}
可以看出,简单类型的注入显得很多余,还不如直接赋值来得方便,一般情况下不使用。当然,需要通过外部文件动态引入的时候可以考虑使用。
⓷ @Resource
@Resource注解不是 Spring 的注解,是 JDK 提供的注解,只不过是 Spring 框架兼容了而已,如果在字段上添加该注解,Spring 也会解析。其作用就是将 @Autowired 和 @Qualifier 注解的作用合二为一。
使用示范:
方式一:不指定唯一id
@Service
public class AccountServiceImpl implements AccountService {
//默认以字段名作为ID去IoC容器中匹配,如果不存在则按类型匹配,匹配不到或匹配多个相同类型就报异常
@Resource
private AccountDao accountDao;
@Override
public void save() {
accountDao.save();
}
}
方式二:指定唯一id
@Service
public class AccountServiceImpl implements AccountService {
//根据类型和ID进行匹配,相当于@Autowired 和 @Qulifiler
@Resource(name = "accountDaoImpl")
private AccountDao accountDao;
@Override
public void save() {
accountDao.save();
}
}
⓸ @Autowired注解和@Resource注解有什么区别?
提供方不同:
@Autowired是 spring 提供的注解,@Resource是 JDK 提供的注解。
匹配机制不同:
- @Autowired注解在没有@Qualifier注解的前提下,首先根据类型自动匹配,如果在 Spring 的容器中存在多个该类型的实例,则以属性名作为 id 进行匹配,如果没有匹配成功,则抛出异常。有@Qualifier(“xxx”)注解会将将 id 为 “xxx” 的实例注入到该属性中,需要结合@Autowired来使用。
- @Resource注解有 name 属性就以 name 属性的值作为 id 进行唯一匹配,匹配不到则抛出异常。没有 name 属性以属性名作为 id 进行唯一匹配,如果没有匹配到,则根据类型进行自动匹配,如果匹配不到或匹配到多个相同类型的实例,则抛出异常。
2.配置注解扫描
需要在 Spring 的 applicationContext.xml 主配置文件中配置注解扫描,扫描指定包下使用了 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">
<!-- 开启组件扫描:扫描指定包下使用了Spring注解的类 -->
<context:component-scan base-package="com.quokka"/>
</beans>
3.Spring整合Junit
Spring 中一个类继承了 junit 中的类,并对类中运行的方法进行增强操作,让代码运行的时候知道有 Spring 容器的存在,且可以从容器中拿到要注入的对象。步骤如下⬇️
⓵ 导入spring-test和junit依赖
<!-- java单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.23</version>
</dependency>
⓶ 在测试类上通过注解的方式标注配置文件的位置
@ContextConfiguration("classpath:applicationContext.xml") //指定配置文件的所在路径
public class AppTest {}
⓷ 在测试类上通过注解的方式表明使用spring的测试类去运行代码
@ContextConfiguration("classpath:applicationContext.xml") //指定配置文件的所在路径
@RunWith(SpringJUnit4ClassRunner.class) //指定使用spring编写的增强的junit类运行代码
public class AppTest {}
⓸ 在测试类中通过@Autowired注入需要测试的对象
@ContextConfiguration("classpath:applicationContext.xml") //指定配置文件的所在路径
@RunWith(SpringJUnit4ClassRunner.class) //指定使用spring编写的增强的junit类运行代码
public class AppTest {
@Autowired
AccountService accountService;
}
4.Spring使用注解方式整合DBUtils
⓵ 新建Maven工程
详见:创建Maven项目
⓶ 导入相关依赖到pom.xml
<dependencies>
<!-- spring依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.23</version>
</dependency>
<!-- java单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version>
</dependency>
<!-- 德鲁伊连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.14</version>
</dependency>
<!-- DBUtils -->
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.7</version>
</dependency>
</dependencies>
⓷ 准备resources/jdbc.properties文件
# 数据库驱动
jdbc.driver=com.mysql.cj.jdbc.Driver
# 数据库链接url
jdbc.url=jdbc:mysql://localhost:3306/你的数据库
# 数据库用户名
jdbc.username=root
# 数据库密码
jdbc.password=你的数据库密码
⓸ 创建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">
<!-- 开启组件扫描:扫描指定包下使用了Spring注解的类 -->
<context:component-scan base-package="com.quokka"/>
<!-- 外部引入jdbc.properties -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置DataSource连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!-- 使用spring的el表达式获取已加载的配置文件中的数据 -->
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 配置QueryRunner,需要注入DataSource依赖 -->
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
<constructor-arg name="dataSource" ref="dataSource"/>
</bean>
</beans>
QueryRunner 和 DruidDatasource 两个类是第三方编写的,因此我们没有办法在他们上面添加注解,所以只能将他们配置在 xml 文件中。
⓹ 编写Dao及其实现类
@Repository
public class AccountDaoImpl implements AccountDao {
//依赖DBUtils的QueryRunner对象
@Autowired
private QueryRunner queryRunner;
public void setQueryRunner(QueryRunner queryRunner) {
this.queryRunner = queryRunner;
}
@Override
public void update(String name) {
try{
String sql = "update account set balance=balance-100 where name=?";
queryRunner.update(sql,name);
}catch (SQLException e){
e.printStackTrace();
}
}
}
⓺ 编写Service及其实现类
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
public void update(String name) {
accountDao.update(name);
}
}
⓻ 测试
@ContextConfiguration("classpath:applicationContext.xml") //指定配置文件的所在路径
@RunWith(SpringJUnit4ClassRunner.class) //指定使用spring编写的增强的junit类运行代码
public class AppTest {
@Autowired
AccountService accountService;
@Test
public void accountServiceTest(){
accountService.update("张三");
}
}
测试前的数据库表:
测试结果: