spring特技

Spring中有3个核心的概念:控制反转(Ioc)、依赖注入(DI)、面向切面编程(AOP)

什么是依赖关系?

当a对象完成某些操作需要调用b对象中的方法来实现时,说明a依赖于对象b,a和b是依赖关系。

创建对象之前,需要先将被依赖对象通过new的方式创建好,然后将其传递给B,这些工作都是B的使用者自己去做的,所有对象的创建都是由使用者自己去控制的,弊端上面也说了,代码量也比较大,代码耦合度比较高(依赖有调整,改动也比较大),也不利于扩展。

那么有没有更好的方式来解决这些问题呢?
上面B对象以及B依赖的对象都是使用者自己主动去控制其创建的,能不能找一个第三方来把这个事情给做了,比如给第三方一个清单,清单中告诉第三方我需要用到B对象以及B需要依赖的对象,然后由这个第三方去负责创建和组装B对象,使用者需要使用B对象的时候,只需要向第三方发起一个查找,如果第三方那边有B对象,直接将其内部组装好的B对象返回就可以了,整个系统中所有需要用到的对象都可以列个清单,让第三方帮忙创造,用的时候只需要向第三方索取就可以了,当B中依赖的对象有新增或者删除的时候,只需要去调整一下清单就可以了,这个事情spring已经帮我们实现了

spring容器

我们的程序启动的时候会创建spring容器,会给spring容器一个清单,清单中列出了需要创建的对象以及对象依赖关系,spring容器会创建和组装好清单中的对象,然后将这些对象存放在spring容器中,当程序中需要使用的时候,可以到容器中查找获取,然后直接使用.

spring容器:主要负责容器中对象的创建、组装、对象查找、对象生命周期的管理等等操作。

IOC:控制反转

使用者之前使用B对象的时候都需要自己去创建和组装,而现在这些创建和组装都交给spring容器去给完成了,使用者只需要去spring容器中查找需要使用的对象就可以了;这个过程中B对象的创建和组装过程被反转了,之前是使用者自己主动去控制的,现在交给spring容器去创建和组装了,对象的构建过程被反转了,所以叫做控制反转;IOC是是面相对象编程中的一种设计原则,主要是为了降低系统代码的耦合度,让系统利于维护和扩展。

IOC控制反转,是一种设计理念,将对象创建和组装的主动控制权利交给了spring容器去做,控制的动作被反转了,降低了系统的耦合度,利于系统维护和扩展,主要就是指需要使用的对象的组装控制权被反转了,之前是自己要做的,现在交给spring容器做了。

DI:依赖注入

依赖注入是spring容器中创建对象时给其设置依赖对象的方式,比如给spring一个清单,清单中列出了需要创建B对象以及其他的一些对象(可能包含了B类型中需要依赖对象),此时spring在创建B对象的时候,会看B对象需要依赖于哪些对象,然后去查找一下清单中有没有包含这些被依赖的对象,如果有就去将其创建好,然后将其传递给B对象;可能B需要依赖于很多对象,B创建之前完全不需要知道其他对象是否存在或者其他对象在哪里以及被他们是如何创建,而spring容器会将B依赖对象主动创建好并将其注入到B中去,比如spring容器创建B的时候,发现B需要依赖于A,那么spring容器在清单中找到A的定义并将其创建好之后,注入到B对象中。

DI依赖注入,表示spring容器中创建对象时给其设置依赖对象的方式,通过某些注入方式可以让系统更灵活,比如自动注入等可以让系统变的很灵活

IOC容器

IOC容器是具有依赖注入功能的容器,负责对象的实例化、对象的初始化,对象和对象之间依赖关系配置、对象的销毁、对外提供对象的查找等操作,对象的整个生命周期都是由容器来控制。我们需要使用的对象都由ioc容器进行管理,不需要我们再去手动通过new的方式去创建对象,由ioc容器直接帮我们组装好,当我们需要使用的时候直接从ioc容器中直接获取就可以了。

spring ioc容器是如何知道需要管理哪些对象呢?

需要我们给ioc容器提供一个配置清单,这个配置支持xml格式java注解的方式,
在配置文件中列出需要让ioc容器管理的对象,以及可以指定让ioc容器如何构建这些对象,当spring容器启动的时候,就会去加载这个配置文件,然后将这些对象给组装好以供外部访问者使用。

这里所说的IOC容器也叫spring容器。

Bean概念

由spring容器管理的对象统称为Bean对象。Bean就是普通的java对象,和我们自己new的对象其实是一样的,只是这些对象是由spring去创建和管理的,我们需要在配置文件中告诉spring容器需要创建哪些bean对象,所以需要先在配置文件中定义好需要创建的bean对象,这些配置统称为bean定义配置元数据信息,spring容器通过读取这些bean配置元数据信息来构建和组装我们需要的对象。

