Spring学习进阶(二)Spring IoC

时间:2022-05-13 07:20:03
在使用Spring所提供的各种丰富而神奇的功能之前,必须在Spring IoC容器中装配好Bean,并建立Bean与Bean之间的关联关系。
控制反转(Inverser of Control ioc)是Spring容器的内核,AOP、声明式事务等功能都是在此基础上开花结果的。所谓Ioc,
就是通过容器来控制业务对象之间的依赖关系,而非传统实现中,由代码直接操控、这也就是“控制反转”概念的所在:控制权
由应用代码中转到了外部容器,控制权的转移,就是反转。控制权转移带来的好处就是降低了业务对象之间的依赖程度。
BeanFactory和ApplicationContext:
Spring通过一个配置文件描述了Bean和Bean之间的依赖关系,利用Java语言的反射功能实例化Bean并建立Bean之间的依赖关系。
Spring的IoC容器在完成这些底层工作之后还提供了Bean实例缓存,生命周期管理,ean实例代理,事件发布,资源装载等高级服务。
Bean工程(com.springframework.beans.factory.BeanFactory)是Spring框架核心的接口,它提供了高级IoC的高级配置机制,
BeanFactory使管理不同类型的java对象成为可能,应用上下文(com.springframework.context.ApplicationContext)是建立在
BeanFactory基础之上,提供了更多面向应用的功能,还提供了国际化支持和框架事件体系,更易于创建实际应用。一般称BeanFactory
为IoC容器,而称ApplicationContext为应用上下文。
区分:BeanFactory是Spring框架的基础设施,面向Spring本身;ApplciationContext面向使用Spring框架的开发者,几乎所有的应用
场合都直接使用ApplicationContext而不是底层的BeanFactory。 BeanFactory介绍:
BeanFactory是一个工厂类,但它和传统的类工厂不同,传统的类工厂仅负责构造一个或几个类的实例,而BeanFactory是类的通用工厂,
他可以创建并管理各种累的对象。这些可被创建和管理的对象本身没有什么特别之处,仅是一个POJO,Spring称这些被创建和管理的java
对象为Bean。
beans.xml:Car的配置文件 beanfactory/BeanFactoryTest.java ApplicationContext介绍:
ApplicationContext是由BeanFactory派生而来,提供了更多面向实际应用的功能,在BeanFactory中,很多功能都需要以编程的方式实现,
而在ApplicationContext中则可以通过配置的方式实现。
与BeanFactory初始化相似,ApplicationContext的初始化也很简单,如果配置文件防止在类路径下,则可以优先使用ClassPathXMLApplicationContext
实现类。
ApplicationContext ctx=new ClassPathApplicationContext("com/smart/context/beans.xml")
对于ClassPathXMLApplicationContext来说com/smart/context/beans.xml相当于classpath:com/smart/context/beans.xml
如果配置文件放置在文件系统的路径下,则可以优先考虑使用FileSystemXmlApplicationContext实现类
ApplicationContext ctx=new FileSystemApplicationContext("com/smart/context/beans.xml")
对于FileSystemApplicationContext来说com/smart/context/beans.xml相当于file:com/smart/context/beans.xml 还可以指定一组配置文件,Spring会自动将多个配置文件在内存中“整合”成一个配置文件
ApplicationContext ctx=new ClassPathXmlApplicationContext(new String[]{"conf/beans1.xml","conf/beans2.xml"})
当然FileSystemXMLApplicationContext和ClassPathXmlApplicationContext都可以显示使用带资源类型前缀的路径,他们的区别在于。如果不显示
指定资源类型前缀,句分别将路径解析为文件系统路径和类路径。
在获取ApplicationContext实例后,就可以像BeanFactory一样调用getBean(beanName)返回Bean了,ApplicationContext的初始化和BeanFactory
的初始化有一个重大的区别:BeanFactory在初始化容器时,并未实例化Bean,直到第一个访问某个Bean时才实例化目标Bean;而ApplicationContext在
初始化应用上下文时就实例化所有单实例的Bean.因此ApplicationContext的初始化时间会比BeanFactory稍长一些,不过稍后的调用则没有“第一次惩罚”
的问题。
Spring3.0之处基于类注解的配置方式,主要功能来自于Spring的一个名为JavaConfig的子项目。一个标注@Configuration注解的POJO即可提供Spring
所需的Bean配置信息。 context/Beans.java context/AnnotationApplicationContext.java WebApplicationContext类体系结构:
WebApplicationContext是专门为Web应用准备的,它允许从相对于Web根目录的路径中装载配置文件,完成初始化工作。从WebApplicationContext中
可以获得ServletConfig的引用,整个Web应用上下文对象将作为属性放置到ServletContext中,以便Web应用环境可以访问Spring应用上下文,Spring
为此还专门提供一个工具类WebApplicationContextUtils,通过该类的getWebApplicationContext(servletContext sc)方法。即可以从ServletContext
中获取WebApplicationContext实例。
WebApplicationContext初始化:
WenApplicationContext的初始化方式和BeanFactory,ApplicationContext的初始化方式有所区别,因为WebApplicationContext需要ServletContext实例,
也就是说必须在拥有Web容器的前提下才能完成启动的工作。可以在web.xml中配置自动的servlet或定义web容器监听器(ServletContextListener)
Spring分别提高了用于启动WebApplicationContext的servlet和web容器监听器。
org.springframework.web.context.Context.ContextLoaderServlet
org.springframework.web.context.Context.ContextLoaderListener 通过Web容器监听器引导:
<!--指定配置文件-->
<context-param>
<param-name>contextConfiguration</param-name>
<param-value>/WEB-INF/viewspace-dao.xml,/WEB-INF/viewspace-service.xml,</param-value>
</context-param>
<!--声明web容器监听器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
ContextLoaderListener通过web容器上下文参数contextConfigLocation获取Spring配置文件的位置,可以指定多个配置文件,用逗号,空格或分号分割
对于未带资源类型前缀的配置文件路径,WebApplicationContext默认这些路径是相对于Web的部署根路径。
在不支持容器监听器的低版本web容器中,可采用ContextLoaderServlet来完成相同的工作。
<!--指定配置文件-->
<context-param>
<param-name>contextConfiguration</param-name>
<param-value>/WEB-INF/viewspace-dao.xml,/WEB-INF/viewspace-service.xml,</param-value>
</context-param>
<!--声明自动启动的servlet-->
<listener>
<servlet-name>springContextLoaderServlet</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<!--启动顺序-->
<load-on-startup>1</load-on-startup>
</listener>
WebApplicationContext需要使用日志功能,
指定Log4J配置文件时启动Spring web应用上下文。
<!--指定配置文件-->
<context-param>
<param-name>contextConfiguration</param-name>
<param-value>/WEB-INF/viewspace-dao.xml,/WEB-INF/viewspace-service.xml</param-value>
</context-param>
<!--指定log4j配置文件位置-->
<context-param>
<param-name>log4jConfiguration</param-name>
<param-value>/WEB-INF/log4j.properties</param-value>
</context-param>
<!--装载log4j配置文件的自启动servlet-->
<servlet>
<servlet-name>log4jConfiguration</servlet-name>
<servlet-class>org.springframework.web.util.Log4jConfigServlet</servlet-class>
<load-on-start-up>1</load-on-start-up>
</servlet>
<!--声明web容器监听器-->
<listener>
<servlet-name>springContextLoaderServlet</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<!--启动顺序-->
<load-on-startup>1</load-on-startup>
</listener> 使用@Configuration的java类提供配置信息的信息在web.xml中做如下配置:
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--通过指定context参数,让spring使用AnnotationConfigWebApplicationContext而非XMLWebApplicationContext启动容器-->
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<!--指定标注了@Configuration的配置类,多个可以使用逗号分开-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.smart.AppConfig1</param-value>
</context-param>
<!--ContextLoaderListener监听器将根据上面配置使用AnnotationConfigWebApplicationContext根据contextConfigLocation指定的配置类启动Spring容器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
ContextLoaderListener如果发现配置了contextClass上下文参数,就会使用参数所指定的WebApplicationContext实现类来初始化容器,该实现类会根据contextConfigLocation
上下文参数指定的@Configuration的配置类所提供的Spring配置信息来初始化容器。 资源加载:
为了访问不同类型的资源,必须使用相应的Resource实现类,Spring提供了一个强大的加载资源的机制,不但可以通过classpath file等资源地址前缀来加载
识别不同的资源类型,还支持Ant风格带通配符的资源地址。
classpath: classpath:com/smart/beanfactory/beans.xml 从类路径中加载资源,classpath:和classpath:/是等价的,都是相对于类的根
路径,资源文件可以在标准的文件系统中,也可以在jar或zip的类包中
file: file:/conf.com.smart/smart/beanfactory/beans.xml 使用UrlResource从文件系统目录中装载资源,可采用绝对或相对路径
http:// http://www.smart.com/resource/beans.xml 使用UrlResource从web服务器中装载资源
ftp:// ftp://www.smart.com/resource/beans.xml 使用UrlResource从ftp服务器中装载资源
没有前缀 com/smart/beanfactory/beans.xml 根据ApplicationContext具体实现类采用对应类型的Resource Bean装配:
要使应用程序中的Spring容器成功启动,需要同时具备以下3方面的条件:
Spring框架的雷暴都已经放到应用程序的类路径下
应用程序为Spring提供玩呗的Bean配置信息
Bean的类都已经放到应用程序的类路径下
Spring启动时读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相应的Bean配置注册表,然后根据这张注册表实例化Bean,装配好Bean之间的依赖关系
为上层应用提供准备就绪的运行环境。
Bean的配置信息是Bean的元数据信息,由4方面组成:
Bean的实现类
Bean的属性信息,如数据源的连接数,用户名密码等
Bean的依赖关系,Spring根据依赖关系配置完成Bean之间的装配
Bean的行为配置,如生命周期范围及生命周期各过程的回调函数等。
Bean元数据信息在Spring容器中的内部对应物是由一个个BeanDefinition形成的Bean注册表,Spring实现了Bean原数据信息内部表示信息和外部表示的解耦。
Bean配置信息定义了Bean的实现和依赖院系,Spring容器根据各种形式的Bean配置信息在容器内部建立Bean定义注册表,然后根据注册表加载,
实例化Bean,并建立Bean和Bean的依赖关系,最后将这些准备就绪的Bean放到Bean缓存池中,以供外层的应用程序调用。 Bean基本配置:
装配一个Bean:
在Spring容器的配置文件中,定义一个简要Bean的配置片段 <bean id="foo" class="com.smart.bean.Foo"/>
一般情况下,Spring IoC容器中的一个Bean即对应配置文件中的一个<bean>.通过heyBean("id")即可获得对应的Bean,在容器中起到定位查找的作用,是外部程序和Spring IoC容器进行交互的桥梁,
class属性指定了Bean对应的实现类 Bean的命名:
一般情况下,在配置一个Bean时,需要为其指定一个id属性作为Bean的名称。id在IoC容器中是唯一的,而且还必须遵循XML的命名规则(字母开始,后面可以是字母,数字,连字符,下划线,
句号,冒号等),IoC不允许出现两个ID相同的Bean但是允许出现两个name相同的bean。如果id和name都没有指定则使用全限定类名作为bean的名称。 属性注入:
属性置入即通过setXXX()方法注入Bean的属性值或依赖对象,由于属性注入方式具有可选择性和灵活性的特定。因此比较普遍使用。
属性注入要求bean提供一个默认的构造函数,并为需要注入的熟悉提供对应的Setter方法,Spring先调用Bean的默认构造函数实例化Bean对象,然后通过反射的方式调用Setter注入属性值,
属性注入配置:
<bean id="car" class="com.smart.ditype.Car">
<property name="maxSpeed"><value>200</value></property>
<property name="brand"><value>BMW</value></property>
<property name="price"><value>2000000</value></property>
</bean>
构造函数注入:
保证一些必要的属性在Bean实例化时就得到设置,并且确保了Bean实例在实例化后就可以使用
按照类型匹配注入
eg:
public Car(String brand,double price){
this.brand=brand;
this.price=price;
}
=====>
<bean id="car" class="com.smart.ditype.Car">
<constructor-arg type="java.lang.String">
<value>BMW</value>
</constructor-arg>
<constructor-arg type="double">
<value>20000</value>
</constructor-arg>
</bean>
实构造函数注入和属性注入都各有优缺点,Spring并没有强制用户使用哪一种方式,用户完全可以根据偏好自己选择,在某种情况下使用构造函数注入,而在另一些情况下使用属性注入, 注入参数详解:
<ref>元素可以通过以下3个属性引用容器中其他的bean.
bean:通过该属性可以引用同一容器或父容器的Bean,这是最常见的形式
local:通过该属性只能引用统一配置文件中的Bean他可以利用XML解析器自动检验引用的合法性,以便在开发编写配置时能够及时发现并纠正配置的错误
parent:引用父容器中的Bean,如<ref parent="car">的配置说明car的Bean是父容器中的Bean
集合类型属性:
public class Boss {
private List favorites=new ArrayList();
private Map jobs=new HashMap();
private Properties mails=new Properties(); public Properties getMails() {
return mails;
} public void setMails(Properties mails) {
this.mails = mails;
}
public Map getJobs() {
return jobs;
} public void setJobs(Map jobs) {
this.jobs = jobs;
} public List getFavorites() {
return favorites;
} public void setFavorites(List favorites) {
this.favorites = favorites;
}
} 注入:
<bean id="boss1" class="com.smart.attr.Boss">
<property name="favorites">
<list>
<value>看报</value>
<value>篮球</value>
<value>足球</value>
</list>
</property>
<property name="jobs">
<map>
<entry>
<key><value>AM</value></key>
<value>会见客户</value>
</entry>
<entry>
<key><value>PM</value></key>
<value>公司内部会议</value>
</entry>
</map>
</property>
<property>
<prop key="jobMail">john-office@smart.com</prop>
<prop key="lifeMail">john-life@smart.com</prop>
</property>
</bean> 自动装配:
Spring IOC容器了解容器中所有Bean的配置信息,此外通过java反射机制可以获知实现类的结构信息。掌握容器中所有Bean的信息后,Spring IoC容器就可以
按照某种规则对容器中的Bean进行自动配置。Bean元素提供了一个指定自动装配类型的属性autowire="<自动装配类型>"
byName:根据名称进行自动装配。
byType:根据类型进行自动配置。
constructor:与byType类似,只不过他是根据构造函数注入而言的
autodetect:根据bean的自省机制决定采用byType还是constructor进行自动装配。 <beans>元素标签中的default-autowire属性可以配置全局自动匹配,默认设置为no,表示不启用。 Bean作用域:在配置文件中定义Bean时,用户不但可以配置Bean的属性值以及相互之间的依赖关系,还可以定义Bean的作用域
作用域类型:
singleton:在Spring IoC容器中仅存在一个Bean实例,Bean以单实例的方式存在。
prototype:每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行new XxxBean()的操作
request:每次http请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境
session:同一个Http Session共享一个Bean,不同HTTP Session使用不同的Bean,该作用域仅适用于WebApplicationContext环境
globalSession:同一个全局Session共享一个Bean,一般用于Portlet应用环境,该作用域仅适用于WebApplicationContext环境、
Spring中注解:
@Repository:用于对DAO实现类进行标注
@Service:用于对Service实现类进行标注
@Controller:用于对Controller实现类进行标注 通过<context:component-scan>定义扫描的包
<context:include-scan>表示要包含的目标类
<context:exclude-scan>表示要排除在外的目标类
过滤表达式:
annotation:所有标注了XxxAnnotation的类,该类型采用目标类是否标注了某个注解进行过滤 com.smart.XxxAnnotation
assignable:所有继承或扩展XxxService的类,该类型采用目标类是否继承或扩展某个特定类进行过滤 com.smart.XxxService
aspectj:所有类名以Service结束的类及集成或扩展他们的类, com.smart..*Service+
regex:正则表达式过滤 com\.smart\.anno\.*
custom:采用XxxTypeFile通过代码的方式根据过滤规则,该类型必须事先TypeFilter接口 com.smart.XxxTypeFilter 自动装配Bean:Spring通过使用@Autowired注解实现Bean的依赖注入,如果容器中没有一个和标注变量类型匹配的Bean,Spring容器会启动时抛出NoSuchBeanDefinitionException
异常,如果希望Spring即使找不到匹配的Bean完成注入也不要抛出异常,那么就可以使用@Autowired(require=false)进行标注。
如果容器中有一个以上匹配的Bean,可以通过@Qualifier注解限定Bean的名称 基于java类的配置:
普通的POJO只要标注@Configuration注解,就可以为Spring容器提供Bean定义的信息了,每个标注了@Bean的类犯法都相当于提供一个Bean的定义信息、 Bean不同配置方式比较
1.基于XML配置
定义:在XML文件中通过<bean>元素定义Bean,例如<bean class="com.bbt.UserDao"/>
名称:通过<bean>的id或name属性定义,例如:<bean id="userDao" class="com.bbt.UserDao">默认名称为com.bbt.UserDao#0
注入:通过<property>子元素或通过p命名空间的动态属性,如p:userDao-ref="userDao"进行注入
生命过程方法:通过<bean>的init-method和destroy-method属性指定Bean实现类的方法名,最多只能指定一个初始化和一个销毁方法
作用范围:通过<bean>的scope属性指定,如<bean class="com.bbt.UserDao" scope="prototype">
延迟初始化:通过<bean>的lazy-init属性指定,默认为default,继承与<beans>的default-lazy-init设置,该值默认为false
2.基于注解配置
定义:在Bean实现类处通过标注@Component或衍型类(@Repository @Service @Controller)定义Bean
名称:通过注解的value属性定义,如@Component("userDao")默认名称为小写字母打头的类名(不带包名)userDao
注入:通过在成员变量或方法入参处标注@Autowired,按类型匹配自动注入。还可以配合使用@Qualifier按名称匹配方式注入
生命过程方法:通过在目标方法上标注@PostConstruct和@PreDestroy注解指定初始化或销毁方法,可以定义任意多个方法
作用范围:通过在类定义处标注@Scope指定。如@Scope("prototype")
延迟初始化:通过在类定义处标注@lazy指定,如@Lazy(true)
3.基于Java类配置
定义:在标注了@Configuration的java类中,通过在类方法上标注@Bean定义一个Bean,方法必须提供Bean的实例化逻辑
名称:通过@Bean的name属性定义,如@Bean("userDao")默认名称为方法名
注入:比较灵活,可以在方法处通过@Autowired使方法入参绑定bean,然后在方法中通过代码进行注入,还可以通过调用配置类的@Bean方法进行注入
生命周期方法:通过@Bean的initMethod或destoryMethod指定一个初始化或销毁方法。对于初始化方法来说,可以直接在方法内部通过代码的方式灵活初始化逻辑
作用范围:通过在Bean方法定义处标注@Scope指定
延迟初始化:通过在Bean方法定义处标注@Lazy指定 Bean不同配置方式的适用场合
基于XML配置:Bean实现类来源于第三方类库,如DataSource,JdbcTemplate等,因无法在类中标注注解,通过XML配置方式较好。命名空间的配置,如aop context等,只能采用基于XML的配置
基于注解配置:Bean的实现类是当前项目开发的,可以直接在Java类中基于注解的配置
基于Java类配置:基于Java类配置的优势在于可以通过代码控制Bean初始化的整体逻辑。所以如果实例化Bean的逻辑比较复杂,则比较适合用基于Java类配置的方式