3 -Spring框架 之 IOC

本文深入讲解Spring框架的核心概念,包括其历史背景、主要功能、体系结构及如何利用IOC和AOP降低代码耦合度。此外,还介绍了Spring的依赖注入机制、Bean的生命周期管理以及配置文件的使用技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    1. Spring 框架概述

 

  1. Spring是个容器,(2)用于降低代码间的耦合度,(3)根据不同的代码它采用的IOC和AOP两种技术进行解耦合。

 

Spring是于2003年兴起的一个轻量级的java开发框架,它是为了解决企业应用开发的复杂性而创建的。Spring的核心是控制反转(IOC)和面向切面编程(AOP)。简单来说,Spring是一个分层的javaSE/EE full-stack(一站式)轻量级开源框架。

 

Spring的主要作用就是为代码”解耦”,降低代码间的耦合度。

根据功能的不同,可以将一个系统中的代码分为主业务逻辑系统级业务逻辑两类。

 

Spring根据代码的功能特点,将降低耦合度的方式分为两类:IOC和AOP。IOC使得主要业务在相互调用过程中,不用再自己维护关系了,即不用再自己创建要使用的对象了。而是由Spring容器统一管理,自动“注入”。而AOP使得系统级服务得到了最大复用,且不用再由程序员手工将系统级服务“混杂”到主业务逻辑中了。而是由Spring容器统一完成 ”织入”。

 

 

 

    1.  Spring体系结构

 

Spring由20多个模块组成,它们可以分为数据访问/集成(Data Access/Integration)、Web 、面向切面编程(AOP Aspects)、应用服务器设备管理(Instrumentation)、消息发送(Messaging)、核心容器(Core container)和测试(Test)。

    1.  Spring的下载

官网:http://spring.io

    1. Spring的特点
      1. 非侵入式

所谓非侵入式是指,Sping框架的API不会在业务逻辑上出现,即业务逻辑是POJO。由于业务逻辑中没有Spring的API,所以业务逻辑可以从Spring框架快速的移植到其他框架。

POJO:Plain Old Java Object(最普通最老的java对象)

      1. 容器

Spring作为一个容器,可以管理对象的生命周期、对象与对象之间的依赖关系。可以通过配置文件,来定义对象,以及设置与其他对象的依赖关系。

      1. IOC

控制反转(Inersion of Control),即创建被调用者的实例不是由调用者完成,而是由Spring容器完成,并注入调用者。

      1. AOP

面向切面编程(AOP,Aspect Orient Peogramming),是一种编程思想,是面向对象编程OOP的补充。很多框架都实现了对AOP编程思想的实现。Spring也提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行开发。

可以把日志、安全、事务管理等服务理解成一个”切面”。

 

2.1 Spring与IOC

控制反转(IOC,Inversion of Control),是一个概念,是一种思想,指将传统上由程序代码直接控制的对象调用权交给容器,通过容器来实现对象的装配和管理,控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。

IOC是一个概念,是一种思想,其实现方式多种多样,当前比较流行的实现方式由两种,依赖注入和依赖查找。

依赖查找:Dependency lookup,DL,容器日工回调接口和上下文环境给组件,程序代码则需要提供集体的查找方式,比较典型的是依赖于JNDI服务接口(Java Naming and Directory Interface)的查找。

依赖注入:Dependency Injection,DI,程序代码不做定位查询,这些工作由容器自行完成。

依赖注入DI是指程序运行过程中,若需要调用另一个对象协助时,无需在代码中创建被调用者,而是依赖于外部容器,由外部容器创建后传递给程序。

Sping的依赖注入对调用者与被调用者几乎没有任何要求,完全支持POJO之间依赖关系的系统。

依赖注入时目前最优秀的解释方式,依赖注入让Spring的Bean之间以配置文件的方式组织在一起,而不是以硬编码的方式耦合在一起的。

 

2.2 IOC的使用

XML文件的配置,对bean进行注册

 

创建容器对象,加载Sping配置文件(三种方式) 

 

动态代码块 

 

 

  1. 动态代码块执行之前已经分配内存并创建对象
  2. 一个空对象(如:Object obj = new Object(),里面没有成员变量)在堆内存中占8个字节
  3. 引用型的变量占的字节数量是不确定的,跟机器寻址有关

 

BeanFactory容器和ApplicationContext的区别:

  1. 这两个容器对于其中bean的创建时机不同
  2. ApplicationContext容器在进行初始化的时候,会将其中的 所有Bean对象进行创建
    1. 缺点:占用系统资源(内存,cpu)
    2. 优点:响应速度块

3、BeanFactory:在执行getBean()方法的时候,即获取的时候创建的对象

    1. 缺点:相对来说响应速度慢
    2. 优点:不多占用系统资源

2.3 Bean的装配

Bean的装配,即Bean对象的创建,容器根据代码要求创建Bean对象后再传递给代码的过程,称为Bean的装配。

2.3.1 默认装配方式