Spring容器对象

BeanFactory接口
org.springframework.beans.factory.BeanFactory

spring容器中具有代表性的容器就是BeanFactory接口,这个是spring容器的顶层接口,提供了容器最基本的功能。

//按bean的id或者别名查找容器中的bean
Object getBean(String name) throws BeansException

//这个是一个泛型方法,按照bean的id或者别名查找指定类型的bean,返回指定类型的bean对象
<T> T getBean(String name, Class<T> requiredType) throws BeansException;

//返回容器中指定类型的bean对象
<T> T getBean(Class<T> requiredType) throws BeansException;

//获取指定类型bean对象的获取器
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
ApplicationContext接口
org.springframework.context.ApplicationContext

这个接口继承了BeanFactory接口,所以内部包含了BeanFactory所有的功能,并且在其上进行了扩展,增加了很多企业级功能,比如AOP、国际化、事件支持等等。。

ClassPathXmlApplicationContext类
org.springframework.context.support.ClassPathXmlApplicationContext

这个类实现了ApplicationContext接口,注意一下这个类名称包含了ClassPath Xml,说明这个容器类可以从classpath中加载bean xml配置文件,然后创建xml中配置的bean对象

AnnotationConfigApplicationContext类
org.springframework.context.annotation.AnnotationConfigApplicationContext

这个类也实现了ApplicationContext接口,注意其类名包含了Annotation和config两个单词,上面我们有说过,bean的定义支持xml的方式和注解的方式, 当我们使用注解的方式定义bean的时候,就需要用到这个容器来装载了,这个容器内部会解析注解来构建构建和管理需要的bean。

创建bean实例的方式

通过反射调用构造方法创建bean对象
通过静态工厂方法创建bean对象
通过实例工厂方法创建bean对象
通过FactoryBean创建bean对象

FactoryBean可以让spring容器通过这个接口的实现来创建我们需要的bean对象,例如创建bean的代理对象

	public interface FactoryBean<T> {
	
	    /**
	     * 返回创建好的对象
	     */
	    @Nullable
	    T getObject() throws Exception;
	
	    /**
	     * 返回需要创建的对象的类型
	     */
	    @Nullable
	    Class<?> getObjectType();
	
	    /**
	    * bean是否是单例的
	    **/
	    default boolean isSingleton() {
	        return true;
	    }
	
	}

getObject方法内部由开发者自己去实现对象的创建,然后将创建好的对象返回给Spring容器

bean scope

singleton

当scope的值设置为singleton的时候,整个spring容器中只会存在一个bean实例,通过容器多次查找bean的时候(调用BeanFactory的getBean方法或者bean之间注入依赖的bean对象的时候),返回的都是同一个bean对象,singleton是scope的默认值,所以spring容器中默认创建的bean对象是单例的,通常spring容器在启动的时候,会将scope为singleton的bean创建好放在容器中(有个特殊的情况,当bean的lazy被设置为true的时候,表示懒加载,那么使用的时候才会创建),用的时候直接返回

单例bean是整个应用共享的,所以需要考虑到线程安全问题,之前在玩springmvc的时候,springmvc中controller默认是单例的,有些开发者在controller中创建了一些变量,那么这些变量实际上就变成共享的了,controller可能会被很多线程同时访问,这些线程并发去修改controller中的共享变量,可能会出现数据错乱的问题;所以使用的时候需要特别注意。

prototype

如果scope被设置为prototype类型的了,表示这个bean是多例的,通过容器每次获取的bean都是不同的实例,每次获取都会重新创建一个bean实例对象。

多例bean每次获取的时候都会重新创建,如果这个bean比较复杂,创建时间比较长,会影响系统的性能,这个地方需要注意。

request

当一个bean的作用域为request,表示在一次http请求中,一个bean对应一个实例;对每个http请求都会创建一个bean实例,request结束的时候,这个bean也就结束了,request作用域用在spring容器的web环境中,这个以后讲springmvc的时候会说,spring中有个web容器接口WebApplicationContext,这个里面对request作用域提供了支持

session

也是用在web环境中,session级别共享的bean,每个会话会对应一个bean实例,不同的session对应不同的bean实例.

application

全局web应用级别的作用于,也是在web环境中使用的,一个web应用程序对应一个bean实例,通常情况下和singleton效果类似的,不过也有不一样的地方,singleton是每个spring容器中只有一个bean实例,一般我们的程序只有一个spring容器,但是,一个应用程序中可以创建多个spring容器,不同的容器中可以存在同名的bean,但是sope=aplication的时候,不管应用中有多少个spring容器,这个应用中同名的bean只有一个。

依赖注入之手动注入

依赖对象的初始化方式
  • 通过构造器设置依赖对象
  • 通过set方法设置依赖对象

将被依赖的对象设置到依赖的对象中,spring容器内部都提供了支持,这个在spirng中叫做依赖注入

