背景

先说一说什么叫把对象交给spring管理。它区别于把类交给spring管理。在spring里采用注解方式@Service、@Component这些,实际上管理的是类,把这些类交给spring来负责实例化。

而对象交给spring管理,举个例子,最常见的在配置文件里定义一个bean,或者JavaConfig的方式就是在@Configure标签标注的类里的@Bean对象。这些Bean已经new出来了。是以对象实例的方式交给spring管理的。这些对象往往是与业务无关的基础组件。比如datasource的bean、redis连接池的bean。个数是有限的。

特别是面试的时候千万要注意他们的区别。有时候有的人觉得自己回答没问题,最后面试没通过,很可能是因为没有弄清楚面试的考察点。面试官的逻辑是如果你没弄清楚问题就回答,面试官很可能会怀疑工作中接到任务也会没有弄清楚就直接开干,做出些负产出来。建议面试的时候不但回答问题,同时也可以说对象交给spring管理和类交给spring管理有点类似,但是……这样清楚的表达自己的思考。

方法一:XML配置Bean

这个方法非常常见,举个例子:

<bean id="car" class="com.lm.spring.bean.Car">

这种定义方法相当于Car car = new Car() 然后 注册car到spring容器。用的时候直接用。

既然bean是被实例化后的对象,就涉及到对象实例化的时候要传参数的问题。

<bean id="car" class="com.lm.spring.bean.Car">     <constructor-arg value="Baoma"></constructor-arg>     <constructor-arg value="Red"></constructor-arg>     <constructor-arg value="400000"></constructor-arg></bean>

这是构造器传参方式,相当于

Car car = new Car("Baoma","Red","400000")。当然构造器传参还支持指定参数的index或者name啥的。其中value代表是基本数据类型,也可以传对象,对象用ref=,代表传的是另外一个bean.

经典的配置场景是数据库配置:先配置dataSource,再配置需要引用dataSource的sessionFactory。

<!-- 数据源配置 --> <bean id="ds" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">      <!-- 基本属性 url、user、password -->      <property name="url" value="#{props.url}" />      <property name="username" value="#{props.user}" />      <property name="password" value="#{props.password}" />      <!-- 配置初始化大小、最小、最大 -->      <property name="initialSize" value="#{props.initialPoolSize}" />      <property name="minIdle" value="#{props.minPoolSize}" />      <property name="maxActive" value="#{props.maxPoolSize}" />      <!-- 配置获取连接等待超时的时间 -->      <property name="maxWait" value="#{props.maxIdleTime}" />      <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->      <property name="timeBetweenEvictionRunsMillis" value="#{props.timeBetweenEvictionRunsMillis}" />      <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->      <property name="minEvictableIdleTimeMillis" value="#{props.minEvictableIdleTimeMillis}" />      <!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->      <property name="poolPreparedStatements" value="true" />      <property name="maxPoolPreparedStatementPerConnectionSize" value="20" />  </bean>
 <!-- sessionFactory  --> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">      <property name="dataSource" ref="ds"></property>      <!-- <property name="hibernateProperties"></property> -->      <property name="configLocation" value="classpath:com/chinasofti/etc/conf/hibernate.cfg.xml"></property> </bean>

这些都是常规用法。前段时间见到了另外一种用法,虽然没啥技术难度。确实之前没有这么用过,也觉得很有意思:

<bean id="car" class="java.util.ArrayList">    <property name="cars">      <list>        <ref bean="car1"></ref>        <ref bean="car2"></ref>        <ref bean="car3"></ref>      </list>    </property>    </bean>

这里用property方法传参相当于调用set方法传参。当然,这里除了可以定义ArrayList对象,还可以定义HashMap、HashSet、Array。

我看到这个恍然大悟,原来自己写的一些方法都不地道。比如机房信息,一个公司可能有七八个机房,什么大兴机房、永丰机房啥的。这个内容很少,放在数据库里没啥必要,之前项目就直接在程序里用枚举定义出来,然后放到map里的。如果加了个机房,就改改代码上线呗。

其实更合适的方法,应该是在配置文件里定义一个class="java.util.map"的bean。然后把内容都放到配置文件里。这些每次添加机房,只改配置文件,不改代码,更合理一些。