代码通过getBean()方式容器获取指定的Bean实例,容器首先会调用Bean类的无参构造器,创建空值的实例对象。

 

是要给出有参构造器,就需要写出无参构造器。(因为底层使用反射机制

 

2.3.2 动态工厂Bean

有些时候,项目中需要通过工厂类来创建Bean实例,而不能向前面例子中似的,直接由Spring容器来装配Bean实例。使用工厂模式创建Bean实例,就会使工厂类与要创建的Bean类耦合到一起。

 

  1. 将动态工厂Bean作为普通Bean使用

在配置文件中注册过动态工厂Bean后,测试类直接通过getBean()获取到工厂对象,再由工厂对象调用其响应方法创建相应的目标对象,配置文件中无需注册目标对象的Bean,因为目标对象的创建不由Spring容器来管理。XML文件配置:

 

工厂类中的返回SomeServiceImpl的对象: 

 

 

对于类中的静态方法,一般是用:类名.方法名();

当使用:对象.方法名();的时候,无论这个对象是new过的还是null的都会被装成:类名.方法名(); 多以当使用一个null对象调用静态方法的时候是不会报空指针异常的。

 

(2)静态工厂bean的使用

 

使用静态方法返回实现类的对象。就是把上面的getSomeService方法改成static的了。

 

2.3.3 容器中Bean的作用域

当通过Spring容器创建一个Bean实例时,不仅可以完成Bean的实例化,还可以通过scope属性,为Bean指定特定的作用域,Spring支持5中作用域。

  1. singleton:单例模式。即在整个Spring容器中,使用singleton定义的Bean将是单例的,只有一个实例,默认值。
  2. Prototype:原型模式。即每次使用getBean方法获取的同一个<bean/>的实例都是一个新的实例。
  3. Request:对于每次HTTP请求,都将会产生一个不同的Bean实例。
  4. Session:对于每个不同的HTTP session,都将产生一个不同的Bean实例。
  5. 对于scope的值request、session与global session,只有在web应用中使用Spring时该作用域才有效。
  6. 对于scope为singleton的单例模式。该Bean是在容器被创建的时候即被装配好了。
  7. 对于scope为prototype的原型模式。Bean实例是在代码中使用该Bean实例时才进行装配的。

 

2.3.4 Bean后处理器

Bean后处理器是一种特殊的Bean,容器中所有的Bean在初始化的时候,均会自动执行类的两个方法。由于该Bean是由其他Bean自动调用执行的,不是程序员手工调用的,因此Bean无需id属性。

 

需要做的是,在Bean后处理器类方法中,只要对Bean类与Bean类中的方法进行判断,就可以实现对指定的Bean的指定方法功能扩展与增强。方法返回的Bean对象,即是增过的对象。

代码中需要自定义Bean后处理器类,该类就实现了接口BeanPostProcessor的类,该接口中包含两个方法,分别在目标Bean初始化完毕之前与之后执行,它们返回值为:功能被扩展或增强后的Bean对象。

 

 

使用动态代理对类中的方法返回值进行增强:

 

2.3.5 定制Bean的生命始末

可以为Bean定制初始化后的生命行为,也可以为Bean定制销毁前的生命行为。

首先,这些方法需要在Bean类中事先定义好:是方法名随意放入public void方法。

 

 

2.3.6 Bean的生命周期

Bean的生命周期有11个时间点。

  1. 实现类的无参构造器
  2. 实现类的setter方法,在bean中注入
  3. 实现类实现了BeanNameAware接口,执行重写方法setBeanName(),获取BeanName的方法
  4. 实现类实现了BeanFactoryAware接口,执行重写方法setBeanFactory()。获取工厂对象
  5. 执行bean后处理器的before的方法
  6. 实现类实现接口InitializingBean,执行重写方法afterPropertiesSet(),表示属性设置完毕了
  7. 执行实现类的初始化方法,即上面所说的生命始末的开始
  8. 执行Bean后处理器的after
  9. 执行程序的主业务
  10. 实现类实现DisposableBean接口,重写方法detory()方法,表示销毁之前。这里不是真正的销毁,销毁是由spring容器管理的。
  11. Spring容器关闭,执行生命始末的末尾方法

 

2.4 基于XML的DI

2.4.1 注入分类

 

Bean 实例在调用无参构造器创建了空值对象后,就要对bean对象的属性进行初始化。初始化是由容器自动完成的。称为注入,根据注入方式的不同,常用的有两类:设值注入、构造注入。

还有另一种,实现特定接口注入,由于这种方式采用浸入式编程,污染了代码,所以几乎不用。

 

  1. 设值注入

设值注入是指,通过setter方法传入被调用者的实例,这种注入方式简单、直观,因而在Spring的依赖注入中大量使用。

 

 

(2)构造注入

构造注入是指,在构造调用者实例的同时,完成被调用者的实例化,使用构造器设置依赖关系。

 

 

 

 

 2.4.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"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd">
        
    <bean id="mySchool" class="edu.sdut.di2.School" >
    	<property name="name" value="大学"/>
    </bean>

	 <bean id="mySchool2" class="edu.sdut.di2.School" >
    	<property name="name" value="中学"/>
    </bean>
    
    <bean id="mySome" class="edu.sdut.di2.Some" >
    	<property name="schools">
    		<array>
    			<ref bean="mySchool"/>
    			<ref bean="mySchool2"/>
    		</array>
    	</property>
    	<property name="myStrs">
    		<array>
    			<value>中国</value>
    			<value>北京</value>
    		</array>
    	</property>
    	<property name="muList">
    		<list>
    			<value>List1</value>
    			<value>List2</value>
    		</list>
    	</property>
    	
    	<property name="mySet">
    		<set>
    			<value>set1</value>
    			<value>set2</value>
    		</set>
    	</property>
    	
    	<property name="myMap">
    		<map>
    			<entry key="mobile" value="1234567"></entry>
    			<entry key="mobile" value="5555555"></entry>
    		</map>
    	</property>
    	<property name="MyPros">
    		<props>
    			<prop key="education">大学</prop>
    			<prop key="gender">man</prop>
    		</props>
    	</property>
    </bean>
    
</beans>

下面这种简写方式只适合存储字符串的时候。 

 

2.4.3 对于域属性的自动注入

对于域属性的注入,也可不在配置文件中显示的注入,可以通过为<bean/>标签设置autowire属性值,为域属性进行隐式自动注入,根据自动注入判断标准的不同,可以分为两种:

 

byName:根据名称自动注入

byType:根据类型自动注入

 

 

  1. byName方式自动注入

 

当配置文件中被调用者Bean的id值与代码中调用者Bean类型的属性名相同时,可使用byName方式,让容器自动将被调用者Bean注入给调用者Bean。容器是通过调用者的Bean类的属性名与配置文件的被调用者bean的id进行比较而实现自动注入的。

 

  1. byType方式自动注入

 

2.4.5 使用SPEL注入

SPEL,Spring Expression Language,即Spring EL表达语言。即,在Spring配置文件中为Bean的属性注入值时,可以直接使用SPEL表达式计算结果。SPEL表达式以 # 开头,后跟一对大括号。

用法:<bean id = “abc” value=”#{}”>

 

 2.4.6 同类抽象bean

 

 

2.4.7 异步抽象类 

 

2.4.8 为应用指定多个Spring配置文件

在实际应用里,随着应用规模的增加,系统中Bean数量也大量增加,导致配置文件变得非常庞大、臃肿。为了避免这种情况的产生。提高配置文件的可读性和可维护性,可以将Spring配置文件分解成多个配置文件。

 

  1. 平等关系的配置文件

将配置文件分解为地位平等的多个配置文件,并将所有配置文件的路径定义为一个String数组,将其作为容器初始化参数出现。其将与可变参数构造器匹配。

 

 

  1. 包含关系的配置文件

 

在xml配置文件中使用标签<import resource=其他xml文件的全名>

如果其他的文件包路径下,需要加classpath:

如:<import resource=classpath:其他xml文件的全名>

 

如果有多个import的话也可以使用*通配符,这时需要注意通配符匹配的xml文件不能包含自己,否则会稻城递归。

 

 

 

 

2.5 基于注解的DI

对于DI使用注解,将不再需要在Spring配置文件中声明Bean实例。Spring中使用注解,需要在原有Spring运行环境基础上再做一些改变,即:

  1. 导入AOP的jar包,因为注解的后台实现用到了AOP编程
  2. 需要更换配置头文件,即添加相应的约束

 

在xml中配置

 

 

持久化类中实现注解 


/*
 * 与@Component注解功能相同,但是意义不同的还有三个:
 * 		@Repository:注解在Dao实现类上
 * 		@Service:注解在Service实现类上
 * 		@Controller:注解在SpringMVC的处理器上
 */
@Scope("prototype")   //默认是singleton
@Component("mySchool") //组件,表示当前类被Spring容器所管理
public class School {
	@Value("xxx大学") //对属性进行注入
	private String name;

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "School [name=" + name + "]";
	}
}

 2.5.1  对域属性使用注解注入

2.5.2 域属性注解@Resource

Spring提供了对JSR-250规范中定义@Resource标准注解的支持,@Resource注解即可以按名称匹配Bean,也可以按类型匹配Bean,使用该注解,要求JDK必须是6以上。

 

  1. 按类型注入域属性

@Resource注解若不带任何参数,则会按照类型进行Bean的匹配注入。

 

  1. 按名称注入域属性 

@Resource注解指定其name属性,则name的值即为按照名称进行匹配的Bean的id。

 

 

2.5.3 Bean的声明始末@PostConstruct 与 @PreDestory

在方法上使用@PostConstruct,与原来的init-method等效,在方法上使用@PreDestory,与destory-method等效。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值