Spring是如何简化Java开发的?
- 基于POJO的轻量级和最小侵入性编程
- 通过依赖注入和面向接口实现松耦合
- 基于切面和惯例进行声明式编程
- 通过切面和模板减少样板式代码
几乎Spring所做的任何事情都可以追溯到上述的一条或多条策略。
Spring容器负责创建对象,装配他们,配置他们并管理他们的整个生命周期,从生存到死亡(在这里,可能是new到finalize())。
容器是Spring框架的核心。Spring自带了多个容器实现,可以归为两种不同的类型。bean工厂(由org.springframework.beans.factory.eanFactory接口定义)是最简单的容器,提供基本的DI支持。应用上下文 (由org.springframework.context.ApplicationContext接口定义)基于BeanFactory构建,并提供应用框架级别的服务。
应用上下文
Spring自带了多种类型的应用上下文,下面罗列几个最可能遇到的。
- AnnotationConfigApplicationContext:从一个或多个基于Java的配置类中加载Spring应用上下文。
- AnnotationConfigWebApplicationContext:从一个或多个基于Java的配置类中加载Spring Web应用上下文。
- ClassPathXmlApplicationContext:从类路径下的一个或多个XML配置中加载上下文定义,把应用上下文的定义文件作为类资源。
- FileSystemXmlApplicationContext:从文件系统下的一个或多个XML配置文件中加载上下文定义。
- XmlWebApplicationContext:从Web应用下的一个或多个XML配置文件中加载上下文定义。
后面讨论web方面的,现在简单说一下FileSystemXmlApplicationContext从文件系统中加载应用上下文或者使用ClassPathXmlApplicationContext从类路径中加载应用上下文。
无论是从文件系统中装载应用上下文还是从类路径下装载应用上下文,将bean加载到bean工厂的过程都是相似的。
如何使用FileSystemXmlApplicationContext加载上下文:
ApplicationContext context = new FileSystemXmlApplicationContext("C:/knight.xml");
类似,使用ClassPathXmlApplicationContext从应用 的类路径下加载应用上下文:
ApplicationContext context = new ClassPathXmlApplicationContext("knight.xml");
使用FileSystemXmlApplicationContext和使用ClassPathXmlApp-licationContext的区别在于:FileSystemXmlApplicationContext在指定的文件系统路径下查找knight.xml文件;而ClassPathXmlApplicationContext是在所有的类路径(包含JAR文件)下查找 knight.xml文件。
使用AnnotationConfigApplicationContext在Java配置中加载应用上下文:
ApplicationContext context = new AnnotationConfigApplicationContext(类的全限定名.class);
应用上下文准备就绪之后,我们就可以调用上下文的getBean()方法从Spring容器中获取bean。
Bean的装配
Spring提供了三种主要bean的装配机制:
- 在XML中进行显式配置。
- 在Java中进行显式配置。
- 隐式的bean发现机制和自动装配。
Spring从两个角度来实现自动装配:
- 组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。
- 自动装配(autowiring):Spring自动满足bean之间的依赖。
@Component 表明该类会作为组件类,并告知Spring要为这个类创建bean。
@Component(“希望的ID”)表明该类会作为组件类,并告知Spring要为这个类创建指定ID的bean。
@ComponentScan 扫描与配置类相同的包和其下的子包。
@ComponentScan(“希望扫描的包名”) 扫描指定的包和其下的子包。
@ComponentScan(basePackages=“希望扫描的包名”) 扫描指定的包和其下的子包。与上一个意义相同。
@ComponentScan(basePackages={“希望扫描的包名1”,“希望扫描的包名2”,...}) 扫描指定的多个包和其下的子包。
@ComponentScan(basePackageClasses={Music.class,Music1.class,...}) 扫描指定类所在的包和其下的子包。
Spring应用上下文中所有的bean都会给定一个ID,默认给定的ID是将类名第一个字母变成小写(比如Music这个bean所给定的ID就是music)。
如果想设置不同的ID,可以采用@Component(“希望的ID”)或者@Named(“所希望的ID”),Spring是支持@Named作为@Component注解的替代方案。
自动装配是让Spring自动满足bean依赖的一种方法,为了声明要进行自动装配,我们采用@Autowired注解。
@Autowirde注解可以用在构造方法、setter方法和其他任何的方法上,Spring都会尝试满足方法参数上所声明依赖。
有且仅有一个bean匹配时,就会被装配进来,如果没有bean匹配,则会抛出异常,如果下个避免抛出异常,可采用@Autowired(required=false)注解。
显示装配
当想要将第三方类库中的组建装配到自己的应用中时,是没办法在它的类上添加@component和@Autowired注解,因此只能采用显示装配的方案。
如何在javaConfig中声明bean?
@Bean(name="希望的bean ID")//默认id为方法名,此处为music
public Music music(){
return new Music();
}
@Bean注解会告诉Spring这个方法将返回一个对象,该对象要注册为Spring应用上下文中的bean。方法体中包含了最终产生bean实例的逻辑。
如何实现注入呢?
- 在JavaConfig中最简单的装配bean的方式就是引用创建bean的方法
@Bean
public People people(){
return new People(music());
}
看起来,Music是通过调用music()方法的到的,但情况并非完全如此。因为music()方法上他Ian加了@Bean注解,Spring会拦截所有对它的调用,并确保直接返回该方法所创建的bean,而不是每次都对其进行实际的调用。
所以,默认情况下,Spring中的bean都是单例的。
- 通过参数的形式注入bean
@Bean
public People people(Music music){
return new People(music);
}
在people方法请求一个Music作为一个参数,当Spring调用people()创建People ben的时候,它会自动装配一个Music到配置方法中。通过这种方式引用其他的bean通常是最佳的选择。
高级装配
- 环境与profile
@profile 该注解可以指定bean属于哪个profile,比如@profile("dev") 代表其下的bean属于dev的profile。
Spring3.1中,@profie注解只能用在类级别上,Spring3.2开始也可以在方法级别上使用@profile注解。
除了使用@profile注解,也可以在xml中的 <beans>节点的profile属性配置,如:<beans profile="dev"></beans>
也可以在<beans>元素中嵌套<beans>元素。
注:
- 没有被指定profile的bean始终都会被创建。
- 激活profile
Spring 通过spring.profiles.active 和 spring.profile.default 两个属性来确定激活那个profile,先去前者,前者没值取后者,后者也没值则不激活任何profile。
没有激活任何profile,Spring只会创建没有定义在profile中的bean。
- 如何设置上述的 spring.profiles.active 和 spring.profile.default 两个属性呢?
- 作为DispatcherServlet的初始化参数
- 作为Web应用的上下文参数
- 作为JNDI条目
- 作为环境变量
- 作为JVM的系统属性
- 在集成测试类上,使用@ActiveProfiles注解配置
注:profile可以激活多个,profile的名称用逗号分隔
bean的作用域
在默认情况下,Spring应用上下文中的所有bean都是以单例(singleton)的形式创建的。
单例是默认作用域。
Spring定义了多种作用域:
- 单例(singleton):在整个应用中,只创建bean的一个实例。
- 原型(prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的实例。
- 会话(session):在Web应用中,为每个会话创建一个bean实例。
- 请求(request):在Web应用中,为每个请求创建一个bean实例。
我们可以使用@Scope注解来设置作用域:
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)或者@Scope("prototype")