经典讲述IOC的博客:http://blog.csdn.net/qq_22654611/article/details/52606960
1 控制反转(Inversion of Control)简称IOC,IOC的思想是:Spring容器来实现这些相互依赖对象的创建、协调工作。对象只需要关系业务逻辑本身就可以了。由spring来负责控制对象的生命周期和对象间的关系。从这方面来说,对象如何得到他的协作对象的责任被反转了(IOC、DI)。IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。org.springframework.beans及org.springframework.context包 是Spring IoC容器的基础,ApplicationContext 是BeanFactory的扩展,功能得到了进一步增强,比如更易 与Spring AOP集成、资源处理(国际化处理)、事件传递及各种不同应用层的context实现 BeanFactory 是Spring IoC容器的实际代表者,IoC容器负责容纳此前所描述的bean,并对bean进行管理。BeanFactory是IoC容器的核心接口。 它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。Spring为我们提供了许多易用的BeanFactory实现, XmlBeanFactory就是最常用的一个。
2 XML的配置元数据的基本结构:
<bean name="hello" class="cn.sxt.bean.Hello">
<property name="name" value="张三"></property>
</bean>
Spring IoC容器的实例化:
public class Hello {
private String name;
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("hello,"+name);
}
}
//解析beans.xml文件 生成管理相应的bean对象
ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml");
Hello hello=(Hello)context.getBean("hello");
hello.show();
3.2 实例化bean
3.2.1 构造器实例化Bean:
beans.xml:
<bean id="u" class="cn.sxt.vo.User"/>
ApplicationContext ac=new ClassPathXmlApplicationContext("beans.xml");
User user=(User)ac.getBean("u");
System.out.println(user);
需要注意的是一定要存在一个默认的构造器。
3.2.2 使用静态工厂方法实例化
<!-- factory-method指定创建对象的静态方法 -->
<bean id="u" class="cn.sxt.vo.User" factory-method="newinstance"/>
测试:
ApplicationContext ac=new ClassPathXmlApplicationContext("beans.xml");
User user=(User)ac.getBean("u");
System.out.println(user);
3.2.3 使用实例化方法实例
<bean id="userFactory" class="cn.sxt.factory.UserDynamicFactory"/>
<!-- factory-bean设置创建对象的工厂 factory-method指定创建对象的方法-->
<bean id="user" factory-bean="userFactory" factory-method="newinstance"/>
3.2.4 使用容器:
Resource res=new FileSystemResource("src/beans.xml");
BeanFactory bean=new XmlBeanFactory(res);
User user=(User)bean.getBean("user");
System.out.println(user.getName());
<bean id="u" class="cn.sxt.vo.User" factory-method="newinstance"/>
4 注入依赖
依赖注入(DI)背后的基本原理是对象之间的依赖关系(即一起工作的其它对象)只会通过以下几种方式来实现:构造器的参数、工厂方法的参数,或给由构造函数或者工厂方法创建的对象设置属性。因此,容器的工作就是创建bean时注入那些依赖关系。
4.1 构造器注入
基于构造器的DI通过调用带参数的构造器来实现,每个参数代表着一个依赖。
4.1.1 构造器参数解析
构造器参数解析根据参数类型进行匹配,如果bean的构造器参数类型定义非常明确,那么在bean被实例化的时候,bean定义中构造器参数的定义顺序就是这些参数的顺序,依次进行匹配。
public class Foo {
public Foo(Bar bar, Baz baz) {
// ...
}
}
<bean name="foo" class="x.y.Foo">
<constructor-arg>
<bean class="x.y.Bar"/>
</constructor-arg>
<constructor-arg>
<bean class="x.y.Baz"/>
</constructor-arg>
</bean>
4.1.2 构造器参数类型匹配
<bean id="user" class="cn.sxt.vo.User">
<!-- 通过参数类型进行匹配 -->
<constructor-arg type="java.lang.String" value="tang"/>
</bean>
4.1.3 构造参数索引
<bean id="user" class="cn.sxt.vo.User">
<!-- 根据参数的位置设置为索引来对指定位置的数进行注入 从0开始-->
<constructor-arg index="0" value="tang"/>
</bean>
4.2 Setter注入
4.2.1 通过调用无参构造器或无参static工厂方法实例化bean之后,调用该bean的setter方法,即可实现基于setter的DI。
<bean id="mysqlDao" class="cn.sxt.dao.impl.UserDaoMySqlImpl"/> <bean id="oralceDao" class="cn.sxt.dao.impl.UserDaoOracleImpl"/> <bean id="service" class="cn.sxt.service.impl.UserServiceImpl"> <!-- ref引用对象,通过Id引用对象注入 --> <!-- 随着此处位置的引用的不同ID,而UserDaoMySqlImpl和UserDaoO racleImpl均实现UserDao接口,将注入不同的对象 --> <property name="userDao" ref="oralceDao"></property> </bean> public class UserDaoMySqlImpl implements UserDao{
@Override
public void getUser() {
System.out.println("mysql获取用户数据");
}
}
public class UserDaoOracleImpl implements UserDao{
@Override
public void getUser() {
// TODO Auto-generated method stub
System.out.println("orac获取用户数据");
}
}
public interface UserService {
public void getUser();
}
public class UserServiceImpl implements UserService{
private UserDao userDao=null;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
4.2.2 注入到构造器
<bean id="service" class="cn.sxt.service.impl.UserServiceImpl">
<constructor-arg>
<ref bean="oralceDao"/>
</constructor-arg>
</bean>
4.2.3 采用static工厂方法返回对象实例:
<!-- 在BeanFactoryCustom中定义一个bean静态方法, -->
<bean id="service" class="cn.sxt.service.impl.BeanFactoryCustom" factory-method="createInstance">
<!-- 注入到bean静态方法中 -->
<constructor-arg ref="oralceDao"/>
</bean>
public class UserServiceImpl implements UserService{
private UserDao userDao=null;
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
public static UserServiceImpl createInstance(UserDao userDao){
//根据参数创建对象
return new UserServiceImpl(userDao);
}
5 集合:
需要注意的是,数据注入时,Spring的底层会强制发生强制转换。没有value值时,将设置为“ ”值,即空字符串。
<bean id="addr" class="cn.sxt.vo.Address">
<property name="address" value="北京西三旗"></property>
</bean>
<bean id="student" class="cn.sxt.vo.Student">
<!-- 注入属性 -->
<property name="name" value="张三丰"></property>
<!-- 注入对象 -->
<property name="addr" ref="addr"/>
<!-- 注入数组 -->
<property name="books">
<array>
<value>傲慢与偏见</value>
<value>仲夏之梦</value>
<value>雾都孤儿</value>
</array>
</property>
<!-- 注入List集合 -->
<property name="hobbies">
<list>
<value>羽毛球</value>
<value>乒乓球</value>
<value>玻璃球</value>
</list>
</property>
<!-- 注入Map集合 -->
<property name="cards">
<map>
<entry key="中国银行" value="1212121"></entry>
<entry key="建设银行" value="1223232"></entry>
<entry key="交通银行" value="1323232"></entry>
</map>
</property>
<!-- 注入Set集合 -->
<property name="games">
<set>
<value>lol</value>
<value>dota</value>
<value>cs</value>
<value>dnf</value>
</set>
</property>
<!-- 注入字符 -->
<property name="wife">
<null/>
</property>
<!-- 注入配置信息 -->
<property name="info">
<props>
<prop key="学号">20150512601</prop>
<prop key="sex">男</prop>
<prop key="name">小乔</prop>
</props>
</property>
</bean>
还可以使用p名称空间配置属性如下:
<bean name="john-classic" class="com.example.Person">
<property name="name" value="John Doe"/>
<property name="spouse" ref="jane"/>
</bean>
<bean name="john-modern"
class="com.example.Person"
p:name="John Doe"
p:spouse-ref="jane"/>
<bean name="jane" class="com.example.Person">
<property name="name" value="Jane Doe"/>
</bean>
</beans>
需要说明的是集合支持合并,子集合将会覆盖父集合中的数据。List集合由于是有序存在的。父集合中的数据将在子集合元素之前。不同集合类型之间是不能合并的。
如:
<beans>
<bean id="parent" abstract="true" class="example.ComplexObject">
<property name="adminEmails">
<props>
<prop key="administrator">[email protected]</prop>
<prop key="support">[email protected]</prop>
</props>
</property>
</bean>
<bean id="child" parent="parent">
<property name="adminEmails">
<!-- the merge is specified on the *child* collection definition -->
<props merge="true">
<prop key="sales">[email protected]</prop>
<prop key="support">[email protected]</prop>
</props>
</property>
</bean>
<beans>
6 depends-on属性
depends-on属性可以用于当前bean初始化之前显式地强制一个或多个bean被初始化。防止出现依赖的对象没有生成。
7 方法注入:
7.1 在大部分情况下,容器中的bean都是singleton类型的。如果一个singleton bean要引用另外一个singleton bean,或者一个非singleton bean要引用另外一个非singleton bean时,通常情况下将一个bean定义为另一个bean的property值就可以了。对于不同周期的Bean,当一个singleton类型的Bean A中的方法需要使用另一个全新的非singleton类型的Bean B时,那么只能通过实现BeanFactoryAware接口,在需要的地方使用getBean(” “)请求一个Bean实例。
如下:
public class CommandManager implements BeanFactoryAware {
private BeanFactory beanFactory;
public Object process() {
System.out.println("process");
//获取一个实例
Command command = createCommand();
//执行Command中的方法
return command.execute();
}
/** * 获取一个command实例 * @return Command对象 */
protected Command createCommand() {
return (Command) this.beanFactory.getBean("command");
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("setBeanFactory");
this.beanFactory = beanFactory;
}
}
其中setBeanFactory方法,在Bean的属性初始化后调用。
执行结果:
参考:http://blog.csdn.net/heyutao007/article/details/5981555
7.2 通过lookup方式:
定义一个抽象类,包含一个抽象方法,访问级别必须是public或protected,保证能被子类重载,可以是抽象方法,必须有返回值,必须是无参数方法,用于创建需要注入的方法的Bean。
public abstract class HelloImpl5 implements HelloApi {
private Printer printer;
//放回一个全新的对象。
public abstract Printer createPrototypePrinter();
}
<bean id="prototypePrinter"
class="cn.javass.spring.chapter3.bean.Printer" scope="prototype"/>
<bean id="singletonPrinter"
class="cn.javass.spring.chapter3.bean.Printer" scope="singleton"/>
<bean id="helloApi1" class="cn.javass.spring.chapter3.HelloImpl5" scope="singleton">
<property name="printer" ref="prototypePrinter"/>
//生成一个bean对应的对象作为createPrototypePrinter的返回值。
<lookup-method name="createPrototypePrinter" bean="prototypePrinter"/>
</bean>
7.3 方法替换
1 定义一个类实现MethodReplacer接口,实现reimplement方法,参数obj为被替换方法的对象,method为被替换方法,args为方法参数;
2 配置
,使用< replaced-method >标签来指定要进行替换方法,属性name指定替换的方法名字,replacer指定该方法的重新实现者,子标签< arg-type >用来指定原来方法参数的类型,必须指定否则找不到原方法,如下将print替换:
<bean id="replacer" class="cn.javass.spring.chapter3.bean.PrinterReplacer"/>
<bean id="printer" class="cn.javass.spring.chapter3.bean.Printer">
<replaced-method name="print" replacer="replacer">
<arg-type>java.lang.String</arg-type>
</replaced-method>
</bean>
参考博客:http://jinnianshilongnian.iteye.com/blog/1415461,感谢大神的分享。
8 Bean的作用域
创建一个bean定义,其实质是用该bean定义对应的类来创建真正实例的“配方(recipe)”。把bean定义看成一个配方很有意义,它与class很类似,只根据一张“处方”就可以创建多个实例。你不仅可以控制注入到对象中的各种依赖和配置值。
可自定义bean的作用域,步骤如下:
8.1 继承org.springframework.beans.factory.config.Scope接口,接口中的方法说明:
第一个方法可以从作用域中获取对象。
Object get(String name, ObjectFactory objectFactory)
第二个方法可以从作用域中移除对象。例如,session作用域的实现可以从session中移除并返回session-scoped bean(如果没有找到相应名称的对象昂,则可以返回null)。
Object remove(String name)
第三个方法是注册作用域析构的回调方法,当作用域销毁或作用域中的某个对象销毁时候会执行。。
void registerDestructionCallback(String name, Runnable destructionCallback)
最后一个方法处理作用域的会话标识。对每一个作用域来说标识是不一样的。例如,对于session,将获得session标识。
String getConversationId()
8.2 使用自定义作用域
让Spring容器装配你的作用域,把一个新的Scope 注册到Spring 容器中的核心方法定义在ConfigurableBeanFactory接口中
void registerScope(String scopeName, Scope scope);
registerScope(..) 方法的第一个参数是一个作用域的唯一名称,例如,Spring 容器中的’singleton’和’prototype’。registerScope(..) 方法的第二个参数是你要注册和使用的自定义Scope的实例。
例如:
Scope customScope = new ThreadScope();
beanFactory.registerScope("thread", scope);
使用作用域:
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="thread">
<bean class="com.foo.ThreadScope"/>
</entry>
</map>
</property>
</bean>
<bean id="bar" class="x.y.Bar" scope="thread">
<property name="name" value="Rick"/>
<aop:scoped-proxy/>
</bean>
<bean id="foo" class="x.y.Foo">
<property name="bar" ref="bar"/>
</bean>
部分摘自官方文档。
未完待续。。。