虽然实际来讲哈,现在都走devops工具发布了,成本工作量是一样的。但是这样写,能明确修改的是配置,不是代码逻辑。影响范围会更明确些。

方法二:@Bean

这个方法是spring boot流行后的常用方法。本质和XML配置方法相同。所有用XML配置文件的方法都可以用这个方法改写。

@Configuration //此处为配置项public class ServiceConfig {    @Bean //此处返回的是一个Spring的配置Bean,与xml的<bean>等价    public IMessageService getMessageService() {//方法名称随便写        return new MessgeServiceImpl();    }}

很多基础组件的使用文档提供的是XML配置形式的,看到过一些刚毕业的同学在抓耳挠腮。因为项目用的spring boot。如果了解他们的本质就会知道可以直接自己这样写。

方法三:BeanFacoty registerSingleton

先上代码,定义一个普通Bean。

@Data
public class User {
private Integer id;
private String name;
private String password;
private Integer age;
}

定义一个被spring可以扫描的类,这个类要实现

BeanFactoryPostProcessor。里面调用registerSingleton注册一个对象。


@Component
public class MyBeanFacoty implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
User user1 = new User();
user1.setId(1);
user1.setName("贾元春");
user1.setAge(27);
configurableListableBeanFactory.registerSingleton("user", user1);
}

}

之后就可以随时进行依赖了。

@RestController
public class JacksonController {
@Resource
private User user;
@GetMapping("/writeStringAsString")
public String writeStringAsString(String toWrite) throws Exception {
System.out.println(user.getAge());
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.writeValueAsString(toWrite);
}

这时候大家是否会有个疑问,XML配置Bean是传统的spring mvc里常用的将对象交给spring来管理的方法,@Bean是spring boot里将对象交给spring来管理的方法。那为什么还要有这个先实现BeanFactoryPostProcessor的方法呢?

因为这种方法可以用来做这件事情,但是不仅仅可以做这件事情。它神通广大,不仅可以将一个对象交给spring管理,还可以将已经交给spring管理的对象拿出来进行修改,还有其他各种的spring初始化的干预都可以做。所以用它来仅仅注册一个Bean有点杀鸡用牛刀的味道。

总结思考

之前也写过一些spring的文章,如:

SpringBoot启动原理

学习Spring的思考框架

专治不会看源码的毛病--spring源码解析AOP篇(2017版)

SpringBoot优雅退出

你看不懂的spring原理是因为不知道这几个概念

Spring参数的自解析--还在自己转换?你out了!

这些文章主要围绕的核心就是spring framework的原理和spring看似基础的应用技巧。

这是我的一个理念。学spring和学厨师很像。基础就是刀工、材料和火候。掌握了基础,通过自己的悟性就可以千变万化了。悟性不是靠教的,有用的还是基础。

spring有很多的扩展内容,spring boot、spring cloud、spring batch。之所以有这些就是因为它做了两件事:第一是控制反转,使用Bean的时候随时取用;第二是aop,把那些可以重用的代码写一份想用的地方都可以生效。

刚毕业的时候,有大师教我们说控制反转和依赖注入是一回事,后来我想想其实不是。控制反转是把对象和类交给spring来管理,由spring来控制器生命周期的过程。依赖注入是在使用对象的时候,可以通过set、构造器和注解方式获取对象的过程。一个是存钱的过程,一个是取钱的过程。联系是存钱是为取钱服务的。

而spring的一些高级框架就是将更多的东西交给spring来管理的过程,比如spring-jdbc,就是通过spring容器就可以调用jdbc了。我个人认为原理了解了,学的价值不大。

spring作者是学音乐出身的。spring牛的地方是它的理念,它没有做什么事情,只是把能替程序员做的事情都做了,给程序员开发带来了春天。把开发变成了艺术。

把对象交给spring管理的3种方法及经典应用的更多相关文章

  1. Spring管理事物两种方式

    Spring管理事物两种方式 1. 编程式事物管理(在开发中不经常使用) 使用步骤 1. 配置数据库事物管理 DataSourceTransactionManager <!--配置事物管理器-- ...

  2. 使用Spring Security3的四种方法概述

