原创:微信公众号 【阿Q说代码】,欢迎分享,转载请保留出处。
一提到Spring,大家最先想到的是啥?是AOP和IOC的两大特性?是Spring中Bean的初始化流程?还是基于Spring的Spring Cloud全家桶呢?
今天我们就从Spring的IOC特性入手,聊一聊Spring中把Bean注入Spring容器的几种方式。
我们先来简单了解下IOC的概念:IOC即控制反转,也称为依赖注入,是指将对象的创建或者依赖关系的引用从具体的对象控制转为框架或者IOC容器来完成,也就是依赖对象的获得被反转了。
可以简单理解为原来由我们来创建对象,现在由
Spring来创建并控制对象。
依稀记得最早接触Spring的时候,用的还是SSH框架,不知道大家对这个还有印象吗?所有的bean的注入得依靠xml文件来完成。
它的注入方式分为:set方法注入、构造方法注入、字段注入,而注入类型分为值类型注入(8种基本数据类型)和引用类型注入(将依赖对象注入)。
以下是set方法注入的简单样例
<bean name="teacher" class="org.springframework.demo.model.Teacher">
<property name="name" value="阿Q"></property>
</bean>
对应的实体类代码
public class Teacher {
private String name;
public void setName(String name) {
this.name = name;
}
}
xml方式存在的缺点如下:
xml文件配置起来比较麻烦,既要维护代码又要维护配置文件,开发效率低;xml时,无论是将xml一次性装进内存,还是一行一行解析,都会占用内存资源,影响性能。随着Spring的发展,Spring 2.5开始出现了一系列注解,除了我们经常使用的@Controller、@Service、@Repository、@Component 之外,还有一些比较常用的方式,接下来我们简单了解下。
当我们需要引入第三方的jar包时,可以用@Bean注解来标注,同时需要搭配@Configuration来使用。
@Configuration用来声明一个配置类,可以理解为xml的<beans>标签
@Bean 用来声明一个bean,将其加入到Spring容器中,可以理解为xml的<bean>标签
简单样例:将 RedisTemplate 注入 Spring
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
......
return redisTemplate;
}
}
我们在翻看Spring源码的过程中,经常会看到@Import注解,它也可以用来将第三方jar包注入Spring,但是它只可以作用在类上。
例如在注解EnableSpringConfigured上就包含了@Import注解,用于将SpringConfiguredConfiguration配置文件加载进Spring容器。
@Import(SpringConfiguredConfiguration.class)
public @interface EnableSpringConfigured {}
@Import的value值是一个数组,一个一个注入比较繁琐,因此我们可以搭配ImportSelector接口来使用,用法如下:
@Configuration
@Import(MyImportSelector.class)
public class MyConfig {}
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"org.springframework.demo.model.Teacher","org.springframework.demo.model.Student"};
}
}
其中selectImports方法返回的数组就会通过@Import注解注入到Spring容器中。
无独有偶,ImportBeanDefinitionRegistrar接口也为我们提供了注入bean的方法。
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
......
}
我们点击AspectJAutoProxyRegistrar类,发现它实现了ImportBeanDefinitionRegistrar接口,它的registerBeanDefinitions方法便是注入bean的过程,可以参考下。
如果觉得源代码比较难懂,可以看一下我们自定义的类
@Configuration
@Import(value = {MyImportBeanDefinitionRegistrar.class})
public class MyConfig {}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
RootBeanDefinition tDefinition = new RootBeanDefinition(Teacher.class);
// 注册 Bean,并指定bean的名称和类型
registry.registerBeanDefinition("teacher", tDefinition);
}
}
}
这样我们就把Teacher类注入到Spring容器中了。
提到FactoryBean,就不得不与BeanFactory比较一番。
BeanFactory : 是 Factory, IOC容器或者对象工厂,所有的Bean都由它进行管理FactoryBean : 是Bean ,是一个能产生或者修饰对象生成的工厂 Bean,实现与工厂模式和修饰器模式类似那么FactoryBean是如何实现bean注入的呢?
先定义实现了FactoryBean接口的类
public class TeacherFactoryBean implements FactoryBean<Teacher> {
/**
* 返回此工厂管理的对象实例
**/
@Override
public Teacher getObject() throws Exception {
return new Teacher();
}
/**
* 返回此 FactoryBean 创建的对象的类型
**/
@Override
public Class<?> getObjectType() {
return Teacher.class;
}
}
然后通过 @Configuration + @Bean的方式将TeacherFactoryBean加入到容器中
@Configuration
public class MyConfig {
@Bean
public TeacherFactoryBean teacherFactoryBean(){
return new TeacherFactoryBean();
}
}
注意:我们没有向容器中注入Teacher, 而是直接注入的TeacherFactoryBean,然后从容器中拿Teacher这个类型的bean,成功运行。
看到这个接口,不知道对于翻看过Spring源码的你来说熟不熟悉。如果不熟悉的话请往下看,要是熟悉的话就再看一遍吧?。
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
// 注册bean到spring容器中
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
@FunctionalInterface
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
BeanFactoryPostProcessor接口是BeanFactory的后置处理器,方法postProcessBeanFactory对bean的定义进行控制。今天我们重点来看看postProcessBeanDefinitionRegistry方法:它的参数是BeanDefinitionRegistry,顾名思义就是与BeanDefinition注册相关的。

