Spring--IOC

经典讲述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">administrator@example.com</prop>
            <prop key="support">support@example.com</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">sales@example.com</prop>
            <prop key="support">support@example.co.uk</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>

部分摘自官方文档。

未完待续。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值