    使用Spring Security3的四种方法概述 那么在Spring Security3的使用中,有4种方法: 一种是全部利用配置文件,将用户.权限.资源(url)硬编码在xml文件中,已经实现过, ...

  3. javascript获取json对象的key名称的两种方法

    javascript获取json对象的key名称的两种方法 数据处理中,你可能接收到一个不确定内容格式的json对象,然后要把key的值提取出来.今天试过两种可以提取json key的方法,均可以正常 ...

  4. js对象之间的"继承"的五种方法

    今天要介绍的是,对象之间的"继承"的五种方法. 比如,现在有一个"动物"对象的构造函数. function Animal(){ this.species = & ...

  5. strut1.X和spring整合的二种方法

    第一种集成方法 原理:在Action中取得BeanFactory对象,然后通过BeanFactory获取业务逻辑对象 缺点:产生了依赖,spring的类在action中产生了依赖查找.(注意和依赖注入 ...

  6. Spring实例化Bean三种方法:构造器、静态工厂、实例工厂

    Spring中Bean相当于java中的类,可以通过xml文件对bean进行配置和管理. 一.Bean的实例化: 构造器实例化.静态工厂实例化.实例工厂方式实例化. 目录: 构造器实例化: xml配置 ...

  7. Spring创建对象的几种方法

    一.通过构造器 无参构造器 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns=" ...

  8. js中控制流管理的四种方法

    引自http://es6.ruanyifeng.com/#docs/generator#yield--表达式 1.常用的回调方法 step1(function (value1) { step2(val ...

  9. java对象转换String类型的三种方法

    在很多情况下我们都需要将一个对象转换为String类型.一般来说有三种方法可以实现:Object.toString().(String)Object.String.valueOf(Object).下面 ...

随机推荐

  1. Linux:从windows到linux的shell脚本编码和格式

    从windows到linux的shell脚本编码和格式问题 异常问题 :set ff=unix 启动脚本在启动时报错比如执行sh start.sh,时会报Command not found等等的错误, ...

  2. getopt模块的学习

    在运行程序时,可能需要根据不同的条件,输入不同的命令行选项来实现不同的功能.目前有短选项和长选项两种格式.短选项格式为"-"加上单个字母选项:长选项为"--"加 ...

  3. php结合redis实现高并发下的抢购、秒杀功能 (转)

      抢购.秒杀是如今很常见的一个应用场景,主要需要解决的问题有两个: 1 高并发对数据库产生的压力 2 竞争状态下如何解决库存的正确减少("超卖"问题) 对于第一个问题,已经很容易 ...

  4. leetcode 数组分成和相等的三个部分

    题目: 给你一个整数数组 A,只有可以将其划分为三个和相等的非空部分时才返回 true,否则返回 false. 形式上,如果可以找出索引 i+1 < j 且满足 (A[0] + A[1] + . ...

  5. kong配置upstream实现简单的负载均衡

    目录 通过konga实现 1. 配置upstream 2. 配置Service发布 3. 配置Route,匹配规则 4. 验证结果 通过 Kong Admin API实现 1. 配置upstream ...

  6. 开发必备linux命令大全-稳赚不亏

    我们的服务一般都是在linux系统运行,因此了解一些关于linux命令是必须.接下来将一一详细介绍一些常用的linux的命令 文件操作 远程登录与操作 磁盘挂载 进程管理 启动和结束 系统性能参数查看 ...

  7. 北京大公司:你是熟悉Map集合吗?

    <对线面试官>系列目前已经连载30篇啦,这是一个讲人话面试系列 [对线面试官]Java注解 [对线面试官]Java泛型 [对线面试官] Java NIO [对线面试官]Java反射 &am ...

  8. poj 折半搜索

    poj2549 Sumsets 题目链接: http://poj.org/problem?id=2549 题意:给你一个含有n(n<=1000)个数的数列,问这个数列中是否存在四个不同的数a,b ...

  9. CentOS更换网易yum源

    最新内容和地址参见http://mirrors.163.com/.help/centos.html 1 首先备份/etc/yum.repos.d/CentOS-Base.repo mv /etc/yu ...

  10. Day12 抽象类、接口、内部类-面向对象编程(3)

    抽象类 abstract修饰符可以用来修饰方法也可以用来修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类: 抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类. ...