一、Spring 简介
- Spring 框架可以说是 Java 开发中最重要的框架,功能非常强大
- 中文文档:https://springdoc.cn/spring/
- 官网:https://spring.io/
- Spring makes Java Simple、modern、productive …
Spring 框架的几个核心概念:
IoC: Inversion of Control:控制反转
DI: Dependency Injection:依赖注入
AOP: Aspect Oriented Programming:面向切面编程
Object Oriented Programming: 面向对象编程
这里使用的 Spring 的版本是:5.2.8.release
🚀耦合:我依赖你,你不见了(不要你了),对我影响很大,我就得改代码
🚀写代码的方向:解耦,降低耦合性
二、读取配置文件、创建对象
①🎄 通过类加载器读取配置文件的输入流
②🎄 通过 Properties
对象的 load()
方法,传入输入流【加载配置文件】
③🎄 通过 Properties
对象的 getProperty(String key)
方法获取到 key 对应的 value 值
④🎄 使用反射 API,通过全类名创建类的实例
/** * 创建对象实例的工厂 */ public class InstanceFactory { // Properties 对象表示【.properties】配置文件 private static Properties properties; static { // 获取配置文件的输入流 try (InputStream is = InstanceFactory.class.getClassLoader().getResourceAsStream("properties.properties")) { properties = new Properties(); // 加载配置文件输入流 properties.load(is); } catch (Exception e) { e.printStackTrace(); } } private static ObjectAble getObjectByName(String key) { // 获取配置文件内容 String fullPath = properties.getProperty(key); try { Class<?> cls = Class.forName(fullPath); return (ObjectAble) cls.newInstance(); } catch (Exception e) { e.printStackTrace(); } return null; } public static ObjectAble create(String key) { return getObjectByName(key); } }
properties.properties 配置文件
personFullPath=com.guoqing.po.PersonV2 dogFullPath=com.guoqing.po.DogV2 boyFullPath=com.guoqing.po.Boy
🎄工厂模式结合配置文件降低类之间的耦合
🎄Spring 的 IoC 就是一个大工厂
三、使用 Spring
(1) 依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.8.RELEASE</version> </dependency>
(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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 【bean】所有被 Spring 管理的对象都是 bean --> <!-- 配置需要被 Spring 管理的类 --> <!-- id: 可通过 id 的值获取到该类的实例 --> <!-- class: 该类的全类名 --> <bean id="boy" class="com.guoqing.po.Boy" /> <bean id="personV1" class="com.guoqing.po.PersonV1" /> <bean id="personV2" class="com.guoqing.po.PersonV2" /> </beans>
(3) 获取 Spring IoC 工厂中的对象实例
类路径的东西:
① java 文件夹中的东西(打包后
classes
中的内容)② resources 文件夹中的东西
public class QQMain { public static void main(String[] args) { // 读取 Spring 的核心配置文件, 并得到 Spring 的 IoC 工厂 ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); // 通过 IoC 工厂获取实例(boy 是 Spring 的核心配置文件中的 bean 标签的 id 属性配置的内容) Boy boy = ctx.getBean("boy", Boy.class); System.out.println("boy = " + boy); } }
🍃 读取配置文件,并返回 IoC 容器的类是:ClassPathXmlApplicationContext
🍃 调用 ClassPathXmlApplicationContext 对象的
getBean()
方法,传入 bean 标签的 id 值 获取对象实例属性的本质含义是:
set 方法
后面的值改为小写①
setName()
【name 就是属性】②
setBoySchool()
【boySchool 就是属性】
- IoC 最重要的作用:解耦合
- Spring 可以轻松整合日志框架
<dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency>
通过
logback-classic
框架可以看到 Spring 相关的日志信息
四、IoC 容器
- IoC:Inversion of Control【控制反转】
- 对象创建的控制权转交给了 Spring
- IoC 容器创建了一系列的 bean
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="bookDaoImpl" class="com.guoqing.dao.impl.BookDaoImpl"/> <bean id="bookServiceImpl" class="com.guoqing.service.impl.BookServiceImpl"> <property name="bookDao" ref="bookDaoImpl"/> </bean> <bean id="bookController" class="com.guoqing.controller.BookController"> <property name="bookService" ref="bookServiceImpl"/> </bean> </beans>
五、依赖注入(DI)
- 依赖注入:Dependency Injection
常见的注入类型有3大类:
① bean(自定义类型)【ref 属性
】
② 基本类型、String、BigDecimal、包装类型【value
属性】
③ 集合类型(数组、Map、List、Set、Properties)☆
常见的注入方式有 2 种:
① 基于 setter(属性)
② 基于 constructor (构造方法)
(1) 基于 setter 注入【bean】
<bean id="cat" class="com.guoqing.domain.Cat"> <property name="catName" value="哆啦A梦"/> <property name="color" value="BLUE"/> </bean> <bean id="person" class="com.guoqing.domain.Person"> <property name="cat" ref="cat"/> </bean>
<bean id="cat" class="com.guoqing.domain.Cat"> <property name="catName" value="哆啦A梦"/> <property name="color" value="BLUE"/> </bean> <bean id="person" class="com.guoqing.domain.Person"> <property name="cat"> <ref bean="cat"/> </property> </bean>
<bean id="girl" class="com.guoqing.domain.Girl"> <property name="cat"> <bean class="com.guoqing.domain.Cat"> <property name="catName" value="卡菲"/> <property name="color" value="PINK"/> </bean> </property> </bean>
(2) 基于 setter 注入【基本类型、包装类型、String、BigDecimal】
<bean id="person" class="com.guoqing.domain.Person"> <property name="id" value="666"/> <property name="name"> <value>张国庆</value> </property> <property name="age" value="8"/> <property name="money"> <value>88888.666</value> </property> </bean>
(3) 基于 setter 注入【集合类型】
- 🍃 注入数组
<bean id="whatever" class="com.guoqing.domain.Whatever"> <property name="books"> <array> <value>Java 编程思想</value> <value>红楼梦</value> <value>水浒传</value> <value>三国演义</value> <value>三体</value> </array> </property> <property name="cats"> <array> <bean class="com.guoqing.domain.Cat"> <property name="color" value="RED"/> <property name="catName" value="小红"/> </bean> <bean class="com.guoqing.domain.Cat"> <property name="color" value="BLACK"/> <property name="catName" value="小黑"/> </bean> </array> </property> </bean>
- 🍃 注入 List
<bean id="whatever" class="com.guoqing.domain.Whatever"> <property name="books"> <!-- ArrayList --> <list> <value>Thinking In Java</value> <value>红楼梦</value> <value>水浒传</value> <value>三国演义</value> <value>三体</value> </list> </property> <property name="cats"> <list> <bean class="com.guoqing.domain.Cat"> <property name="color" value="RED"/> <property name="catName" value="小红"/> </bean> <bean class="com.guoqing.domain.Cat"> <property name="color" value="BLACK"/> <property name="catName" value="小黑黑"/> </bean> </list> </property> </bean>
- 🍃 注入 Set
<bean id="whatever" class="com.guoqing.domain.Whatever"> <property name="goods"> <!--LinkedHashSet--> <set> <value>苹果1</value> <value>土豆2</value> <value>西瓜3</value> <value>电脑4</value> <value>窗户5</value> <value>窗户5</value> <value>窗户5</value> <value>鼠标6</value> </set> </property> </bean>
HashSet 是无顺序的【可去重】
LinkedHashSet 是有顺序的【可去重】
- 🍃 注入 Map
<bean id="whatever" class="com.guoqing.domain.Whatever"> <property name="peoSal"> <map> <entry key="张国庆" value="88888"/> <entry key="周杰伦" value="155555"/> <entry key="王凡" value="266666"/> <entry key="陈铭酒" value="12121"/> <entry key="刘德华" value="888888"/> </map> </property> <property name="peoCatMap"> <map> <entry key="ZGQ"> <bean class="com.guoqing.domain.Cat"> <property name="catName" value="猫猫1"/> <property name="color" value="RED"/> </bean> </entry> <entry key="CMJ"> <bean class="com.guoqing.domain.Cat"> <property name="catName" value="猫猫2"/> <property name="color" value="PINK"/> </bean> </entry> </map> </property> </bean>
- 🍃 注入 Properties
<bean id="whatever" class="com.guoqing.domain.Whatever"> <property name="properties"> <props> <prop key="dao">com.guoqing.dao.impl.BookDaoImpl</prop> <prop key="service">com.guoqing.service.impl.BookServiceImpl</prop> </props> </property> </bean>
基于 setter 注入调用的是无参的构造方法
(4) 基于 setter 注入(命名空间)
没有命名空间的写法:
<bean id="cat" class="com.guoqing.domain.Cat"> <property name="color" value="skyblue"/> <property name="catName" value="阿辉"/> </bean> <bean id="teacher" class="com.guoqing.domain.Teacher"> <property name="id" value="2"/> <property name="name" value="李老师"/> <property name="salary" value="12121.666"/> <property name="gender" value="男"/> <property name="cat" ref="cat"/> </bean>
命名空间写法:
增加命名空间:
xmlns:p="http://www.springframework.org/schema/p"
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="cat" class="com.guoqing.domain.Cat" p:catName="阿辉辉" p:color="SKYBLUE"/> <bean id="teacher" p:name="张国庆老师" p:id="22" p:salary="6666.666" p:gender="男" p:cat-ref="cat" class="com.guoqing.domain.Teacher"/> </beans>
(5) 基于构造方法注入
- 基本类型
<!--调用没有参数的构造方法--> <bean id="student" class="com.guoqing.domain.Student"/>
<bean id="student" class="com.guoqing.domain.Student"> <!--调用有一个参数的构造方法--> <constructor-arg value="张国庆"/> </bean>
<bean id="student" class="com.guoqing.domain.Student"> <!--调用有2个参数的构造方法--> <constructor-arg value="张国庆"/> <constructor-arg value="99.5"/> </bean>
<bean id="student" class="com.guoqing.domain.Student"> <!--假如顺序和构造方法参数的顺序不一样的话, 可以指定 type--> <!--若不顺序不一样, 且不做任何处理, 必定报错--> <constructor-arg value="99.5" type="double"/> <constructor-arg value="张国庆" type="java.lang.String"/> </bean>
<bean id="student" class="com.guoqing.domain.Student"> <!--假如顺序和构造方法参数的顺序不一样的话, 可以指定 index--> <!--若不顺序不一样, 且不做任何处理, 必定报错--> <constructor-arg value="99.5" index="1"/> <constructor-arg value="张国庆" index="0"/> </bean>
- bean 注入
<bean id="student" class="com.guoqing.domain.Student"> <constructor-arg value="张国庆" index="0"/> <constructor-arg> <bean class="com.guoqing.domain.Cat"> <property name="catName" value="花儿"/> <property name="color" value="ORANGE"/> </bean> </constructor-arg> </bean>
<bean id="cat" class="com.guoqing.domain.Cat"> <property name="catName" value="流浪猫"/> <property name="color" value="GRAY"/> </bean> <bean id="student" class="com.guoqing.domain.Student"> <constructor-arg value="张国庆" index="0"/> <constructor-arg ref="cat"/> </bean>
<bean id="cat" class="com.guoqing.domain.Cat"> <property name="catName" value="流浪猫"/> <property name="color" value="GRAY"/> </bean> <bean id="student" class="com.guoqing.domain.Student"> <constructor-arg value="张国庆" index="0"/> <constructor-arg> <ref bean="cat"/> </constructor-arg> </bean>
- 注入集合
<bean id="student" class="com.guoqing.domain.Student"> <constructor-arg type="java.util.List"> <list> <value>语文</value> <value>数学</value> <value>英语</value> <value>物理</value> <value>化学</value> </list> </constructor-arg> </bean>
六、创建过程比较复杂的对象
🥄 Spring 的配置文件中配置的对象只需要执行某些构造方法即可创建
🥄 当遇到创建过程极其复杂的对象(无法通过调用构造方法创建的对象)时就需要下面的方式了
🥄 如:JDBC 的 Connection 对象的创建
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.19</version> </dependency>
(1) 静态工厂(调用静态方法)
public class JdbcConnectionFactory { /** * 静态工厂 */ public static Connection getConnection() throws Exception { Class.forName("com.mysql.cj.jdbc.Driver"); return DriverManager.getConnection("jdbc:mysql://localhost:3306/jump_zgq?serverTimezone=UTC", "root", "root"); } }
<bean id="jdbcConnection" class="com.guoqing.factory.JdbcConnectionFactory" factory-method="getConnection"/>
🎀把创建复杂对象代码写在工厂中
🎀通过该工厂中的 static 方法返回对象的实例
🎀在 Spring 的核心配置文件中,通过
factory-method
属性指定需要调用该工厂类的哪个方法返回对象实例🎀【缺点】无法传参
(2) 实例工厂
public class JdbcConnectionFactory { private String driverClass; private String url; private String user; private String pwd; public JdbcConnectionFactory(String driverClass, String url, String user, String pwd) { this.driverClass = driverClass; this.url = url; this.user = user; this.pwd = pwd; } /** * 实例工厂 */ public Connection getConnection() throws Exception { System.out.println("调用了 getConnection() 方法"); Class.forName(this.driverClass); return DriverManager.getConnection(url, user, pwd); } }
<!--创建工厂实例--> <bean id="jdbcConnectionFactory" class="com.guoqing.factory.JdbcConnectionFactory"> <constructor-arg name="driverClass" value="com.mysql.cj.jdbc.Driver"/> <constructor-arg name="url" value="jdbc:mysql://localhost:3306/jump_zgq?serverTimezone=UTC"/> <constructor-arg name="user" value="root"/> <constructor-arg name="pwd" value="root"/> </bean> <!--通过工厂实例创建 Connection 对象--> <bean id="jdbcConnection" factory-bean="jdbcConnectionFactory" factory-method="getConnection"/>
🎉
factory-bean
属性指代工厂类的实例🎉
factory-method
属性指代要调用工厂对象的哪个实例方法来返回该复杂对象
(3) FactoryBean
🎁 Spring 提供的、用于方便地创建复杂对象的接口
🎁 本质和【实例工厂】类似
public class JdbcConnectionFactoryBean implements FactoryBean<Connection> { private String driverClass; private String url; private String user; private String pwd; public JdbcConnectionFactoryBean(String driverClass, String url, String user, String pwd) { this.driverClass = driverClass; this.url = url; this.user = user; this.pwd = pwd; } /** * 写创建该复杂对象的一系列代码 */ @Override public Connection getObject() throws Exception { System.out.println("调用了 getObject() 方法"); Class.forName(this.driverClass); return DriverManager.getConnection(url, user, pwd); } @Override public Class<?> getObjectType() { return Connection.class; } }
<bean id="jdbcConnection" class="com.guoqing.factoryBean.JdbcConnectionFactoryBean"> <constructor-arg name="driverClass" value="com.mysql.cj.jdbc.Driver"/> <constructor-arg name="url" value="jdbc:mysql://localhost:3306/jump_zgq?serverTimezone=UTC"/> <constructor-arg name="user" value="root"/> <constructor-arg name="pwd" value="root"/> </bean>
🍃 监听
class
属性后的类是否实现(implements)了FactoryBean 接口🍃 若实现了 FactoryBean 接口,则调用该类的
getObject()
方法,然后将该方法返回的实例放入 IoC 容器中若就是想拿到该类的实例,可通过
&
符号拼接上 id 属性的值,然后通过getBean()
方法获取
public class QQMain { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); Object bean = ctx.getBean("&jdbcConnection"); // bean = com.guoqing.factoryBean.JdbcConnectionFactoryBean@436e852b System.out.println("bean = " + bean); } }
若文章有错误请不吝赐教