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,而UserDaoMySqlImplUserDaoO 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>

部分摘自官方文档。

未完待续。。。

你可能感兴趣的:(spring,IOC)