spring原理机制
转⾃:blog.csdn/nrain2/article/details/45459311
1,关于spring容器:
spring容器是Spring的核⼼,该容器负责管理spring中的java组件,
ApplicationContext ctx = new ClassPathXmlApplicationContext("l");//这种⽅式实例化容器,容器会⾃动预初始化所有Bean实例Bean("beanName");
ApplicationContext 实例正是Spring容器。
ApplicationContext容器默认会实例化所有的singleton Bean
Spring容器并不强制要求被管理组件是标准的javabean。
2,Spring的核⼼机制:依赖注⼊。
不管是依赖注⼊(Dependency Injection)还是控制反转(Inversion of Conctrol),其含义完全相同:
当某个java实例(调⽤者)需要调⽤另⼀个java实例(被调⽤者)时,传统情况下,通过调⽤者来创建被调⽤者的实例,通常通过new来创建,
⽽在依赖注⼊的模式下创建被调⽤者的⼯作不再由调⽤者来完成,因此称之为"控制反转";创建被调⽤者实例的⼯作通常由Spring来完成,然后注⼊调⽤者,所以也称之为"依赖注⼊"。
3,依赖注⼊⼀般有2中⽅式:
设置注⼊:IoC容器使⽤属性的setter⽅式注⼊被依赖的实例。<property name="" ref="">
构造注⼊:IoC容器使⽤构造器来注⼊被依赖的实例。<constructor-arg ref="">
配置构造注⼊的时候<constructor-arg>可以配置index属性,⽤于指定该构造参数值作为第⼏个构造参数值。下表从0开始。
4,Spring容器和被管理的bean:
Spring有两个核⼼接⼝:BeanFactory和ApplicationContext,其中ApplicationContext是BeanFactory的⼦接⼝。他们都可以代表Spring容器。
Spring容器是⽣成Bean实例的⼯⼚,并管理Spring中的bean,bean是Spring中的基本单位,在基于Spring的java EE⼯程,所有的组件都被当成bean处理。
包括数据源、Hibernate的SessionFactory、事务管理器。
①Spring容器:Spring最基本的接⼝就是BeanFactory,
BeanFactory有很多实现类,通常使⽤XmlBeanFactory,但是对于⼤部分的javaEE应⽤⽽⾔,推荐使⽤ApplictionContext,它是BeanFactory的⼦接⼝,
ApplictionContext的实现类为FileSystemXmlApplicationContext和ClassPathXmlApplicationContext FileSystemXmlApplicationContext:基于⽂件系统的XML配置⽂件创建ApplicationContext;ClassPathXmlApplicationContext:基于类加载路径下的xml配置⽂件创建ApplicationContext。
②ApplicationContext的事件机制,
ApplicationContext事件机制是基于观察者设计模式实现的。通过ApplicationEvent类和ApplicationListener接⼝,
其中ApplicationEvent:容器事件,必须由ApplicationContext发布;
ApplicationListener:,可有容器内的任何Bean担任。
③容器中bean的作⽤域:
singleton:单例模式,在整个Spring IoC容器中,使⽤singleton定义的bean将只有⼀个实例;
prototype:原型模式,每次通过容器的getBean⽅法获取prototype定义的Bean时,都将产⽣⼀个新实例;
request:对于每次HTTP请求中,使⽤request定义的bean都将产⽣⼀个新实例,只有在web应⽤程序使⽤Spring时,该作⽤域才有效;session:同理
global session:同理
注意:request和session作⽤域只在web应⽤中才⽣效,并且必须在web应⽤中增加额外的配置才会⽣效,为了让request,session两个作⽤域⽣效,必须将HTTP请求对象绑定到为该请求提供服务的线程上,这使得具有request和session作⽤域的Bean实例能够在后⾯的调⽤链中被访问。
当⽀持Servlet2.4及以上规范的web容器时,我们可以在web应⽤的l增加如下Listener配置,该Listener负责为request作⽤域⽣效:
<listener>
<listener-class>org.t.request.RequestContextListener</listener-class>
</listener>
如果仅使⽤了⽀持Servlet2.4以前规范的web容器,则该容器不⽀持Listener规范,故⽆法使⽤这种配置,可以使⽤Filter配置⽅式,我们可以在web应⽤的l增加如下Filter配置:
<filter>
<filter-name>requestContextFilter</filter-name>
<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>requestContextFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
再如下⾯的代码:
<bean id="p" class="lee.Person" scope="request"/>
这样容器就会为每次HTTP请求⽣成⼀个lee.Person的实例当该请求响应结束时,该实例也随之消失。
如果Web应⽤直接使⽤Spring MVC作为MVC框架,即使⽤SpringDispatchServlet或DispatchPortlet来拦截所有⽤户请求,则⽆需这些额外
的配置,因为SpringDispatchServlet或DispatchPortlet已经处理了所有和请求有关的状态处理。
④获取容器的引⽤:
通常情况下:
Bean⽆需访问Spring容器,⽽是通过Spring容器访问的,即使需要⼿动访问Spring容器,程序也已通过类似下⾯的代码获取Spring容器的引⽤。
ApllicationContext cts = ClassPathApplalicationContext("l");
但在⼀些极端的情况下,可能Bean需要访问Spring容器。Spring提供另⼀种⽅法访问Spring容器:
实现BeanFactoryAware接⼝的Bean,拥有访问Spring容器的能⼒,实现BeanFactoryAware的Bean被容器实例化后,会拥有⼀个引⽤指向创建他的BeanFactory。BeanFactoryAware只有⼀个⽅法setBeanFactory(BeanFactory beanFactory)该参数指向创建他的BeanFactory。
缺点:污染了代码,使代码与Spring接⼝耦合在⼀起,因此没有特别的必要,建议不要直接访问容器。
5,Bean实例的创建⽅式及对应配置:
创建Bean的⽅法:
①调⽤构造器创建Bean实例;
②调⽤静态⼯⼚⽅法创建Bean;
③调⽤实例⼯⼚创建Bean。
调⽤静态⼯⼚⽅法创建Bean:
class属性是必须的,但此时的class并不是指定Bean实例的实现类⽽是静态⼯⼚类。采⽤静态⼯⼚类需
要配置如下两个属性:
class静态⼯⼚类的名字;
factory-method⼯⼚⽅法(必须是静态的)。
如果静态⼯⼚的⽅法有参数通过<constructor-arg/>元素知道。
调⽤实例⼯⼚⽅法创建Bean:
使⽤实例⼯⼚Bean时class属性⽆需指定,因Spring容器不会直接实例化该Bean,
创建Bean时需要如下属性:
factory-bean:该属性为⼯⼚Bean的ID;
factory-method:该属性是定实例⼯⼚的⼯⼚⽅法。
6,⼊理解Spring容器中的Bean:
抽象Bean:
所有的抽象Bean,就是是定abstract属性为true的Bean,抽象Bean不能被实例化,抽象Bean的价值在于被继承
使⽤⼦Bean:
随着应⽤规模的增⼤,Spring配置⽂件的增长速度更快。当应⽤中的组件越来越多,,Spring中的Bean配置也随之⼤幅度增加。
就会出现⼀中现象:有⼀批配置Bean的信息完全相同,只有少量的配置不同。怎么解决呢?
这时候就可以⽤Bean的继承来解决。
注意:⼦Bean⽆法从⽗Bean继承如下属性:
depends-on,aotuwirwe,dependency-check,singleton,scope,lazy-iniyt这些属性总是⼦Bean定义,或采⽤默认值。
通过为⼀个&/> 元素指定parent属性,即可指定该Bean是⼀个⼦Bean。
Bean继承与java中继承的区别:
Spring中的⼦bean和⽗Bean可以是不同类型,但java中的继承则可保证⼦类是⼀种特殊的⽗类;
Spring中的Bean的继承是实例之间的关系,因此只要表现在参数值的延续,⽽java中的继承是类之间的关系,主要表现为⽅法、属性之间的延续;
Spring中的⼦Bean不可以作为⽗Bean使⽤,不具备多态性,java中的⼦类完全可以当成⽗类使⽤。
Bean的⽣命周期:
①singleton与prototype的区别:
singleton:Spring可以精确的知道该Bean何时被创建、初始化、销毁。对于singleton作⽤域的Bean,每次客户端请求Spring容器总会返回⼀个共享的实例。
prototype:Spring容器仅仅负责创建Bean,当容器创建了Bean的实例后,Bean实例完全交给客户端代码管理,容器不在跟踪其⽣命周期。每次客户端请求prototype作⽤域的Bean,都会为他创建⼀个新的实例,
②依赖关系注⼊后的⾏为:
Spring提供两种⽅法在Bean全部属性设置成功后执⾏特定的⾏为:
使⽤init-method属性;
该Bean实现InitializingBean接⼝
第⼀种⽅法:使⽤init-method属性指定某个⽅法在Bean全部属性依赖关系设置结束后⾃动执⾏。使⽤这种⽅法不需要将代码与Spring的接⼝耦合在⼀起,代码污染少;
第⼆种⽅法:实现Initializing接⼝,该接⼝有⼀个⽅法void afterPropertiesSet() throws Exception,虽然实现次接⼝⼀样可以在Bean全部属性设置成功后执⾏特定的⾏为,但是污染了代码,是侵⼊式设计,因此不推荐使⽤。
注意:如果即采⽤init-method属性指定初始化⽅法,⼜实现InitializingBean接⼝来指定初始化⽅法,先执⾏initializingBean接⼝中定义的⽅法,再执⾏init-method属性指定的⽅法。
③Bean销毁之前⾏为:
与定制初始化相似,Spring也提供两种⽅式定制Bean实例销毁之前的特定⾏为,如下:
使⽤destroy-method属性:
实现DisposableBean接⼝:
注意:如果即采⽤destroy-method属性指定销毁之前的⽅法,⼜实现DisposableBean接⼝来指定指定销毁之前的⽅法,与②类似。
④default-init-method与default-destroy-method属性,指定了所有的Bean都会执⾏此⽅法,⽽不是单个的Bean。
协调作⽤域不同步的Bean:
描述:
当Spring容器中作⽤域不同的Bean相互依赖时,可能出现⼀些问题:
当两个singleton作⽤域Bean存在依赖关系时,或当prototype作⽤依赖singleton作⽤域的Bean时,通过属性定义依赖关系即可。、mvc实例
但是,当singleton作⽤域的Bean依赖prototype作⽤域Bean时,singleton作⽤域的Bean只有⼀次初始化的机会,他的依赖关系也只有在初始化阶段被设置,⽽他所依赖的prototype作⽤域的Bean则会不断的产⽣新的Bean实例。
解决⽅案:
第⼀种:部分放弃依赖注⼊:singleton作⽤域的Bean每次需要prototype作⽤域的Bean,则主动向容器请求新的Bean实例。
第⼆种:利⽤⽅法注⼊。
第⼀种⽅案肯定是不好的,代码主动请求新的Bean实例,必然会导致与Spring API耦合,造成代码严重污染。
通常情况下采⽤第⼆中⽅式。
⽅法注⼊通常使⽤lookup⽅法注⼊,利⽤lookup⽅法注⼊可以让Spring容器重写容器中Bean的抽象⽅法或具体⽅法,返回查容器中的其他 Bean,被查的Bean通常是non-singleton Bean(尽管也可以是singleton).
如:public class SteelAxe implements Axe{
//每执⾏⼀次加⼀
private int count;
public String chop(){
return ++count;
}
}
public abstract class Chinese implements Perosom{
private Axe axe;
//定义⼀个抽象⽅法,该⽅法将由Spring负责实现
public abstract Axe createAxe();
public voidsetAxe(Axe axe){
this axe = axe;
}
public Axe getAxe(){
return axe;
}
}
在Spring配置⽂件中配置:
<bean id="steelAxe" class="...SteelAxe" scope="prototype"></bean>
<bean id="chinese" class="..Chinese" >
< lookup-mehtod name="createAxe" bean="steelAxe">
<property name="axe" ref="steelAxe"/>
</bean>
容器中的⼯⼚Bean:
此处的⼯⼚Bean与前⾯介绍的实例⼯⼚⽅法创建Bean、静态⼯⼚创建Bean有所区别:
前⾯的那些⼯⼚是标准的⼯⼚模式,Spring只是负责调⽤⼯⼚⽅法来创建Bean实例;
此处⼯⼚Bean是Spring的⼀种特殊Bean,这种⼯⼚Bean必须实现FactoryBean接⼝。
FactoryBean接⼝是⼯⼚Bean标准的⼯⼚Bean的接⼝,实现该接⼝的Bean只能当⼯⼚Bean使⽤,当我们将⼯⼚Bean部署在容器中,并通过getBean()⽅法来获取⼯⼚Bean,容器不会返回FactoryBean实例⽽是FactoryBean的产品。
FactoryBean提供了三个⽅法:
Object getObject();
Class getObjectType();
boolean isSingleton();
如:
public class PersonFactory implements FactoryBean{
Person p = null;
public Object getObject() throws Exception{
if(p==null){
p = new Chinense();
return p;
}
}
public Class getObjectType(){
return Chinese.class;
}
public boolean isSingleton(){
return true;
}
}
<!--配置⼀个FactoryBean,和普通的Bean⼀样-->
<bean id="chinese" class=""/>
public static void main(String args[]){\
//以classpth下的l创建Reource对象
ClassPathResource re = new ClasspathResource("l");
//创建BeanFactory
XmlBeanFactory factory = new XmlBeanFactory(re);
Person p = (Bean("chinese");
//如需要获取FactoryBean本⾝则应该在bean id前加&
Person p = (Bean("&chinese");
}
对于初学者可能⽆法体会到⼯⼚bean的作⽤,实际上,FactoryBean是Spring中⾮常有⽤的接⼝。例如:TransationProxyFactroyBean,这个⼯⼚转为⽬标Bean创建事务代理.
7,深⼊理解依赖关系配置
组件与组件之间的耦合,采⽤依赖注⼊管理,但是普通的javabean属性值,应直接在代码⾥设置。
对于singleton作⽤域的bean,如果没有强制取消其预初始化⾏为,系统会在创建Spring容器时预初始化所有的singleton作⽤域的bean,与此同时,该bean依赖的bean也⼀起被实例化。
BeanFactory与ApplicationContext实例化容器中的bean的时机不同,前者等到程序需要Bean实例才创建Bean
后者会预初始化容器中的所有Bean。
因为采⽤ApplicationContext作为Spring的容器,创建容器时,会创建容器中所有singleton作⽤域的所有
bean,因此可能需要更多的系统资源,但是⼀旦创建成功。应⽤后⾯的响应速度会很快,因此,对于普通的javaEE⽽⾔,建议使⽤ApplicationContext作为Spring的容器。Bean实例4中属性值的设置:
value;ref;bean;list、set、map、props
①设置普通属性值value,略;
②配置合作者Bean ref
可以为ref元素指定两个属性:bena、Local
bean:引⽤在不同⼀份XML配置⽂件中的其他Bean实例的ID属性值;
Local:引⽤同⼀份XML配置⽂件的其他Beanid属性值。
也可以不配置以上两个属性。
③组合属性名称:
public class A{
private Person p = new Person();
}
Spring配置⽂件
<bean id="a" class="A">
<property name="p.name" value="aaa"/>
</bean>
④注⼊嵌套Bean:
<bean id="" class="">
< property name="">
//属性为嵌套Bean 不能由Spring容器直接访问,因此没有id属性
<bean class="..."/>
</property>
</bean>
⑤注⼊集合值:
<list>
<value></value>
<value></value>
</list>
<map>
//每⼀个entry配置⼀个key-value对
<entry>
<key>
<value>.</value>
</key>
<value></value>
</entry>
</map>
<set>
<value></value>
<bean></bean>
<ref local=""/>
</set>
<props>
<prop key="">.....</prop>
<prop key="">.....</prop>
</props>
⑥注⼊⽅法返回值:
public class ValueGenrator{
public int getValue(){
return 6;
}
public static int getStaticValue(){
return 9;
}
}
<bean id="valueGenrator" class="lee.ValueGenrator"/>
<bean id="son1" class="Son">
<property name="age">
<bean class="org.springframework.fig.MethodInvokignFactoryBean">
//配置⾮静态⽅法
<property name="targetObject" ref="valueGenrator"/>
//配置静态⽅法
<!--
<property name="targetClass" value="lee.ValueGenrator"/>
-->
<property name="targetMehtod" value="getStaticValue/>
</property>
</bean>
8,强制初始化Bean:
Spring有⼀个默认的规则,总是先初始化主调Bean,然后在初始化依赖Bean。
为了指定Bean在⽬标Bean之前初始化,可以使⽤depends-on属性
9,⾃动装配:
Spring能⾃动装配Bean与Bean之间的依赖关系,即使⽆需使⽤ref显式指定依赖Bean。
Spring的⾃动装配使⽤autowire属性指定,每⼀个<bean/>元素都可以指定autowire属性,也就是说在Spring容器中完全可以让某些Bean⾃动装配,⽽某些Bean不没使⽤⾃动装配。
⾃动装配可以减少配置⽂件的⼯作量,但是降低了依赖关系的透明性和依赖性。
使⽤autowire属性⾃动装配,autowire属性可以接受如下⼏个值:
no:不使⽤⾃动装配。这是默认配置。
byName:根据属性⾃动装配,BeanFactory会查容器中所有的Bean,出id属性与属性名同名的Bean来完成注⼊。如果没有到匹配的Bean,Spring则不会进⾏任何注⼊。
byType:根据属性类型⾃动装配,BeanFactroy会查容器中所有的 Bean,如果⼀个正好与依赖属性类型相同的Bean,就会⾃动注⼊这个属性。
如果有多个这样的Bean则会抛出异常。如果没有这样的Bean则什么也不会发⽣,属性不会被设置。
constructor:与byType类似,区别是⽤于构造注⼊的参数,如果BeanFactory中不是恰好有⼀个Bean与构造器参数类型相同。则会抛出异常。
autodetect:BeanFactory会根据Bean内部的结构,决定使⽤constructor或byType,如果到⼀个缺省的构造器,就会应⽤byType。
注意:对于⼤型的应⽤⽽⾔,不⿎励使⽤⾃动装配,
10,依赖检查:
Spring提供⼀种依赖检查的功能,可以防⽌出现配置⼿误,或者其他情况的错误。
使⽤依赖检查可以让系统判断配置⽂件的依赖关系注⼊是否完全有效。
使⽤依赖检查,可以保证Bean的属性得到了正确的设置,有时候,某个Bean的特定属性并不需要设置值,或者某些属性已有默认值,此时采⽤依赖检查就会出现错误,该Bean就不应该采⽤依赖检查,幸好Spring可以为不同的Bean单独指定依赖检查的⾏为,Spring提供dependency-chech属性来配置依赖检查,当然也可以指定不同的检查依赖策略。
该属性有如下值:
none:不进⾏依赖检查,没有指定值的Bean属性仅仅是没有设置值,这是默认值。
simple:对基本类型和集合(除了合作者Bean)进⾏依赖检查。
objects:仅对合作者Bean进⾏依赖检查。
all:对合作者Bean、基本数据类型全部进⾏依赖检查。
public class Chinese implements Person{
private Axe axe;
private int age = 30;
//implements method
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age
}
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论