Spring全家桶
学习官网:https://spring.io/
第一章 Spring5框架
Spring Framework Documentation
https://docs.spring.io/spring-framework/docs/current/reference/html/index.html
1.1 Spring 框架概述
/*
1、Spring是轻量级的开源的JavaEE框架
2、Spring可以解决企业应用开发的复杂性
3、Spring有两个核心部分:IOC和AOP
(1)IOC:控制反转,把创建对象过程交给Spring进行管理
(2)AOP:面向切面,不修改源代码进行功能增强
4、Spring特点
(1)方便解耦,简化开发
(2)AOP编程支持
(3)方便程序测试
(4)方便和其他框架进行整合
(5)方便进行事务操作
(6)降低API开发难度
*/
入门案例
第一步 下载Spring5,选取最新稳定版—5.3.8
https://repo.spring.io/release/org/springframework/spring/
第二步 创建java工程,并导入Spring相关JAR包
第三步 创建类和方法
User.java
public class User {
public void add(){
System.out.println("add...");
}
}
第四步 创建Spring配置文件bean1.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">
<!--配置User对象创建-->
<bean id="user" class="edu.mm.User"></bean>
</beans>
第五步 测试
@Test
public void testAdd(){
//1.加载spring配置文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
//2.获取通过配置文件创建的对象
User user = context.getBean("user", User.class);
System.out.println(user);
user.add();
}
运行截图:
1. 2 IOC 容器
1.2.1 IOC概念
(1)IOC,控制反转,把对象创建和对象之间的调用过程,交给 Spring 进行管理。
(2)使用 IOC目的:为了降低耦合度 。
1.2.2 IOC 底层原理
关键字:xml 解析、工厂模式、反射
需求:创建UserService.java和UserDao.java,希望在service层调用dao层的某个方法。
IOC过程
第一步:通过xml配置文件,创建对象。
<bean id="" class=""></bean>
第二步:创建工厂类
class UserFactory{
public static UserDao getDao(){
String classValue=class属性值;//1.xml解析(基于Dom4j),获取全类名
Class clazz=Class.forName(classValue);//2.通过反射创建对象
return (UserDao)clazz.newInstance();
}
}
第三步:在service层通过工厂模式来创建对象
class UserService{
public void excute(){
UserDao dao=UserFactory.getDao();
dao.add();
}
}
原始方法 new关键字 缺点:耦合度太高
UserDao.java
class UserDao{
public void add(){
...
}
}
UserService.java
class UserService{
UserDao userDao=new UserDao();
userDao.add();
}
工厂模式 工厂类作为中间件,来降低service层和dao层的耦合度。
UserFactory.java
class UserFactory{
public static UserDao getDao(){
return new UserDao();
}
}
UserService.java
class UserService{
public void excute(){
UserDao dao=UserFactory.getDao();
dao.add();
}
}
1.2.3 IOC 接口(BeanFactory)
-
IOC 思想基于 IOC 容器,IOC 容器底层就是对象工厂
-
Spring 提供 IOC 容器的两种方式(两个接口)
-
BeanFactory
IOC容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用 *加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象
-
ApplicationContext
BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用 *加载配置文件时候就会把在配置文件对象进行创建
-
1.2.4 IOC 操作 Bean管理(基于 xml)
IOC操作Bean管理包括:Spring创建对象和注入属性。
Bean 管理操作有两种方式:xml、注解
-
基于 xml 方式创建对象
<bean id="user" class="edu.mm.User"></bean>
【注意】
(1)在 spring 配置文件中,使用 bean 标签,标签里面添加对应属性,就可以实现对象创建
(2)在 bean 标签有很多属性,常用的属性如下
id 属性:唯一标识
class 属性:类全路径(包类路径)
(3)创建对象时候,默认是执行无参数构造方法完成对象创建
-
基于 xml 方式注入属性
/*DI:依赖注入,就是注入属性*/
-
使用 set 方法进行注入
第一步:创建实体类,定义属性和对应的set方法
public class User { public String name; public String stumum; public void setName(String name) { this.name = name; } public void setStumum(String stumum) { this.stumum = stumum; } }
第二步:在spring配置文件中创建对象,注入属性
<!--使用property属性注入 name:类里面属性名称 value:向属性注入的值--> <bean id="user" class="edu.mm.User"> <property name="name" value="java"></property> <property name="stumum" value="171404050119"></property> </bean>
-
使用有参数的构造函数进行注入
第一步:创建类、属性,创建有参数的构造函数
public class Book { public String bName; public String author; public Book(String bName, String author) { this.bName = bName; this.author = author; } }
第二步:在 spring 配置文件中进行配置
<bean id="book" class="edu.mm.Book"> <constructor-arg name="author" value="屈原"></constructor-arg> <constructor-arg name="bName" value="楚辞"></constructor-arg> </bean>
第三步:测试
@Test public void testBook(){ //1.加载spring配置文件 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); //2.获取通过配置文件创建的对象 Book book = context.getBean("book", Book.class); System.out.println(book.bName); }
-
-
基于xml 注入其他类型属性
-
注入属性—外部bean
/* (1)创建两个类service类和dao类 (2)在service调用dao里面的方法 (3)在spring配置文件中进行配置 */ public class UserService { public UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void show(){ userDao.showInfo(); System.out.println("I am service"); } }
<!--注入userDao对象 name属性:类里面属性名称 ref属性:创建userDao对象bean标签id值--> <bean id="userService" class="edu.mm.service.UserService"> <property name="userDao" ref="userDao"></property> </bean> <bean id="userDao" class="edu.mm.dao.UserDao"></bean>
-
注入属性—内部bean
/* 一对多关系:部门和员工 在实体类之间表示一对多关系,员工表示所属部门,使用对象类型属性进行表示 */ public class Dept { public String dName; public void setdName(String dName) { this.dName = dName; } } public class Emp { public String empName; public String empGender; public Dept dept;//员工属于某一个部门,使用对象形式表示 public void setEmpName(String empName) { this.empName = empName; } public void setEmpGender(String empGender) { this.empGender = empGender; } public void setDept(Dept dept) { this.dept = dept; } }
<bean id="emp" class="edu.mm.bean.Emp"> <property name="empName" value="李白"></property> <property name="empGender" value="男"></property> <property name="dept"> <bean id="dept" class="edu.mm.bean.Dept"> <property name="dName" value="保卫科"></property> </bean> </property> </bean>
-
注入属性—级联赋值
方式一:
<bean id="emp" class="edu.mm.bean.Emp"> <!--设置两个普通属性--> <property name="empName" value="李白"></property> <property name="empGende" value="男"></property> <!--级联赋值--> <property name="dept" ref="dept"></property> </bean> <bean id="dept" class="edu.mm.bean.Dept"> <property name="dName" value="保卫科"></property> </bean>
方式二:
<!--前提:Emp实体类要有getDept()--> <bean id="emp" class="edu.mm.dao.Emp"> <property name="empName" value="李白"></property> <property name="empGender" value="男"></property> <property name="dept" ref="dept"></property> <property name="dept.dName" value="保卫科"></property> </bean> <bean id="dept" class="edu.mm.dao.Dept"></bean>
-
注入属性—集合属性
1、注入数组类型属性
2、注入 List 集合类型属性
3、List集合注入对象属性
4、注入 Map 集合类型属性
5、注入Set 集合类型属性
public class Stu { //1 数组类型属性 private String[] courses; //2 List集合类型属性 private List<String> list; //3 List集合注入对象属性 private List<Course> courseList; //4 Map 集合类型属性 private Map<String,String> maps; //5 Set 集合类型属性 private Set<String> sets; public void setSets(Set<String> sets) { this.sets = sets; } public void setCourses(String[] courses) { this.courses = courses; } public void setList(List<String> list) { this.list = list; } public void courseList(List<Course> courseList) { this.courseList = courseList; } public void setMaps(Map<String, String> maps) { this.maps = maps; } }
<!--集合类型属性注入--> <bean id="stu" class="edu.mm.bean.Stu"> <!--1.数组类型属性注入--> <property name="courses"> <array> <value>java 课程</value> <value>数据库课程</value> </array> </property> <!--2.list类型属性注入--> <property name="list"> <list> <value>张三</value> <value>小三</value> </list> </property> <!--3.List集合中注入对象属性--> <property name="courseList"> <list> <ref bean="course1"></ref> <ref bean="course2"></ref> </list> </property> <!--4.map类型属性注入--> <property name="maps"> <map> <entry key="JAVA" value="java"></entry> <entry key="PHP" value="php"></entry> </map> </property> <!--5.set类型属性注入--> <property name="sets"> <set> <value>MySQL</value> <value>Redis</value> </set> </property> </bean> <bean id="course1" class="edu.mm.bean.Course"> <property name="cname" value="Spring5 框架"></property> </bean> <bean id="course2" class="edu.mm.bean.Course"> <property name="cname" value="MyBatis 框架"></property> </bean>
-
-
FactoryBean
1、Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)
2、普通 bean:在配置文件中定义 bean 类型就是返回类型
3、工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样
第一步 创建类,让这个类作为工厂 bean,实现接口 FactoryBean
第二步 实现接口里面的方法,在实现的方法中定义返回的 bean 类型
-
IOC 操作 Bean 管理(bean 作用域)
/* 在Spring中,默认情况下,bean 是单实例对象 (1)在spring配置文件bean标签里面有属性(scope)用于设置单实例还是多实例 (2)scope 属性值 第一个值 默认值,singleton,表示是单实例对象 第二个值 prototype,表示是多实例对象 (3)singleton 和 prototype 区别 第一,singleton单实例,prototype多实例 第二,scope=singleton,加载spring配置文件时创建单实例对象 scope=prototype,在调用getBean方法时创建多实例对象 */
-
IOC 操作 Bean 管理(bean 生命周期)
/*bean 生命周期: (1)通过构造器创建 bean 实例(无参数构造) (2)为 bean 的属性设置值和对其他 bean 引用(调用 set 方法) (3)调用 bean 的初始化的方法(需要进行配置初始化的方法) (4)使用bean(对象获取到了) (5)当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法) */
-
IOC 操作 Bean 管理(xml 自动装配)
根据指定装配规则(属性名称或者属性类型),Spring 自动将匹配的属性值进行注入
<!--实现自动装配 bean标签属性 autowire,配置自动装配 autowire 属性常用两个值: byName 根据属性名称注入 ,注入值 bean 的 id 值和类属性名称一样 byType 根据属性类型注入 -->
1.2.5 IOC 操作 Bean 管理(基于注解)
-
注解的含义
(1)注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值…)
(2)使用注解,注解作用在类、方法、属性上面
(3)使用注解目的:简化 xml 配置
-
针对 Bean 管理中创建对象Spring提供以下注解
(1)@Component
(2)@Service
(3)@Controller
(4)@Repository
上面四个注解功能是一样的,都可以用来创建 bean 实例
-
基于注解方式实现对象创建
-
第一步:引入依赖 spring-aop-5.3.8.jar
-
第二步:开启组件扫描
<!--开启组件扫描 1、如果扫描多个包,多个包使用逗号隔开 ;2、扫描包上层目录--> <context:component-scan base-package="edu.mm"></context:component-scan>
-
第三步:创建类,在类上面添加创建对象注解
/*在注解里面value属性值可以省略不写,默认值是类名称,首字母小写*/
-
-
基于注解方式实现属性注入
-
@Autowired:根据属性类型进行自动装配
-
@Resource:默认根据类型注入,也可以根据名称注入
-
@Value:注入普通类型属性
-
@Qualifier:根据名称进行注入
@Qualifier和@Autowired 一起使用
-
-
完全注解开发
第一步:创建配置类,替代 xml 配置文件
@Configuration @ComponentScan(basePackages = "edu.mm") public class SpringConfig { .... }
第二步:编写测试类
@Test public void testService2() { //加载配置类 ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); UserService userService = context.getBean("userService",UserService.class); System.out.println(userService); userService.add(); }
1.3 AOP
1.3.1 AOP概念
面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得 业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率,简单来说,就是不通过修改源代码方式,在主干功能里面添加新功能。
1.3.2 AOP底层原理
AOP 底层使用动态代理来实现,包括:JDK动态代理和CGLIB动态代理。
- JDK 动态代理—有接口
/*创建接口实现类代理对象,增强类的方法*/
-
CGLIB 动态代理—没接口
/*创建子类的代理对象,增强类的方法*/
1.4 JdbcTemplate
Spring 框架对 JDBC 进行封装,使用 JdbcTemplate 方便实现对数据库操作。
步骤:
第一步:准备工作
(1)引入相关jar包
-
druid-1.1.9.jar
-
mysql-connector-java-5.1.7-bin.jar
-
spring-jdbc-5.3.8.jar
-
spring-orm-5.3.8.jar
-
spring-tx-5.3.8.jar
(2)创建数据库
/*
Navicat MySQL Data Transfer
Source Server : hh
Source Server Version : 80023
Source Host : localhost:3306
Source Database : school
Target Server Type : MYSQL
Target Server Version : 80023
File Encoding : 65001
Date: 2021-07-05 11:59:40
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`sno` char(10) NOT NULL,
`sname` char(20) DEFAULT NULL,
`ssex` char(2) DEFAULT NULL,
`sage` smallint DEFAULT NULL,
`sdept` char(20) DEFAULT NULL,
PRIMARY KEY (`sno`),
UNIQUE KEY `sname` (`sname`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES ('100256', '刘晨', '男', '19', 'IS');
INSERT INTO `student` VALUES ('1114', '马修', '男', '20', 'CS');
INSERT INTO `student` VALUES ('171145', '屈明明', '男', '15', 'CS');
INSERT INTO `student` VALUES ('1714', '李白', '男', '100', '九三学社');
INSERT INTO `student` VALUES ('178963', '张力', '女', '18', 'MA');
(3)在 spring 配置文件配置数据库连接池
方式一:直接配置数据库连接池
<!--组件扫描-->
<context:component-scan base-package="edu.mm"></context:component-scan>
<!--配置数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="jdbc:mysql:///school?useUnicode=true&characterEncoding=utf8"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
</bean>
方式二:spring.xml读取dbConfig.properties文件来配置数据库连接池
dbConfig.properties
jdbc.user=root
jdbc.password=123456
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/school?useUnicode=true&characterEncoding=utf8
jdbc.driverClass=com.mysql.jdbc.Driver
spring.xml
<!--引入外部配置文件-->
<context:property-placeholder location="classpath:dbConfig.properties"> </context:property-placeholder>
<!--组件扫描-->
<context:component-scan base-package="edu.mm"></context:component-scan>
<!--配置数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${jdbc.jdbcUrl}"></property>
<property name="username" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClassName" value="${jdbc.driverClass}"></property>
</bean>
(4)配置 JdbcTemplate 对象,注入 DataSource
<!--创建JdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入dataSource-->
<property name="dataSource" ref="dataSource"></property>
</bean>
第二步:编写代码,使用JdbcTemplate 操作数据库
调用 JdbcTemplate 对象里面 update ()、queryForObject(),实现增、删、改、查操作
以插入一条数据为例:
@Test
public void test1(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
//1.创建sql语句
String sql="insert into student values(?,?,?,?,?)";
Student student=new Student();
student.setSno("1714");
student.setSname("李白");
student.setSage(100);
student.setSsex("男");
student.setSdept("九三学社");
//2.调用方法实现
Object[] args={
student.getSno(),student.getSname(),student.getSsex(),student.getSage(),student.getSdept()};
int ret = jdbcTemplate.update(sql, args);
System.out.println(ret);
}
补充:
/*
param1:sql语句
param2:可变参数,sql语句的值
*/
int update(String sql, @Nullable Object... args);
/*添加一条数据*/
String sql = "insert into t_book values(?,?,?)";
Object[] args = {
book.getUserId(), book.getUsername(), book.getUstatus()};
/*修改一条数据*/
String sql = "update t_book set username=?,ustatus=? where user_id=?";
Object[] args = {
book.getUsername(), book.getUstatus(),book.getUserId()};
/* 删除一条数据*/
String sql = "delete from t_book where user_id=?";
/*
param1:sql语句
param:返回类型Class
*/
String sql = "select count(*) from t_book";//查询表的记录数
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);//查询返回某个值
/*
param1:sql 语句
param2:RowMapper 是接口,针对返回不同类型数据,使用这个接口里面实现类完成数据封装
param3:sql语句值
*/
String sql = "select * from t_book where user_id=?";//查询返回对象
Book book = jdbcTemplate.queryForObject(sql, new
BeanPropertyRowMapper<Book>(Book.class), id);//查询返回对象
/*
param1:sql 语句
param2:RowMapper 是接口,针对返回不同类型数据,使用这个接口里面实现类完成数据封装
*/
String sql = "select * from t_book";//查询返回集合
List<Book> bookList = jdbcTemplate.query(sql, new
BeanPropertyRowMapper<Book>(Book.class));//查询返回集合
第三步:解决bug
当你在测试类中使用到@Autowired自动注入时,会报以上错误
解决方案:在测试类上方加上@RunWith(SpringRunner.class)
注解
原因:有了@RunWith(SpringRunner.class)这些类才能实例化到spring容器中,自动注入才能生效不然直接一个NullPointerExecption
1.5 事务管理
事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败。
在 Spring 进行事务管理时,使用声明式事务管理,最常用的是基于注解方式,使用@Transactiona注解将事物添加到Service层,底层基于AOP,通过PlatformTransactionManager接口来进行事务管理。
事物的4个特性:原子性、一致性、隔离性、持久性
典型场景:银行转账
1.6 Spring5 新特性
(1)整合日志框架
(2)@Nullable 注解
(3)函数式注册对象
(4)整合 JUnit5 单元测试框架
(5)SpringWebflux 使用