通过观察该类,我们发现它里边包含了registerBeanDefinition方法,这个不就是我们想要的吗?为了能更好的使用该接口来达到注入bean的目的,我们先来看看Spring是如何操作此接口的。

看下invokeBeanFactoryPostProcessors方法,会发现没有实现PriorityOrdered和Ordered的bean(这种跟我们自定义的实现类有关)会执行以下代码。
while (reiterate) {
......
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
......
}
进入该方法
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors,
BeanDefinitionRegistry registry) {
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
}
会发现实现了BeanDefinitionRegistryPostProcessor接口的bean,其postProcessBeanDefinitionRegistry方法会被调用,也就是说如果我们自定义接口实现该接口,它的postProcessBeanDefinitionRegistry方法也会被执行。
话不多说,直接上代码。自定义接口实现类
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
/**
* 初始化过程中先执行
**/
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Teacher.class);
//Teacher 的定义注册到spring容器中
registry.registerBeanDefinition("teacher", rootBeanDefinition);
}
/**
* 初始化过程中后执行
**/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
}
启动类代码
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
MyBeanDefinitionRegistryPostProcessor postProcessor = new MyBeanDefinitionRegistryPostProcessor();
//将自定义实现类加入 Spring 容器
context.addBeanFactoryPostProcessor(postProcessor);
context.refresh();
Teacher bean = context.getBean(Teacher.class);
System.out.println(bean);
}
启动并打印结果
org.springframework.demo.model.Teacher@2473d930
发现已经注入到Spring容器中了。
以上就是我们总结的几种将bean注入Spring容器的方式,赶快行动起来实战演练一下吧!
阿Q将持续更新java实战方面的文章,感兴趣的可以关注下,也可以来技术群讨论问题呦!
我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
我正在使用Ruby2.1.1和Rails4.1.0.rc1。当执行railsc时,它被锁定了。使用Ctrl-C停止,我得到以下错误日志:~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`gets':Interruptfrom~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`verify_server_version'from~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.
question的一些答案关于redirect_to让我想到了其他一些问题。基本上,我正在使用Rails2.1编写博客应用程序。我一直在尝试自己完成大部分工作(因为我对Rails有所了解),但在需要时会引用Internet上的教程和引用资料。我设法让一个简单的博客正常运行,然后我尝试添加评论。靠我自己,我设法让它进入了可以从script/console添加评论的阶段,但我无法让表单正常工作。我遵循的其中一个教程建议在帖子Controller中创建一个“评论”操作,以添加评论。我的问题是:这是“标准”方式吗?我的另一个问题的答案之一似乎暗示应该有一个CommentsController参
在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList()Obt
SPI接收数据左移一位问题目录SPI接收数据左移一位问题一、问题描述二、问题分析三、探究原理四、经验总结最近在工作在学习调试SPI的过程中遇到一个问题——接收数据整体向左移了一位(1bit)。SPI数据收发是数据交换,因此接收数据时从第二个字节开始才是有效数据,也就是数据整体向右移一个字节(1byte)。请教前辈之后也没有得到解决,通过在网上查阅前人经验终于解决问题,所以写一个避坑经验总结。实际背景:MCU与一款芯片使用spi通信,MCU作为主机,芯片作为从机。这款芯片采用的是它规定的六线SPI,多了两根线:RDY和INT,这样从机就可以主动请求主机给主机发送数据了。一、问题描述根据从机芯片手
转自:spring.profiles.active和spring.profiles.include的使用及区别说明下文笔者讲述spring.profiles.active和spring.profiles.include的区别简介说明,如下所示我们都知道,在日常开发中,开发|测试|生产环境都拥有不同的配置信息如:jdbc地址、ip、端口等此时为了避免每次都修改全部信息,我们则可以采用以上的属性处理此类异常spring.profiles.active属性例:配置文件,可使用以下方式定义application-${profile}.properties开发环境配置文件:application-dev
这是针对我无法破坏的现有公共(public)API,但我确实希望对其进行扩展。目前,该方法采用字符串或符号或任何其他在作为第一个参数传递给send时有意义的内容我想添加发送字符串、符号等列表的功能。我可以只使用is_a吗?数组,但还有其他发送列表的方法,这不是很像ruby。我将调用列表中的map,所以第一个倾向是使用respond_to?:map。但是字符串也会响应:map,所以这行不通。 最佳答案 如何将它们全部视为数组?String的行为与仅包含String的Array相同:deffoo(obj,arg)[*arg].eac
我今天看到了一个ruby代码片段。[1,2,3,4,5,6,7].inject(:+)=>28[1,2,3,4,5,6,7].inject(:*)=>5040这里的注入(inject)和之前看到的完全不一样,比如[1,2,3,4,5,6,7].inject{|sum,x|sum+x}请解释一下它是如何工作的? 最佳答案 没有魔法,符号(方法)只是可能的参数之一。这是来自文档:#enum.inject(initial,sym)=>obj#enum.inject(sym)=>obj#enum.inject(initial){|mem
我创建了一个由于“在运行时执行的单例元类定义”而无法编码的对象(这段代码的描述是否正确?)。这是通过以下代码执行的:#defineclassXthatmyusesingletonclassmetaprogrammingfeatures#throughcallofmethod:break_marshalling!classXdefbreak_marshalling!meta_class=class我该怎么做才能使对象编码正确?是否可以从对象instance_of_x的classX中“移除”单例组件?我真的需要一个建议,因为我们的一些对象需要通过Marshal.dump序列化机制进行缓存。