通过构造器注入

构造器的参数就是被依赖的对象,构造器注入又分为3种注入方式:

  • 根据构造器参数索引注入
  • 根据构造器参数类型注入
  • 根据构造器参数名称注入
setter注入

spring对符合javabean特点类,提供了setter方式的注入,会调用对应属性的setter方法将被依赖的对象注入进去。

setter注入相对于构造函数注入要灵活一些,构造函数需要指定对应构造函数中所有参数的值,而setter注入的方式没有这种限制,不需要对所有属性都进行注入,可以按需进行注入。

自动注入(autowire)

手动注入的不足

所谓手动注入是指在xml中采用硬编码的方式来配置注入的对象,比如通过构造器注入或者set方法注入.

  • 如果需要注入的对象比较多,比如A类中有几十个属性,那么上面的property属性是不是需要写几十个,此时配置文件代码量暴增
  • 如果A类中新增或者删除了一些依赖,还需要手动去调整bean xml中的依赖配置信息,否则会报错
  • 总的来说就是不利于维护和扩展
Class.isAssignableFrom方法

用法

//用来判断c2和c1是否相等,或者c2是否是c1的子类。
c1.isAssignableFrom(c2)
自动注入

自动注入是采用约定大约配置的方式来实现的,程序和spring容器之间约定好,遵守某一种都认同的规则,来实现自动注入。
xml中可以在bean元素中通过autowire属性来设置自动注入的方式:

<bean id="" class="" autowire="byType|byName|constructor|default" />
byteName:按照名称进行注入

spring容器会按照set属性的名称去容器中查找同名的bean对象,然后将查找到的对象通过set方法注入到对应的bean中,未找到对应名称的bean对象则set方法不进行注入
需要注入的set属性的名称和被注入的bean的名称必须一致

byType:按类型进行注入

spring容器会遍历x类中所有的set方法,会在容器中查找和set参数类型相同的bean对象,将其通过set方法进行注入,未找到对应类型的bean对象则set方法不进行注入。
需要注入的set属性的类型和被注入的bean的类型需要满足isAssignableFrom关系
一个容器中满足某种类型的bean可以有很多个,将容器中某种类型中的所有bean,通过set方法注入给一个java.util.List<需要注入的Bean的类型或者其父类型或者其接口>对象
将容器中某种类型中的所有bean,通过set方法注入给一个java.util.Map<String,需要注入的Bean的类型或者其父类型或者其接口>对象

constructor:按照构造方法进行注入

spring会找到x类中所有的构造方法(一个类可能有多个构造方法),然后将这些构造方法进行排序(先按修饰符进行排序,public的在前面,其他的在后面,如果修饰符一样的,会按照构造函数参数数量倒序,也就是采用贪婪的模式进行匹配,spring容器会尽量多注入一些需要的对象)得到一个构造函数列表,会轮询这个构造器列表,判断当前构造器所有参数是否在容器中都可以找到匹配的bean对象,如果可以找到就使用这个构造器进行注入,如果不能找到,那么就会跳过这个构造器,继续采用同样的方式匹配下一个构造器,直到找到一个合适的为止。

default:默认注入方式

bean xml的根元素为beans,注意根元素有个default-autowire属性,这个属性可选值有(no|byName|byType|constructor|default),这个属性可以批量设置当前文件中所有bean的自动注入的方式,bean元素中如果省略了autowire属性,那么会取default-autowire的值作为其autowire的值,而每个bean元素还可以单独设置自己的autowire覆盖default-autowire的配置

depend-on

无依赖bean创建和销毁的顺序

  1. bean对象的创建顺序和bean xml中定义的顺序一致
  2. bean销毁的顺序和bean xml中定义的顺序相反

depend-on使用方式

<bean id="bean1" class="" depend-on="bean2,bean3; bean4" />

depend-on:设置当前bean依赖的bean名称,可以指定多个,多个之间可以用”,;空格“进行分割
上面不管bean2,bean2,bean4在任何地方定义,都可以确保在bean1创建之前,会先将bean2,bean3,bean4创建好,表示bean1依赖于这3个bean,可能bean1需要用到bean2、bean3、bean4中生成的一些资源或者其他的功能等,但是又没有强制去在bean1类中通过属性定义强依赖的方式去依赖于bean2、bean3、bean4;当然销毁的时候也会先销毁当前bean,再去销毁被依赖的bean,即先销毁bean1,再去销毁depend-on指定的bean。

primary

当从容器中查找一个bean的时候,如果容器中出现多个Bean候选者时,可以通过primary="true"将当前bean置为首选者,那么查找的时候就会返回主要的候选者,否则将抛出异常。

参考

控制反转(IoC)与依赖注入(DI)
创建bean实例的方式
bean scope
依赖注入之手动注入
自动注入(autowire)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值