赞
踩
Spring Framework是一个强大且广泛使用的Java应用程序框架,它提供了众多功能和工具,其中之一就是Spring容器。Spring容器是Spring应用程序的核心,它负责管理和维护对象(通常称为"Bean")的生命周期。在Spring中,Bean的加载和获取是常见的操作,本文将总结Spring Bean的加载和获取方式,以帮助开发者更好地理解和使用Spring框架。
在Spring中,最传统的方式是使用XML配置文件定义Bean。通过在XML配置文件中声明Bean的定义,Spring容器会在应用程序启动时加载这些Bean并管理它们的生命周期。以下是一个简单的XML配置示例:
<!--开启注解扫描-->
<context:component-scan base-package="com.fd.spring"/>
<!--注入bean-->
<bean class="com.fd.spring.domain.User" id="user"/>
测试:
public static void main( String[] args )
{
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
System.out.println(applicationContext.getBean(User.class));
}
Bean类添加自动注入注解
@Component // 自动注入注解
public class User {
}
spring.xml配置文件
<!--开启注解扫描-->
<context:component-scan base-package="com.fd.spring"/>
测试结果:
创建配置类
@Configuration
@ComponentScan(basePackages = "com.fd.spring") // 开启注解扫描
public class SpringConfig {
}
测试方法:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
System.out.println(applicationContext.getBean(User.class));
测试结果:
使用@Bean加载第三方bean,并将所在类定义为配置类或Bean
@Configuration
public class SpringConfig1 {
@Bean
public Book book() {
return new Book();
}
}
测试方法:
AnnotationConfigApplicationContext applicationContext1 = new AnnotationConfigApplicationContext(SpringConfig1.class);
for (String name : applicationContext1.getBeanDefinitionNames()) {
System.out.println(name);
}
扩展:
根据条件确认是否加载Bean
引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
@Configuration @Import(User.class) public class SpringConfig1 { @Bean public Book book() { return new Book(); } @Bean @ConditionalOnClass(Mouse.class) // 存在Mouse类就加载Cat public Cat tom() { return new Cat(); } @Bean @ConditionalOnMissingClass("com.fd.spring.domain.Mouse") // 不存在Mouse类就加载Dog public Dog dog() { return new Dog(); } @Bean @ConditionalOnBean(User.class) // 容器存在User bean则加载Food public Food food() { return new Food(); } }
测试结果:
类似注解:
@ConditionalOnBean(仅仅在当前上下文中存在某个对象时,才会实例化一个Bean)
@ConditionalOnClass(某个class位于类路径上,才会实例化一个Bean)
@ConditionalOnExpression(当表达式为true的时候,才会实例化一个Bean)
@ConditionalOnMissingBean(仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean)
@ConditionalOnMissingClass(某个class类路径上不存在的时候,才会实例化一个Bean)
@ConditionalOnNotWebApplication(不是web应用)
使用@Import注解导入要注入的bean对应的字节码,
@Import(User.class)
public class SpringConfig2 {
}
被导入的bean无需使用注解声明为bean,此形式可以有效的降低源代码与Spring技术的耦合度,在spring技术底层及诸多框架的整合中大量使用
public class User {
}
测试:
AnnotationConfigApplicationContext applicationContext2 = new AnnotationConfigApplicationContext(SpringConfig2.class);
for (String name : applicationContext2.getBeanDefinitionNames()) {
System.out.println(name);
}
测试结果:
使用@Import注解导入配置类,会把配置类内的bean一起加载
@Import(SpringConfig1.class)
public class SpringConfig2 {
}
测试结果:
使用上下文对象在容器初始化完毕后注入bean
AnnotationConfigApplicationContext applicationContext2 = new AnnotationConfigApplicationContext(SpringConfig2.class);
// 手动注册
applicationContext2.register(Dog.class);
for (String name : applicationContext2.getBeanDefinitionNames()) {
System.out.println(name);
}
测试结果:
导入实现了ImportSelector接口的类,实现对导入源的编程式处理(动态加载)
public class MySelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 判断导入类元数据是否存在XX注解
boolean flag = importingClassMetadata.hasAnnotation("com.fd.spring.annotation.MyAnnotation");
// 如果存在就导入User类,否则导入Book类
if (flag) {
return new String[] {"com.fd.spring.domain.User"};
} else {
return new String[] {"com.fd.spring.domain.Book"};
}
}
}
自定义注解
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}
@MyAnnotation
@Import(MySelector.class)
public class SpringConfig3 {
}
测试方法:
AnnotationConfigApplicationContext applicationContext3 = new AnnotationConfigApplicationContext(SpringConfig3.class);
for (String name : applicationContext3.getBeanDefinitionNames()) {
System.out.println(name);
}
测试结果:
取消自定义注解
// @MyAnnotation
@Import(MySelector.class)
public class SpringConfig3 {
}
测试结果:
扩展:
通过ImportSelector接口,我们就可以根据条件判断确认是否加载Bean
public class MySelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
try {
// 如果存在Mouse类则加载Cat类
Class<?> clazz = Class.forName("com.fd.spring.domain.Mouse");
return new String[] {"com.fd.spring.domain.Cat"};
} catch (ClassNotFoundException e) {
return new String[]{};
}
}
}
导入实现了ImportBeanDefinitionRegistrar接口的类,通过BeanDefinition的注册器注册实名bean,实现对容器中bean的裁定,例如对现有bean的覆盖,进而达成不修改源代码的情况下更换实现的效果
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Book.class).getBeanDefinition();
registry.registerBeanDefinition("book1",beanDefinition);
}
}
配置类
@Import(MyImportBeanDefinitionRegistrar.class)
public class SpringConfig4 {
}
测试方法:
AnnotationConfigApplicationContext applicationContext4 = new AnnotationConfigApplicationContext(SpringConfig4.class);
for (String name : applicationContext4.getBeanDefinitionNames()) {
System.out.println(name);
}
测试结果:
导入实现了BeanDefinitionRegistryPostProcessor接口的类,通过BeanDefinition的注册器注册实名bean,实现对容器中bean的最终裁定
public class MyBeanDefinitionRegisterPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Dog.class).getBeanDefinition();
beanDefinitionRegistry.registerBeanDefinition("dog", beanDefinition);
}
}
配置类
@Import(MyBeanDefinitionRegisterPostProcessor.class)
public class SpringConfig5 {
}
测试结果:
在Spring中,Bean的实例化、定位、配置应用程序中的对象及建立对象间的依赖关系,都是在IoC容器中进行的。因此,要在Spring中获取Bean,本质上就是从IoC容器当中获取Bean。
在Spring中,BeanFactory是IoC容器的实际代表者,该接口提供了IoC容器最基本功能。同时,Spring还提供了另外一种类型的容器:ApplicationContext容器。
ApplicationContext容器包括BeanFactory容器的所有功能(BeanFactory的子接口),提供了更多面向应用的功能,它提供了国际化支持和框架事件体系,更易于创建实际应用。
一般情况,我们称BeanFactory为IoC容器,称ApplicationContext为应用上下文。但有时为了方便,也将ApplicationContext称为Spring容器。
通常不建议使用BeanFactory,但BeanFactory 仍然可以用于轻量级的应用程序,如移动设备或基于applet的应用程序,其中它的数据量和速度是显著。
通过BeanFactory来获取Bean。基于xml配置文件的时代,可以通过如下方式获得BeanFactory,再通过BeanFactory来获得对应的Bean。这种写法估计也只会出现在古老的项目当中。
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("spring.xml"));
beanFactory.getBean(User.class);
在项目启动时先获取ApplicationContext对象,然后将其存储在一个地方,以便后续用到时进行使用。这里只介绍基于SpringBoot启动实现:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
// 启动时,保存上下文,并保存为静态
ConfigurableApplicationContext ac = SpringApplication.run(Application.class, args);
SpringContextUtil.setApplicationContext(ac);
}
}
对应的SpringContextUtil类如下:
public class SpringContextUtil {
private static ApplicationContext ac;
public static <T> T getBean(String beanName, Class<T> clazz) {
T bean = ac.getBean(beanName, clazz);
return bean;
}
public static void setApplicationContext(ApplicationContext applicationContext){
ac = applicationContext;
}
}
启动Spring项目时,直接获取到ApplicationContext的引用,然后将其存储到工具类当中。在使用时,则从工具类中获取ApplicationContext容器,进而从中获得Bean对象。
在上面的方式中,XmlBeanFactory已经被废弃,但可以通过其他方式来获得BeanFactory,然后再从BeanFactory中获得指定的Bean。获取BeanFactory实例最简单的方式就是实现BeanFactoryAware接口。
@Component public class BeanFactoryHelper implements BeanFactoryAware { private static BeanFactory beanFactory; /** * 重写 BeanFactoryAware 接口的方法 * @param beanFactory :参数赋值给本地属性之后即可使用 BeanFactory * @throws BeansException BeansException */ @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { BeanFactoryHelper.beanFactory = beanFactory; } /** * 根据名称获取容器中的对象实例 * @param beanName :注入的实例必须已经存在容器中,否则抛异常:NoSuchBeanDefinitionException * @return Object */ public static Object getBean(String beanName) { return beanFactory.getBean(beanName); } /** * 根据 class 获取容器中的对象实例 * @param requiredType :被注入的必须已经存在容器中,否则抛异常:NoSuchBeanDefinitionException * @param <T> Class * @return 对象 */ public static <T> T getBean(Class<T> requiredType) { return beanFactory.getBean(requiredType); } /** * 判断 spring 容器中是否包含指定名称的对象 * @param beanName bean名称 * @return 是否存在 */ public static boolean containsBean(String beanName) { return beanFactory.containsBean(beanName); } //其它需求皆可参考 BeanFactory 接口和它的实现类 }
在上述工具类中,便是基于BeanFactoryAware的特性,获得了BeanFactory,然后再通过BeanFactory来获得指定的Bean。
通过实现ApplicationContextAware接口,在Spring容器启动时将ApplicationContext注入进去,从而获取ApplicationContext对象,这种方法也是常见的获取Bean的一种方式,推荐使用。
@Component public class SpringContextUtils implements ApplicationContextAware { private static ApplicationContext ac; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { ac = applicationContext; } public static <T> T getBean(Class<T> clazz) { T bean = ac.getBean(clazz); return bean; } }
此种方式依旧是先获得ApplicationContext容器,然后从中获取Bean对象,只不过是基于继承ApplicationObjectSupport类实现的。
@Component public class SpringContextUtils extends ApplicationObjectSupport { private ApplicationContext applicationContext; public <T> T getBean(Class<T> clazz) { if (applicationContext == null) { applicationContext = getApplicationContext(); } if (applicationContext == null) { return null; } else { return applicationContext.getBean(clazz); } } }
ApplicationObjectSupport类图如下,我们看到它实现了ApplicationContextAware接口,在Spring容器初始化过程中回调方法setApplicationContext来完成ApplicationContext的赋值。
WebApplicationObjectSupport是ApplicationObjectSupport的一个实现类,提供了Web相关的支持。实现原理与ApplicationObjectSupport一样。
@Component public class SpringContextUtils extends WebApplicationObjectSupport { private ApplicationContext applicationContext; public <T> T getBean(Class<T> clazz) { if (applicationContext == null) { applicationContext = getApplicationContext(); } if (applicationContext == null) { return null; } else { return applicationContext.getBean(clazz); } } }
类图如下:
Spring工具类,方便在非Spring管理环境中获取Bean。
@Component public final class SpringUtils implements BeanFactoryPostProcessor{ /** Spring应用上下文环境 */ private static ConfigurableListableBeanFactory beanFactory; @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException{ SpringUtilsS.beanFactory = beanFactory; } /** * 获取对象 * * @param name * @return Object 一个以所给名字注册的bean的实例 * @throws BeansException * */ @SuppressWarnings("unchecked") public static <T> T getBean(String name) throws BeansException{ return (T) beanFactory.getBean(name); } /** * 获取类型为requiredType的对象 * * @param clz * @return * @throws BeansException * */ public static <T> T getBean(Class<T> clz) throws BeansException{ T result = (T) beanFactory.getBean(clz); return result; } /** * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true * * @param name * @return boolean */ public static boolean containsBean(String name){ return beanFactory.containsBean(name); } /** * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) * * @param name * @return boolean * @throws NoSuchBeanDefinitionException * */ public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException{ return beanFactory.isSingleton(name); } /** * @param name * @return Class 注册对象的类型 * @throws NoSuchBeanDefinitionException * */ public static Class<?> getType(String name) throws NoSuchBeanDefinitionException{ return beanFactory.getType(name); } /** * 如果给定的bean名字在bean定义中有别名,则返回这些别名 * * @param name * @return * @throws NoSuchBeanDefinitionException * */ public static String[] getAliases(String name) throws NoSuchBeanDefinitionException{ return beanFactory.getAliases(name); } /** * 获取aop代理对象 * * @param invoker * @return */ @SuppressWarnings("unchecked") public static <T> T getAopProxy(T invoker){ return (T) AopContext.currentProxy(); } }
Spring提供了工具类WebApplicationContextUtils,通过该类可获取WebApplicationContext对象。
这个方法很常见于SpringMVC构建的Web项目中,适用于Web项目的B/S结构。下面两个工具方式的区别是,前者在获取失败时抛出异常,后者返回null。
public class SpringContextUtils2 { private ApplicationContext applicationContext; public static <T> T getBean(ServletContext request, String name, Class<T> clazz){ WebApplicationContext webApplicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(request); // 或者 WebApplicationContext webApplicationContext1 = WebApplicationContextUtils.getWebApplicationContext(request); //webApplicationContext1.getBean(name, clazz); return webApplicationContext.getBean(name, clazz); } }
使用ContextLoader提供的getCurrentWebApplicationContext方法,也是常用的获取WebApplicationContext的一种方法。
具体实现代码如下:
WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
wac.getBean(beanID);
该方法常见于SpringMVC实现的Web项目中。该方式是一种不依赖于Servlet,不需要注入的方式。但是需要注意一点,在服务器启动时和Spring容器初始化时,不能通过该方法获取Spring容器。
总结:
虽然,spring提供了好几种方法(3、4、5、6、7)可以实现在普通的类中继承或实现相应的类或接口来获取spring 的ApplicationContext对象,但是在使用是一定要注意实现了这些类或接口的普通java类一定要在Spring 的配置文件application-context.xml文件中进行配置或注解配置(注入容器)。否则获取的ApplicationContext对象将为null。
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。