Spring IoC与Bean管理
Spring快速入门
IoC控制反转
- IoC控制反转,全称Inverse of Control,是一种设计理念
- 由代理人来创建与管理对象,消费者通过代理人来获取对象
- IoC的目的是降低对象之间直接耦合
- 加入IoC容器将对象统一管理,让对象关联变为弱耦合
DI依赖注入
- IoC是设计理念,是现代程序设计遵循的标准,是宏观目标
- DI(Dependency Injection)是具体技术实现,是微观实现
- DI在Java中利用反射技术实现对象注入(Injection)
Spring
Spring的含义
- Spring可从狭义与广义俩个角度看待
- 狭义的Spring是指Spring框架(Spring Framework)
- 广义的Spring是指Spring生态体系
狭义的Spring
- Spring框架是企业开发复杂性的一站式解决方案
- Spring框架的核心是IoC容器与AOP面向切面变成
- Spring IoC负责创建与管理系统对象,并在此基础上扩展功能
传统开发方式
- 对象直接引用导致对象硬性关联,程序难以扩展维护
Spring IoC
- IoC容器是Spring生态的地基用于统一创建与管理对象依赖
Spring IoC容器职责
- 对象的控制器交由第三方统一管理(IoC控制反转)
- 利用Java反射技术实现运行时对象创建与关联(DI依赖注入)
- 基于配置提高应用程序的可维护性与扩展性
代码示例:
需要引入的依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.1</version> </dependency>
Apple.java
package com.imooc.spring.ioc.entity; /** * @author CubeMonkey * @create 2020-11-27 23:57 */ public class Apple { private String title; private String color; private String origin; public Apple() { } public Apple(String title, String color, String origin) { this.title = title; this.color = color; this.origin = origin; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public String getOrigin() { return origin; } public void setOrigin(String origin) { this.origin = origin; } }
applicationContext.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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 在IoC容器启动时,自动由Spring实例化Apple对象,取名sweetApple放入到容器中--> <bean id="sweetApple" class="com.imooc.spring.ioc.entity.Apple"> <property name="title" value="红富士"></property> <property name="origin" value="欧洲"></property> <property name="color" value="红色"></property> </bean> <bean id="sourApple" class="com.imooc.spring.ioc.entity.Apple"> <property name="title" value="青苹果"></property> <property name="origin" value="中亚"></property> <property name="color" value="绿色"></property> </bean> <bean id="softApple" class="com.imooc.spring.ioc.entity.Apple"> <property name="title" value="金帅"></property> <property name="origin" value="中国"></property> <property name="color" value="金色"></property> </bean> <bean id="rdApple" class="com.imooc.spring.ioc.entity.Apple"> <property name="title" value="蛇果"></property> <property name="origin" value="美国"></property> <property name="color" value="红色"></property> </bean> <bean id="lily" class="com.imooc.spring.ioc.entity.Child"> <property name="name" value="莉莉"></property> <property name="apple" ref="softApple"></property> </bean> <bean id="andy" class="com.imooc.spring.ioc.entity.Child"> <property name="name" value="安迪"></property> <property name="apple" ref="rdApple"></property> </bean> <bean id="luna" class="com.imooc.spring.ioc.entity.Child"> <property name="name" value="露娜"></property> <property name="apple" ref="sweetApple"></property> </bean> </beans>
SpringApplication.java
package com.imooc.spring.ioc; import com.imooc.spring.ioc.entity.Apple; import com.imooc.spring.ioc.entity.Child; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author CubeMonkey * @create 2020-11-30 16:14 */ public class SpringApplication { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); Apple sweetApple = context.getBean("sweetApple", Apple.class); System.out.println(sweetApple.getTitle()); //从Ioc容器中提取beanId = lily的对象 Child lily = context.getBean("lily", Child.class); lily.eat(); Child andy = context.getBean("andy", Child.class); andy.eat(); Child luna = context.getBean("luna", Child.class); luna.eat(); } }
XML管理对象(Bean)
三种配置方式
1. 基于XML配置Bean
实例化Bean的三种方式
-
基于构造方法对象实例化
核心代码:
applicationContext.xml
<bean id="apple2" class="com.imooc.spring.ioc.entity.Apple"> <constructor-arg name="title" value="红富士"></constructor-arg> <constructor-arg name="color" value="红色"></constructor-arg> <constructor-arg name="origin" value="欧洲"></constructor-arg> </bean>
-
基于静态工厂实例化
核心代码:
applicationContext.xml
<!--利用静态工厂获取对象--> <bean id="apple4" class="com.imooc.spring.ioc.factory.AppleStaticFactory" factory-method="createSweetApple"></bean>
AppleStaticFactory.java
package com.imooc.spring.ioc.factory; import com.imooc.spring.ioc.entity.Apple; /** * 静态工厂通过静态方法创建对象,隐藏创建对象的细节 * @author CubeMonkey * @create 2020-12-01 18:50 */ public class AppleStaticFactory { public static Apple createSweetApple(){ //logger.info("") Apple apple = new Apple(); apple.setTitle("红富士"); apple.setOrigin("欧洲"); apple.setColor("红色"); return apple; } }
-
基于工厂实例方法实例化
核心代码:
applicationContext.xml
<!--利用工厂实例方法获取对象--> <bean id="factoryInstance" class="com.imooc.spring.ioc.factory.AppleFactoryInstance"></bean> <bean id="apple5" factory-bean="factoryInstance" factory-method="createSweetApple"></bean>
AppleFactoryInstance.java
package com.imooc.spring.ioc.factory; import com.imooc.spring.ioc.entity.Apple; /** * 工厂实例方法创建对象是指IoC容器对工厂类进行实例化并调用对应的实例方法创建对象的过程 * @author CubeMonkey * @create 2020-12-01 18:56 */ public class AppleFactoryInstance { public Apple createSweetApple(){ Apple apple = new Apple(); apple.setTitle("红富士"); apple.setOrigin("欧洲"); apple.setColor("红色"); return apple; } }
2. 基于注解配置Bean
3. 基于Java代码配置Bean
从IoC容器获取bean
方法:
Apple apple4 = context.getBean("apple4", Apple.class);//推荐 System.out.println(apple4.getTitle()); Apple apple3 = (Apple)context.getBean("apple3"); System.out.println(apple3.getTitle());
id与name属性相同点
- bean id 与name都是设置对象在IoC容器中唯一标识
- 两者在同一配置文件中都不允许出现重复
- 两者允许在多个配置文件中出现重复,新对象覆盖旧对象
id与name属性区别
- id要求更为严格,一次只能定义一个对象标识(推荐)
- name更为宽松,一次允许定义多个对象标识
- tips:id与name的命名有意义,按驼峰命名书写
路径表达式
加载单个配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
加载多配置文件
String[] configLocations = new String[]{"classpath:applicationContext.xml", {"classpath:applicationContext-1.xml"}};
ApplicationContext context = new ClassPathXmlApplicationContext(configLocations)
路径表达式说明
表达式实例 | 说明 |
---|---|
classpath:config.xml | 扫描classpath根路径(不包含jar)的config.xml |
classpath:com/imooc/config.xml | 扫描classpath下(不包含jar)com.imooc包中的config.xml |
classpath*:com/imooc/config.xml | 扫描classpath下(包含jar)com.imooc包中的config.xml |
classpath:config-*.xml | 扫描classpath根路径下所有以config-开头的XML文件 |
classpath:com/**/config.xml | 扫描com包下(包含任何子包)的config.xml |
file:c/config.xml | 扫描c盘根路径config.xml |
依赖注入
对象依赖注入
-
依赖注入是指运行时将容器内对象利用反射赋给其他对象的操作
-
基于setter方法注入对象
<bean name="sweetApple" class="com.imooc.spring.ioc.entity.Apple"> <!-- IoC容器自动利用反射机制运行时调用setXxx方法为属性赋值--> <property name="title" value="红富士"></property> <property name="color" value="红色"></property> <property name="origin" value="欧洲"></property> <property name="price" value="123"></property> </bean> <bean name="lily" class="com.imooc.spring.ioc.entity.Child"> <property name="name" value="莉莉"></property> <!--利用ref注入依赖对象--> <property name="apple" ref="sweetApple"></property> </bean>
-
基于构造方法注入对象
<bean id="sourApple" class="com.imooc.spring.ioc.entity.Apple"> <property name="title" value="青苹果"></property> <property name="color" value="绿色"></property> <property name="origin" value="中亚"></property> <property name="price" value="9.8"></property> </bean> <bean id="andy" class="com.imooc.spring.ioc.entity.Child"> <constructor-arg name="name" value="安迪"></constructor-arg> <constructor-arg name="apple" ref="sourApple"></constructor-arg> </bean>
集合依赖注入
注入List
<bean id = "..." class = "...">
<property name="someList">
<list>
<value>具体值</value>
<ref bean="beanId"></ref>
</list>
</property>
</bean>
注入Set
<bean id = "..." class="...">
<property name="someSet">
<set>
<value>具体值</value>
<ref bean="beanId"></ref>
</set>
</property>
</bean>
注入Map
<bean id="..." class="...">
<property name="someMap">
<map>
<entry key="k1" value="v1"></entry>
<entry key="k2" value-ref="beanId"></entry>
</map>
</property>
</bean>
注入Properties
<bean id="..." class="...">
<properties name="someProperties">
<props>
<prop key="k1">v1</prop>
<prop key="k2">v2</prop>
</props>
</properties>
</bean>
查看容器内对象
String[] beanNames = context.getBeanDefinitionNames();
bean scope属性
- bean scope属性用于决定对象何时被创建与作用范围
- bean scope配置将影响容器内对象的数量
- bean scope默认值singleton(单例),指全局共享同一个对象实例
bean scope属性清单
scope属性 | 说明 |
---|---|
singleton | 单例(默认值),每一个容器有且只有唯一的实例,实例被全局共享 |
prototype | 多例,每次使用时都是创建一个实例 |
request | web环境下,每一次独立请求存在唯一实例 |
session | web环境下,每一个session存在有唯一实例 |
applicaiton | web环境下,每一个ServletContext存在唯一实例 |
websocket | 每一次WebSocket连接中存在唯一实例 |
singleton与prototype对比
singleton | prototype | |
---|---|---|
对象数量 | 全局唯一 | 存在多个 |
实例化时机 | IoC容器启动时 | getBean()或对象注入时 |
线程安全问题 | 存在 | 不存在 |
执行效率 | 高 | 低 |
bean生命周期
Ioc容器极简实现
示例代码:
package com.imooc.spring.ioc.context;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**
* @author Rex
* @create 2020-12-31 16:08
*/
public class ClassPathXmlApplicationContext implements ApplicationContext{
private Map iocContainer = new HashMap();
public ClassPathXmlApplicationContext(){
try {
String filePath = this.getClass().getResource("/applicationContext.xml").getPath();
filePath = new URLDecoder().decode(filePath, "utf-8");
SAXReader reader = new SAXReader();
Document document = reader.read(new File(filePath));
List<Node> nodes = document.getRootElement().selectNodes("bean");
for (Node node : nodes) {
Element ele = (Element) node;
String id = ele.attributeValue("id");
String className = ele.attributeValue("class");
Class<?> c = Class.forName(className);
Object obj = c.newInstance();
List<Node> properties = ele.selectNodes("property");
for (Node p : properties) {
Element property = (Element) p;
String propName = property.attributeValue("name");
String propValue = property.attributeValue("value");
String setMethodName = "set"+propName.substring(0, 1).toUpperCase() + propName.substring(1);
System.out.println("准备执行"+setMethodName+"方法注入数据");
Method setMethod = c.getMethod(setMethodName, String.class);
setMethod.invoke(obj, propValue);
}
iocContainer.put(id, obj);
}
System.out.println(iocContainer);
System.out.println("IOC容器初始化完毕");
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public Object getBean(String beanId) {
return iocContainer.get(beanId);
}
}