IOC
概述
1、通过控制反转,对象在被创建时,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它,即依赖被注入到对象中
2、在 Spring 中,控制反转,把对象创建和对象之间的调用过程,交给 Spring 进行管理
3、目的:降低耦合度
4、底层原理:xml 解析、工厂模式、反射
IOC 接口
1、IOC 基于 IOC 容器完成,IOC 容器底层即对象工厂
2、Spring 两个接口实现 IOC 容器
3、BeanFactory 接口
(1)IOC 容器基本实现,Spring 内部的使用接口,不提供开发人员进行使用
(2)加载配置文件时,不会创建对象,在获取对象(使用)才创建对象
4、ApplicationContext 接口
(1)BeanFactory 接口的子接口,提供更多功能,用于开发
(2)加载配置文件时,就创建配置文件对象
(3)Java 工程,创建 ApplicationContext 对象;Web 工程,创建 WebApplicationContext 对象
Bean 管理
1、两个操作
(1)Spring 创建对象
(2)Spring 注入属性,即依赖注入:DI(Dependency Injection)
2、实现方式
(1)基于 xml 配置文件方式
(2)基于注解方式
基于 xml 配置文件方式
1、创建对象
(1)示例
<bean id="user" class="com.spring.User"></bean>
(2)在 Spring 配置文件中,使用 bean 标签,标签里面添加对应属性,就可以实现对象创建
(3)id 属性:唯一标识,不可添加特殊字符
(4)name 属性:唯一标识,已弃用,可以添加特殊字符
(5)class 属性:类的全路径(包类路径)
(6)创建对象时,默认执行无参构造器
2、注入属性
(1)set 方法(示例)
<bean id="user" class="com.spring.User">
<property name="account" value="001"></property>
</bean>
(2)有参构造器(示例)
<bean id="user" class="com.spring.User">
<constructor-arg name="account" value="001"></constructor-arg>
<constructor-arg index="1" value="002"></constructor-arg>
</bean>
(3)name 属性:类中属性名
(4)value 属性:注入属性指值
(5)index 属性:有参构造器的索引值,从 0 开始,表示第 1 个参数
3、p 名称空间注入
(1)简化 xml 配置
(2)实际开发中较少使用
(3)在配置文件中,添加 p 名称空间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
(4)注入属性
<bean id="user" class="com.spring.User" p:account="001"></bean>
xml 注入其他类型属性
1、set 方法示例,有参构造器同理
2、字面量:用于表达源代码中一个固定值的表示法
(1)null 值
<bean id="user" class="com.spring.User">
<property name="account">
<null/>
</property>
</bean>
(2)属性值包含特殊符号
(3)方式一:进行转义
<bean id="user" class="com.spring.User">
<property name="account" value="<001>"></property>
</bean>
(4)方式二:带特殊符号的内容写到 CDATA
<bean id="user" class="com.spring.User">
<property name="account" value="<001>">
<value><![CDATA[<001>]]></value>
</property>
</bean>
3、外部 Bean:直接在 beans 标签内部直接定义的 Bean 对象,外部 Bean 可以被多个 Bean 对象引用
(1)示例
class Service {
private Dao dao;//聚合DAO对象
public void setDao(Dao dao) {
this.dao = dao;
}
public void invokeDao() {
System.out.println("调用DAO对象");
}
}
class Dao {
public void method() {
System.out.println("DAO方法被调用");
}
}
<bean id="user" class="Service">
<property name="dao" ref="Dao"></property>
</bean>
<bean id="Dao" class="Dao"></bean>
(2)ref 属性:创建 Dao 对象的 bean 标签的 id 值
4、内部 Bean:在某个 bean 标签的内部定义的 Bean 对象,内部 Bean 只能被某个对象的某个属性引用
(1)一对多(示例)
class Department {
private String departmentName;
public void setDepartmentName(String departmentName) {
this.departmentName = departmentName;
}
}
class Employee {
private int employeeId;
private Department department;//聚合Department对象
public void setEmployeeId(int employeeId) {
this.employeeId = employeeId;
}
public void setDepartment(Department department) {
this.department = department;
}
}
<bean id="employee" class="Employee">
<property name="employeeId" value="001"></property>
<property name="department">
<bean id="dept" class="Department">
<property name="departmentName" value="部门"></property>
</bean>
</property>
</bean>
(2)级联赋值:在一个 Bean 对象中注入另一个外部 Bean 对象的属性,并对该属性进行赋值
(3)方式一:先在外部 Bean 中注入属性后,再引用
<bean id="employee" class="Employee">
<property name="employeeId" value="001"></property>
<property name="department" ref="department"></property>
</bean>
<bean id="department" class="Department">
<property name="departmentName" value="部门"></property>
</bean>
(4)方式二:直接引用外部 Bean 的属性
<bean id="employee" class="Employee">
<property name="employeeId" value="001"></property>
<property name="department" ref="department"></property>
<!-- Employee中必须有getDepartment方法 -->
<property name="department.departmentName" value="部门"></property>
</bean>
<bean id="department" class="Department"></bean>
5、集合属性:数组、List、Map(示例)
(1)注入一般类型
class Collection {
private String[] array;
private List<String> list;
private Map<String, String> map;
public void setArray(String[] array) {
this.array = array;
}
public void setList(List<String> list) {
this.list = list;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
}
<!-- 注入集合类型属性 -->
<bean id="collection" class="Collection">
<!-- 注入数组类型属性 -->
<property name="array">
<array>
<value>数组元素</value>
</array>
</property>
<!-- 注入List类型属性 -->
<property name="list">
<list>
<value>List元素</value>
</list>
</property>
<!-- 注入Map类型属性 -->
<property name="map">
<map>
<entry key="键" value="值"></entry>
</map>
</property>
<!-- 注入Set类型属性 -->
<property name="set">
<set>
<value>Set元素</value>
</set>
</property>
</bean>
(2)注入对象类型值
class Collection {
private List<Element> list;
public void setList(List<Element> list) {
this.list = list;
}
}
class Element{
private String name;
public void setName(String name) {
this.name = name;
}
}
<bean id="collection" class="Collection">
<!-- 注入List类型属性 -->
<property name="list">
<list>
<ref bean="element1"></ref>
<ref bean="element2"></ref>
</list>
</property>
</bean>
<bean id="element1" class="Element">
<property name="name" value="元素1"></property>
</bean>
<bean id="element2" class="Element">
<property name="name" value="元素2"></property>
</bean>
(3)提取集合类型属性,注入为新集合
<!-- 引入名称空间util -->
<?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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
</beans>
<!-- util标签提取 -->
<util:list id="refine">
<!-- ref标签可以引入对象 -->
<value>元素1</value>
</util:list>
<bean id="collection" class="Collection">
<property name="list" ref="refine"></property>
</bean>
Spring 两种 Bean
1、普通 Bean:配置文件中的定义类型,与返回类型一致
2、工厂 Bean
(1)配置文件中的定义类型,与返回类型可以不一致
(2)实现 FactoryBean 接口
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
// 获取泛型T的实例,用来创建Bean
@Nullable
T getObject() throws Exception;
//获取 T getObject()中的返回值 T 的具体类型,如果T是一个接口,返回其具体实现类的类型
@Nullable
Class<?> getObjectType();
//判断创建的bean是否是单例
default boolean isSingleton() {
return true;
}
}
Bean 作用域
1、在 Spring 中,默认情况下,Bean 是单例对象
2、在 Spring 配置文件中,bean 标签中的属性 scope 用于设置单例 / 多例
3、scope 标签值
(1)singleton,默认值,加载 Spring 时,就会创建该单例对象
(2)prototype,不是在加载 Spring 时创建对象,而是在调用 getBean() 时创建多实例对象
(3)request:不使用,表示 Web 的请求,每次创建的对象放到 request 域中
(4)session:不使用,表示 Web 的会话,每次创建的对象放到 session 域中
Bean 生命周期
1、通过无参构造器创建 Bean 实例
2、为 Bean 的属性,调用 set 方法,设置值和引用其他 Bean
3、把 Bean 实例传递到 Bean 后置处理器的 postProcessBeforeInitialization()
4、调用 Bean 的初始化方法
(1)需要进行配置初始化方法
(2)bean 标签,init-method 属性,值为初始化方法名
5、把 Bean 实例传递到 Bean 后置处理器的 postProcessAfterInitialization()
6、获取到 Bean 对象,可以使用
7、当容器关闭时,调用 Bean 销毁方法
(1)需要进行配置销毁方法
(2)bean 标签,destroy-method 属性,值为销毁方法名
Bean 后置处理器
1、实现 BeanPostProcessor 接口
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
xml 自动装配
1、根据指定装配规则(属性名称 / 属性类型),Spring 自动注入匹配的属性值
2、bean 标签,autowire 属性,配置自动装配
(1)byName:根据属性名称注入,注入值 Bean 的 id 值和类中属性名相同
(2)byType:根据属性类型注入,不能存在多个相同类型的 Bean
xml 引入外部属性文件
1、引入 .properties 属性文件,到 Spring 配置文件
(1)引入 context 名称空间
<?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 http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
(2)配置 Druid(示例)
<!-- 引入外部属性文件 -->
<context:property-placeholder location="classpath:配置文件名.properties"/>
<!-- 配置连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!-- ${}中的内容,自动匹配配置文件的key,把对应value覆盖填充到""中 -->
<property name="url" value="${jdbc_url}" />
<property name="username" value="${jdbc_user}" />
<property name="password" value="${jdbc_password}" />
<property name="filters" value="stat" />
<property name="maxActive" value="20" />
<property name="initialSize" value="1" />
<property name="maxWait" value="6000" />
<property name="minIdle" value="1" />
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="poolPreparedStatements" value="true" />
<property name="maxOpenPreparedStatements" value="20" />
<property name="asyncInit" value="true" />
</bean>
注解
1、代码特殊标记
2、格式:@注解名称(属性名称=属性值, 属性名称=属性值……)
3、作用域(位置):类上、方法上、属性上
4、目的:简化 xml 配置
基于注解方式创建对象
1、注解
(1)@Component:普通组件
(2)@Service:Service 层
(3)@Controller:Web 层
(4)@Repository:DAO 层
(5)四个注解功能相同,实际没有严格规定,而是建议使用位置
2、步骤
(1)引入依赖 jar 包:spring-aop:包含在应用中使用 Spring 的 AOP 特性时,所需的类和源码级元数据支持
(2)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"
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 http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启组件扫描
base-package属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包里及其子包中的所有类
如果扫描多个包,使用逗号分隔路径
-->
<context:component-scan base-package="包路径"></context:component-scan>
</beans>
(3)或注解 + 配置类开启组件扫描
//表示配置类
@Configuration
//开启组件扫描
@ComponentScan(basePackages = {"包的路径"})
class SpringConfig {
}
(4)创建类,类上添加注解
//value等价于bean标签中的id值,默认值为类名,首字母小写
@Component(value = "module")
class Module{
}
3、组件扫描匹配
(1)use-default-filters:true 表示使用默认 filter;false 表示不使用默认 filter,使用自定义配置 filter
(2)context:include-filter:设置扫描哪些内容
<context:component-scan base-package="包路径" use-default-filters="false">
<!-- 只扫描包路径下,含Component注解的类 -->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>
(3)context:include-filter:设置不扫描哪些内容
<context:component-scan base-package="包路径">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>
基于注解方式注入属性
1、@Autowired
(1)根据属性类型进行自动装配
(2)不需要 set 方法
class Service {
@Autowired
private Dao dao;
}
class Dao {
}
(3)不配合 @Qualifier 使用,存在多个相同类型的 Bean,则报错
(4)required 属性:默认 true,没有找到对应 Bean,则报错
2、@Qualifier
(1)根据属性名注入
(2)需要与 @Autowired 搭配使用,不需要 set 方法
class Service {
@Autowired
@Qualifier(value = "对象实例名")
private Dao dao;
}
class Dao {
}
3、@Resource
(1)不需要 set 方法
(2)该注解并非 Spring 框架,而是 javax.annotation.Resource
(3)根据属性类型注入
class Service {
@Resource
private Dao dao;
}
class Dao {
}
(4)根据属性名注入
class Service {
@Resource(name = "对象实例名")
private Dao dao;
}
class Dao {
}
(5)如果 @Resource 指定 name,则只会按照 name 进行查找,当找不到时抛出异常,找到则将 Bean 注入
(6)如果 @Resource 没有指定 name,则把属性名作为名字进行查找,找到则将 Bean 注入,当按照名字查找不到时,按照类型进行查找
4、@Value
(1)注入普通类型属性
(2)不需要 set 方法
class Service {
@Value(value = "注入普通属性值")
private String name;
}
测试类 @Test
1、xml 文件
ApplicationContext context = new ClassPathXmlApplicationContext("文件名.xml");
类名 对象名 = context.getBean("对象名", 类名.class);
2、注解
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
类名 对象名 = context.getBean("对象名", 类名.class);
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战