美文网首页
spring ioc

spring ioc

作者: 许先森的许 | 来源:发表于2019-01-10 17:47 被阅读11次
    1、IoC 依赖注入(控制反转) 方便解耦

    耦合:
    业务层的A类中要使用持久层B类对象,于是在A类中new了一个B类对象来用,这就导致了A类会强依赖于B类,产生耦合,耦合只能减弱不能消除,消除相当于这两个类毫无关系了。
    减弱耦合:
    不在A类中newB类的对象,建立一个对象容器(工厂),这个容器里面就产生各种对象给别的类调用,对象的生成只在这个容器中,可以把要生成的对象path写在properties或xml文件中,在容器中用静态代码块读取properties或xml中的内容,通过反射生成对象,保存在容器中,比如一个Map中,再向外界提供一个根据map的key获取对象的方法。

    1、1那么spring是如何做到减弱耦合的呢?

    原理和上面的对象容器工厂差不多,容器由spring完成了,我们只需要在xml中配置业务层和持久层的对象信息:说明要创建哪个对象、用什么方式去取:

        <bean id=“accountService” class=“com.demo.service.impl.AccountServiceImpl”><bean/>
        <bean id=“accountDao” class=“com.demo.dao.impl.AccountDaoImpl”><bean/>
        id:对象的唯一标示;class:要创建对象的全限定类名
    

    spring有一个核心容器,xml中读取的对象都生成放在核心容器中;下面我们只需要获取到核心容器后,再根据bean的id获取对象:

    核心容器类:
        ApplicationContext:
            是BeanFactory下的子接口的再子接口。
            特点是创建bean对象时,采用的是立即加载的策略。(当读取完xml配置文件,配置文件中所有的bean对象都已经创建完成了,实测)
        BeanFactory:
            它是springioc容器的顶层接口。
            特点是创建bean对象时,采用的是延迟加载的策略。(当真正要从容器获取bean对象时才创建,读完配置文件时不创建)
        单例对象可以用ApplicationContext,多例对象用BeanFactory,不过spring会帮我们处理,一般都用ApplicationContext,设置属性可以选择单例多例。
    
    • 用bean标签创建bean对象三种创建方式:
      1、通过调用默认构造函数来创建bean对象,默认情况下,在bean标签中写了class属性,spring就会调用默认构造函数创建对象,如果没有默认构造函数,则对象创建失败:
      <bean id=“唯一标示” class=“全限定类名”></bean>

    2、静态工厂创建,项目中已经有一个创建对象的工厂类,其中有创建对象的静态方法可以使用:
    <bean id=“唯一标示” class=“全限定类名(工厂类)” factory-method=“方法名”></bean>

    3、实例工厂创建,项目中已经有一个创建对象的工厂类,其中有创建对象的非静态方法可以使用:

    <bean id=“唯一标示” factory-bean=“工厂bean的id”  factory-method=“方法名”></bean>
    
    • bean对象的作用范围:
      可以通过一个配置属性来指定:scope属性: 比如 scope=“singleton”
      有几个值:
      singleton:单例的 (默认值,也最常用)
      prototype:多例的
      request:请求范围
      session:会话范围
      global-session:全局会话范围

    • Bean对象的生命周期:
      单例对象
      出生:容器创建,对象出生
      活着:只要容器在,对象就一直可用
      死亡:容器销毁,对象消亡
      多例对象
      出生:每次使用时,容器才会为我们创建对象
      活着:只要对象在使用过程中就一直活着
      死亡:spring不知道对象你什么时候用完,spring把这件事交给了java的gc:对象长时间不用,并且也没有其他对象引用时,java的垃圾回收器回收

    1、2对象创建出来了,那么依赖关系要怎么构建呢,spring提供了依赖注入:

    场景:展示层调用AccountServiceImpl(业务层)的saveAccount方法,这个方法中又调用AccountDao(持久层)类的保存账号方法,那这个时候AccountServiceImpl类中就需要引用到AccountDao对象,我们前面已经把这两个对象都用spring创建出来了,那么现在开始往AccountServiceImpl中依赖AccountDao对象,为了简单我们先用简单类型的属性代替AccountDao对象引入到AccountServiceImpl中:

    1、构造函数注入:
    使用constructor-arg标签,该标签是写在bean标签内部的子标签:
        标签的属性:
            type:指定要注入的参数在构造函数中的类型
            index:指定要注入的参数在构造函数中的索引位置
            name:指定要注入的参数在构造函数中的名称
            value:指定要注入数据内容,他只能指定基本类型数据和String类型数据
            ref:指定其他bean类型数据。写的是其他bean的id。其他bean指的是存在于spring容器中的bean。
    例子:
    <bean id=“accountService” class=“com.demo.service.impl.AccountServiceImpl”>
        <constructor-arg    name=“name”  value=“xc”></constructor-org>
        <constructor-arg    name=“age”  value=“27”></constructor-org>
        <constructor-arg    name=“birthday”  ref=“now”></constructor-org>
    <bean/>
    <bean id=“now” class=“java.util.Date”></bean>
    

    这种方式需要AccountServiceImpl类中有带有这三个参数的构造函数,否则报错。
    这时候在展示层通过spring获取的AccountServiceImpl对象其中的三个属性就已经有了上面设置的值,这感觉有点像是初始化对象赋值呢?不过好像确实是这样,因为是用构造函数注入的。

    2、使用setter方法注入:(常用)
    涉及的标签:property。也是要写在bean标签内部的子标签
    标签属性:
        name:指定的是setter方法的名称。匹配的是类中setter方法set后面的部分:setAppName(),那么这里就取appName,首字母要小写。
        value:指定要注入数据内容,他只能指定基本类型数据和String类型数据
        ref:指定其他bean类型数据。写的是其他bean的id。其他bean指的是存在于spring容器中的bean。
        例子:
        <bean id=“accountService” class=“com.demo.service.impl.AccountServiceImpl”>
        <property    name=“name”  value=“xc”></property>
        <property    name=“age”  value=“27”></property>
        <property    name=“birthday”  ref=“now”></property>
        <bean/>
    

    这种方式需要类中的属性有set方法,可以没有get方法,但是一定要有set方法.
    用这种方式往AccountServiceImpl中引用AccountDao:

    <bean id=“accountService” class=“com.demo.service.impl.AccountServiceImpl”>
    <property name=“accountDao” ref=“accountDao”></constructor-org>
    <bean/>
    <bean id=“accountDao” class=“com.demo.dao.impl.AccountDaoImpl”><bean/>
    这样写完,业务层和持久层的依赖关系就交给了spring来维护了。

    • 2.1使用p名称空间注入:它的本质仍然是需要类中提供set方法,同时在配置文件中要导入p名称空间:

    <bean id=“accountService” class=“com.demo.service.impl.AccountServiceImpl” p:name=“张三”>
    <bean/>

    • 2.2复杂类型注入:注入的方式采用set方法,注入的类型都是集合类型:
      注意:结构相同标签可以互换
            <!— 给数组注入数据—>
            <property    name=“myStrs” >
                <array>
                    <value>AAA</value>
                    <value>BBB</value>
                    <value>ccc</value>
                </array>
            </property>
            <!— 给list注入数据—>
            <property    name=“myList” >
                <list>
                    <value>AAA</value>
                    <value>BBB</value>
                    <value>ccc</value>
                </list>
            </property>
            <!— 给set注入数据—>
            <property    name=“mySet” >
                <set>
                    <value>AAA</value>
                    <value>BBB</value>
                    <value>ccc</value>
                </set>
            </property>
            <!— 给map注入数据—>
            <property    name=“myMap” >
                <map>
                    <entry key=“testA” value=“AAA”></value>
                    <entry key=“testB” value=“BBB”></value>
                </map>
            </property>
            <!— 给properties注入数据—>
            <property    name=“myProps” >
                <props>
                    <prop key=“testa”>AAA</value>
                    <prop key=“testB”>BBB</value>
                </props>
            </property>
        <bean/>
      
      
    1.3用spring注解的方式再做一遍上面的工作:

    和上面xml的目的都是为了降低耦合,只不过是形式不一样

     * 用于创建对象的:
     *
     * @Component :
     * 作用:就相当于在spring的xml配置文件中写了一个bean标签。
     * 属性:
     * value:用于指定bean的Id。当不写时,默认值是当前类名,首字母改小写。
     * 由此注解衍生的三个注解:
     * @Controller, 用于表现层
     * @Service, 用于业务层
     * @Repository 用于持久层
     * 他们的作用以及属性和@Component的作用是一样的。它们的出现时spring框架为我们提供更明确的语义话来指定不同层的bean对象。
     * <p>
     * 用于注入数据的:
     * @Autowired
     * 作用:自动按照类型注入。只要容器中有唯一的类型匹配,则可以直接注入成功。
     * 细节:当使用此注解注入时,set方法就可以省略了。
     * 属性:required :是否必须注入成功。取值true(默认值)/false。当取值是true时,没有匹配的对象就会报错。写不写无所谓,反正没有匹配到总会报错,只不过报错信息不一样。
     * 用于改变作用范围的:
     * 和生命周期相关的:
     */
    
    @Component(value = "accountService")
    public class AccountServiceImpl implements IAccountService {
    
        @Autowired(required = false)
        IAccountDao accountDao;
    
        @Override
        public void saveAccount() {
            accountDao.saveAccount();
        }
    }
    
    public class Client {
    
        public static void main(String[] args) {
            ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
            IAccountService accountService = ac.getBean("accountService",IAccountService.class);
            IAccountDao accountDao = ac.getBean("accountDao",IAccountDao.class);
    
            accountService.saveAccount();
        }
    }
    <?xml version="1.0" encoding="UTF-8"?>
    
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
             http://www.springframework.org/schema/context
             http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    
        <!--告知spring创建容器时要扫描的包-->
        <context:component-scan base-package="com.ncz.nczupkeep.biz"></context:component-scan>
    
    </beans>
    
    
    

    相关文章

      网友评论

          本文标题:spring ioc

          本文链接:https://www.haomeiwen.com/subject/qvoarqtx.html