IOC(inversion of control)控制反转,这是一种比较重要的解决面向对象设计耦合问题的方式,是Spring框架的核心之一,控制反转一般分为两大类型,一种是依赖注入(dependecy injection)简称DI,另一种是依赖查找(dependency lookup),依赖注入的应用相对广泛,所以现在基本上控制反转=依赖注入,Spring正是使用了依赖注入,所以Spring的IOC特性有时也叫DI
控制反转究竟有什么好处呢?几乎所有的应用都是靠两个或者更多的类相互协作来实现业务逻辑的,一个类需要依赖另外一个类来实现一个业务逻辑,这必然要创建一个引用,才能使用其它类的方法,这会增强程序的耦合度,让测试难以进行。解决这个问题的办法就是对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给他,所以这算的上是一个注入的过程,为什么叫控制反转?因为不再是我去请求这个引用,而是外界实体将引用注入给我,这是被动与主动身份的一个反转。
BeanFactory是Spring bean容器的根接口,是所有bean容器的基础
BeanFactory的方法
分别用介绍每个属性方法,并使用ClassPathXMlApplicationContext做示例
创建一个HelloWorld类
public class HelloWorld {
private String name;
public void hello(){
System.out.println("Hello World!"+name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
在ClassPath下新建一个bean-test.xml文件
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 新建一个HelloWorld的bean,名称为helloWorld
在xml中创建bean的过程其实相当于一个创建对象的过程,只不过这里是交给Spring处理,
Spring利用反射来创建对象
-->
<bean id="helloWorld" class="spring.test.bean.HelloWorld">
<!-- 相当于 HelloWorld helloWorld=new HelloWorld(); -->
<property name="name" value="China"></property>
<!-- helloWorld.setName("China"); -->
</bean>
</beans>
创建容器
public class Test {
public static void main(String[] args) {
BeanFactory beanTest=new ClassPathXmlApplicationContext("bean-test.xml");
System.out.println(beanTest);
}
}
输出结果
org.springframework.context.support.ClassPathXmlApplicationContext@1f17ae12: startup date [Sun Oct 18 14:17:12 CST 2015]; root of context hierarchy
1.静态字符串常量FACTORY_BEAN_PREFIX,在JAVA中接口中定义的变量都会变成Static Final类型.对BeanFactory的转义定义,如果使用Bean的名称检索FactoryBean得到的是工厂生成的对象,如果需要得到工厂本身,需要使用&符号转义
String FACTORY_BEAN_PREFIX = "&";
2.IOC容器是否存在这个bean
boolean containsBean(String name);
根据配置的bean的id即helloWorld来判断这个是否在容器beanTest中存在
public class Test {
public static void main(String[] args) {
BeanFactory beanTest=new ClassPathXmlApplicationContext("bean-test.xml");
System.out.println(beanTest.containsBean("helloWorld"));
}
}
返回值
true
3.根据bean的名称,获取bean的别名数组
String[] getAliases(String name);
上面已经证明了helloWorld这个bean存在于容器中,现在调用函数获取别名数组
public class Test {
public static void main(String[] args) {
@SuppressWarnings("resource")
BeanFactory beanTest=new ClassPathXmlApplicationContext("bean-test.xml");
String[] aliases=beanTest.getAliases("helloWorld");
System.out.println("数组的长度为:"+aliases.length);
for(String str:aliases){//循环输出helloWorld实例的别名
System.out.println(str);
}
}
}
输出结果为
数组的长度为:0
数组的长度为0,是因为我们并没有给helloWorld别名,在看下面的代码,修改bean-test.xml配置文件,给bean加上别名
<bean id="helloWorld" class="spring.test.bean.HelloWorld" <span style="background-color: rgb(255, 204, 204);"><span style="color:#ff0000;">name="alias1"</span></span>>
再运行程序,控制台输出
数组的长度为:1
alias1
4.获取根据Class类型获取实例。此处使用反射,泛型
<T> T getBean(Class<T> requiredType) throws BeansException;
看代码
public class Test {
public static void main(String[] args) {
@SuppressWarnings("resource")
BeanFactory beanTest=new ClassPathXmlApplicationContext("bean-test.xml");
HelloWorld a=beanTest.getBean(HelloWorld.class);//此处运用反射的知识
a.hello();//输出结果:Hello World!China
}
}
这种方式具有一定的局限性,当配置文件中有两个bean属于HelloWorld类型的时候,Spring不知道该取哪个Bean就会报错,在配置文件中加入以下bean配置
<bean id="helloWorld1" class="spring.test.bean.HelloWorld">
<property name="name" value="Beijing"></property>
</bean>
重新运行
Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [spring.test.bean.HelloWorld] is defined: expected single matching bean but found 2: helloWorld1,helloWorld
提醒你此处有两个同类型的bean,没有唯一的bean定义
5.根据类类型和Object对象获取bean, Object... args可变参数的使用。类型安全验证机制
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
6.根据名称获取bean实例
Object getBean(String name) throws BeansException;
示例
public class Test {
public static void main(String[] args) {
@SuppressWarnings("resource")
BeanFactory beanTest=new ClassPathXmlApplicationContext("bean-test.xml");
HelloWorld a=(HelloWorld) beanTest.getBean("helloWorld");
a.hello();//输出:Hello World!China
}
}
7.根据名称和Class类型获取bean实例,相对于getBean(String name)增加了类型验证机制,这是比较安全的做法
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
上面我们已经知道helloWorld是HelloWorld类型的一个bean,示例
HelloWorld a=beanTest.getBean("helloWorld",HelloWorld.class);//类型验证后不需要强制类型转换
HelloWorld b=(HelloWorld) beanTest.getBean("helloWorld");//强制类型转换
Test c=(Test) beanTest.getBean("helloWorld",Test.class);//抛出异常:Exception in thread "main" org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'helloWorld' must be of type [spring.test.bean.Test], but was actually of type [spring.test.bean.HelloWorld]
8.根据名称和对象获取bean实例
Object getBean(String name, Object... args) throws BeansException;
9.获取Class类型
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
这里是典型的反射的用法,Class<?> a=name.getClass();
public class Test {
public static void main(String[] args) {
@SuppressWarnings("resource")
BeanFactory beanTest=new ClassPathXmlApplicationContext("bean-test.xml");
Class<?> a=beanTest.getType("helloWorld");
System.out.println(a);//控制台输出:class spring.test.bean.HelloWorld
}
}
10.是否原型,原型即每次获取的bean都是不同的bean,他们只是配置相同而已
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
public class Test {
public static void main(String[] args) {
@SuppressWarnings("resource")
BeanFactory beanTest=new ClassPathXmlApplicationContext("bean-test.xml");
System.out.println(beanTest.isPrototype("helloWorld"));//false
}
}
上述示例告诉我们helloWorld并不是原型的,它是单例的即每次获取的都是都一个bean,获取的只是不同的引用,同时也可以得出Spring默认配置的bean为单例的,下面改变bean-test.xml配置,配置一个原型的bean
<bean id="helloWorld" class="spring.test.bean.HelloWorld" <span style="background-color: rgb(255, 0, 0);">scope="prototype"</span>>//设置bean的作用域为原型
运行程序
System.out.println(beanTest.isPrototype("helloWorld"));//true
11.是否单例,这个跟原型是相对的
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
12.根据bean的名称验证类型,跟getBean(String name, Class<T> requiredType)有点相似,不同的是此处并不返回实例,返回的是个boolean值
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
使用实例
System.out.println(beanTest.isTypeMatch("helloWorld",HelloWorld.class));//true
System.out.println(beanTest.isTypeMatch("helloWorld",Test.class));//false
13.根据bean的名称判断这个bean是否能跟Resolvable匹配,这是4.2版本增加的新方法
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
这里涉及到Spring核心工具包的ResolvableType类
总结:要想学好框架,反射、泛型、设计模式的知识是必不可少的。
BeanFactory有一个直接实现类,三个子接口,下一步,看SimpleJndiBeanFactory类