《Spring源码解析-入门Spring的IOC思想》首发橙寂博客转发请加此提示html
你们在面试的时候,应该都会碰到这么一个问题。请浅谈一下Spring IOC
(控制反转)思想?或者是解释下什么是DI
(依赖注入)?。本篇会从Spring官方文档
的角度,结合本身的工做经验 ,给你们讲一下本身对Spring
的一些理解。java
Spring Core
是Spring
的核心。而Spring
的核心讲的就是一个IOC
思想。Spring
的IOC
容器管理着是一个或者多个Bean
。在一个Spring
应用启动,Spring
会根据咱们提供的元数据。 作一个自省
的过程。当咱们真正用一些Bean
时。这时咱们能够经过注解(@Autowired)的方式或者是主动调用ApplicationContext
的getBean()
方法根据标识符(Id或者带包名的类名)从容器中取得咱们想要的 Bean
对象。这就是IOC
也叫作DI
。web
固然在容器中每一个Bean
都是有个惟一的Id
的,若是咱们不显示的提供,Spring
会根据Java的规范默认按照驼峰命名法命名(首字母小写)。(例如:postService,postDao)面试
若是想Spring
去管理Bean
那么你就得告诉Spring
你须要管理的Bean
。这个过程叫作配置元数据。配置元数据有两中方式。 每一个Bean
在Spring
中被定义成了BeanDefinition
这个对象里面保存咱们这个Bean
的一些基本属性:spring
bean
的链接池的的大小,或者链接数。Bean
的做用域。Bean
的两种方式在咱们的平常工做中,咱们会使用websocket
@Commpont
@Service
@Configuration
//表明这是一个配置类
public class QuartzConfig {
/** * 往容器中初始注入scheduler * @return * @throws SchedulerException */
@Bean
public Scheduler scheduler() throws SchedulerException{
SchedulerFactory schedulerFactoryBean = new StdSchedulerFactory();
return schedulerFactoryBean.getScheduler();
}
}
复制代码
以上注解都是告诉Spring
我须要将带这些注解的Bean
教给Spring
容器管理。除了这个具体每一个注解带的含义是不同的。有想要了解的本身去了解下。session
dao.xmlapp
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="accountDao"
class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions for data access objects go here -->
</beans>
复制代码
以上是一个Dao
的配置文件。接下来程序去加载这个文件就行了。socket
//这里能够加载多个文件,分隔。
ApplicationContext context = new ClassPathXmlApplicationContext("daos.xml");
// 经过Id得到容器中的对象
JpaAccountDao dao = context.getBean("accountDao");
// 使用对象中的方法
List<String> userList = dao.getUsernameList();
复制代码
元数据配置好了,可是一般在咱们平常的使用中咱们的Bean
确定是依赖了其余Bean
的。 好比一个Controller
中须要注入Service
等等。 因此在配置的时候咱们就须要把须要依赖的类(或属性)注入进去。ide
构造方式的形式注入Bean
package x.y;
//构造方式的形式注入
public class ThingOne {
public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
// ...
}
}
<beans>
<bean id="beanOne" class="x.y.ThingOne">
<constructor-arg ref="beanTwo"/>
<constructor-arg ref="beanThree"/>
</bean>
<bean id="beanTwo" class="x.y.ThingTwo"/>
<bean id="beanThree" class="x.y.ThingThree"/>
</beans>
复制代码
构造方式的形式注入属性
package examples;
public class ExampleBean {
// Number of years to calculate the Ultimate Answer
private int years;
// The Answer to Life, the Universe, and Everything
private String ultimateAnswer;
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
//第一种方式
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>
//第二种方式
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
</bean>
复制代码
Setter
方式注入
public class ExampleBean {
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
public void setBeanOne(AnotherBean beanOne) {
this.beanOne = beanOne;
}
public void setBeanTwo(YetAnotherBean beanTwo) {
this.beanTwo = beanTwo;
}
public void setIntegerProperty(int i) {
this.i = i;
}
}
<bean id="exampleBean" class="examples.ExampleBean">
<!-- setter injection using the nested ref element -->
<property name="beanOne">
<ref bean="anotherExampleBean"/>
</property>
<!-- setter injection using the neater ref attribute -->
<property name="beanTwo" ref="yetAnotherBean"/>
<property name="integerProperty" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
复制代码
这种方式应该是目前用的最多的一种形式。特别是在SpringBoot
项目中,抛弃xml
配置的方式,全局使用了注解的方式。 (题外话:SpringBoot中的自动装配机制其实跟Spring
的机制是相通的。Springboot内置了一些自动装配的配置类。因此只须要改改配置文件。就能自动注入咱们须要类)
当你想要在你的Bean
中使用@Autoried
@Resource(name = "bean的名字")
注入您须要的Bean
。 你必定要肯定Bean
存在于容器中。(也就是说必定要先提供的元数据)。
下面讲几个在平常开发中常常会用到的例子
@Controller
public class BaseController {
//系统用户
@Autowired
public SysUserService sysUserService;
//这个name必定是你配置的名字,若是你没配那么默认是首字母小写
@Resource(name = "sysPermissionService")
public SysPermissionService sysPermissionService;
}
复制代码
上面两个例子是在开发常用的。
自动装配机制
上面讲了怎么往Bean
对象中注入属性以及对象
。而且演示了怎么往Bean
中注入另外一个Bean
。那么SpringIOC
容器是怎么作到两个Bean
之间互相协做的呢?这彻底取决于IOC
的自动装配机制。自动装配能够经过name
,'type','构造函数'等模式来实现这个功能。 默认是经过Type
来实现的。也是说好比你想要个String
类型的对象它就会给你注入一个String
类型的。
Bean
的做用域每一个Bean
在容器中是有本身的做用域的。一共存在singleton
(单例),prototype
(原型),request
,'session','application','websocket'。 这里主要讲一下'singleton'和'prototype'。我在面试中被问到过这二者的区别,我是这么回答的:singleton是无状态的,prototype是有状态的。而后我举了个例子,好比有个orderService
购物车服务,一个客户是否是有一个购物车,并且购物车是不能被共享的。也就是每一个人都要建立一个购物车。这就是prototype
原型模式。每次获取Bean
都会有一个全新的Bean
。因此说prototype
有状态. 而singleton
容器中只有一个实例。使用singleton
定义的实例在同一个线程中A对象跟B对象都依赖的是同一个实例。IOC
中的对象默认是singleto
做用域。 有想要了解其余的参考spring官方文档Bean的做用域
IOC
内置了一些接口。方便咱们在SpringIOC
外部操做Bean
对容器进行扩展。好比ApplicationContextAware
接口。BeanPostProcessor
接口,BeanFactoryPostProcessor
接口都内置了回调方法。 这里给你们讲一个实用的工具类就是,获取ApplicationContext
这个容器对象这样咱们就能操做getBean方法
。 下面咱们会经过继承ApplicationContextAware
来实现。
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
private static ApplicationContext applicationContext = null;
/** * 取得存储在静态变量中的ApplicationContext. */
public static ApplicationContext getApplicationContext() {
assertContextInjected();
return applicationContext;
}
/** * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. */
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
assertContextInjected();
return (T) applicationContext.getBean(name);
}
/** * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. */
public static <T> T getBean(Class<T> requiredType) {
assertContextInjected();
return applicationContext.getBean(requiredType);
}
/** * 清除SpringContextHolder中的ApplicationContext为Null. */
public static void clearHolder() {
applicationContext = null;
}
/** * 实现ApplicationContextAware接口, 注入Context到静态变量中. */
@Override
public void setApplicationContext(ApplicationContext appContext) {
applicationContext = appContext;
}
/** * 实现DisposableBean接口, 在Context关闭时清理静态变量. */
@Override
public void destroy() throws Exception {
SpringContextHolder.clearHolder();
}
/** * 检查ApplicationContext不为空. */
private static void assertContextInjected() {
Validate.validState(applicationContext != null, "applicaitonContext属性未注入, 请在applicationContext.xml中定义SpringContextHolder.");
}
}
复制代码
想要了解其它两个的使用参考spring官方文档容器扩展
SpringIOC
是学习Spring
的核心。下面笔者会带着你们一块儿去学习Spring Core
的源码。敬请期待。