Spring的基础知识——IOC与DI

1、IOC

        (1)、概念:把对象的创建、初始化、销毁等工作交给Spring来做

        (2)、Hello World

            首先导入必须的jar包

            112106_Qi7z_1476426.png

            接着创建bean容器配置文件applicationContext.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">
	
	<!-- hello world -->
	<bean id="helloworld" class="ff.helloworld.HelloWorld"></bean>
	
</beans>

            <beans>元素中属性是一些必要的约束,<bean>元素描述某个类,id属性是bean唯一标识,class属性是类的全限定名

            然后创建HelloWorld类和测试类,进行测试

public class HelloWorld {
	
	public void say() {
		System.out.println("Hello World -Spring");
	}

}

       

public class TestHelloWorld {
	
	private ApplicationContext applicationContext;
	
	@Before
	/**
	 * 加载context
	 */
	public void init() {
		
		applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
	}
	
	@Test
	public void testSay() {
		
		HelloWorld hw = (HelloWorld) applicationContext.getBean("helloworld");
		hw.say();
	}
}

            加载配置文件applicationContext.xml生成ApplicationContext对象,再用它的getBean()方法从bean容器中获取helloworld对象,调用say()方法。

    (3)、Spring创建对象的三种方式

        a、构造器(用的最多)

            通过类的构造器创建对象,上面helloworld案例就是通过构造器创建

        b、静态工厂

           applicationContext.xml中不配置具体的某个bean,而是配置一个工厂bean,在创建工厂bean的时候调用工厂方法(factory-method)创建具体bean。

<!-- 静态工厂创建bean -->
	<bean 
		id="beanDemoFactory_static" class="ff.beanCreate.BeanDemoFactory_Static" 
		factory-method="getInstance" lazy-init="true"></bean>
public class BeanDemo {
	
	public BeanDemo() {
		System.out.println("i am BeanDemo");
	}
	
	public void say() {
		System.out.println("i am created!");
	}
}
public class BeanDemoFactory_Static {
	
	public static BeanDemo getInstance() {
		
		return new BeanDemo();
	}
}

         c、实例工厂

            application中不配置具体某个bean,而是配置一个工厂bean同时配置一个第三方bean,在创建第三方bean的时候通过factory-bean属性找到工厂bean,再调用工厂方法(factory-method属性)创建具体bean。

<!-- 实例工厂创建bean -->
	<bean 
		id="beanDemoFactory_instance" class="ff.beanCreate.BeanDemoFactory_instance"></bean>
	<!-- 第三方bean -->
	<bean 
		id="beanDemo_instance" factory-bean="beanDemoFactory_instance" 
		factory-method="getInstance"></bean>
public class BeanDemo {
	
	public BeanDemo() {
		System.out.println("i am BeanDemo");
	}
	
	public void say() {
		System.out.println("i am created!");
	}
}
public class BeanDemoFactory_instance {
	
	public BeanDemo getInstance() {
		
		return new BeanDemo();
	}
}

          d、为什么要用工厂方式创建对象?

            实现bean创建和使用分离,将bean创建的工作交由工厂来完成。这样可以统一管理bean的创建,如可以在各个bean创建前做一些初始化工作。此外,便于集成其它框架的bean创建管理方法。

    (4)、Spring创建对象的时机

        Spring默认在启动IoC容器时自动创建对象,但可以通过配置<bean>元素的lazy-init属性自定义创建时机。

        lazy-init属性有三种值:default、false、true。默认值为default,效果等同于值false,在启动时创建对象,若将其设置为true则在获取bean的时候创建对象。

        注意:一般不设置为true,原因是这样设置后Spring在启动不会创建这个bean,如果这个bean有bug,那么启动时不会报错,导致存在安全隐患。若一个bean的属性中含有大量数据,过早的放入内存会影响程序性能,这时候将lazy-init设置为ture。

    (5)、bean的scope(作用域)

       a、 默认情况

            默认情况下Spring创建的bean是单例的,每次获取bean的时候返回同一个对象,这个bean的属性自然也被多处引用中共享,这样很不安全。所以默认情况下不能将数据定义到bean属性中。

        b、修改bean的scope

            <bean>元素中的scope属性可以修改scope。scope属性有四个值singleton(单例)、prototype(多例)、request、session,其中prototype为原型模式,每次获取bean返回不同对象。

        c、选择单例还是多例?

            Spring IoC容器在启动时会创建applicationContext.xml文件中配置的所有bean,实际开发中大多数的bean只会使用到1次,所以默认配置为单例,对特殊bean配置成多例。

    (6)、scope与创建时机的结合

        a、当scope为property时,不管lazy-init值是什么,都在获取bean的时候创建对象。因为property是多例模式,每次获取bean的时候都会返回不同的对象。

        b、当scope为singleton时,在什么时候创建对象由lazy-init决定,且都以单例模式创建。

    (7)、bean初始化,销毁

        <bean>元素的init-method和destory-method定义初始化和销毁方法。

    (8)、bean创建、初始化、销毁的顺序

        当Spring启动(或代码中获取bean)时创建bean对象,接着Spring内部调用初始化方法(如果配置),当Spring容器关闭时执行这个bean对象的销毁方法。

        说明:1、初始化和销毁方法由Spring内部执行。 2、销毁方法在Spring容器关闭时执行,但Spring容器一般不会关闭,所以容器里的对象会一直存在。3、如果<bean>元素的scope属性值为property,Spring容器不负责销毁。

2、 DI(依赖注入)

        (1)、概念:给bean对象属性赋值

        (2)、三种赋值方式

            a、通过setter方法赋值

                配置<bean>元素内<property>子元素,在bean被创建时调用对应的setter方法给属性注入值。

<bean id="persion" class="domain.Persion">
		<property name="student" ref="student"></property>
		<property name="string" value="this is a string"></property>
		<property name="list">
			<list>
				<value>index1</value>
				<value>index2</value>
				<ref bean="student"/>
			</list>s
		</property>
	</bean>
public class Persion {
	
	private Student student;  // Student对象
	private String string;
	private List list;
	
	public void say(Student student) {
		student.say();
	}

	public void setStudent(Student student) {
		this.student = student;
	}

	public void setString(String string) {
		this.string = string;
	}

	public void setList(List list) {
		this.list = list;
	}
}

        当Person对象被创建时,Spring通过<property>元素找到对应的setter方法,将配置的值注入。

        <property>元素给属性注入值,value为基本类型以及String类型值,ref为引用属性的bean的id,array、list、map为集合属性

            b、通过构造方法赋值

                在<bean>元素内配置<constructor-arg>元素,当Persion类被创建时调用对应的构造器为属性赋值。

<bean id="student" class="ff.ing.pojo.Student">
		<constructor-arg index="0" value="盖伦"></constructor-arg>
	</bean>
	
	<bean id="persion" class="ff.ing.pojo.Persion">
		<constructor-arg index="0" value="007"></constructor-arg>
		<constructor-arg index="1" value="赵信"></constructor-arg>
		<constructor-arg index="2" ref="student"></constructor-arg>
	</bean>
public class Persion {
	
	private int id;
	private String name;
	private Student student;
	
	public Persion() {
		
	}
	
	public Persion(int id, String name, Student student) {
		
		this.id = id;
		this.name = name;
		this.student = student;
	}
	
	public void say() {
		String str = "id:" + id + "   name:" + name + "   student:" + student.getName();
		System.out.println(str);
	}
}

       index属性为构造器参数列表中参数的序号。    

        c、通过注解赋值

            1、依赖注入注解

          (1) 首先需要修改applicationContext.xml文件:添加注解命名空间约束,启动依赖注入注解解析器

<!-- 添加注解命名空间约束 -->
xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
		
<!-- 启动依赖注入注解解析器 -->
<context:annotation-config/>

<!-- bean -->
<bean>........</bean>

        (2) 在类中使用注解

public class Persion {
	
	private int id;
	private String name;
	
	@Resource(name="student")
	private Student student;
	
    ………
    ………
}

   @Resource会到IoC容器中寻找id与name匹配的bean,赋值给student属性。

    @Resource是java内置注解,Spring框架定义了自己的注解。@Autowired按照类型进行匹配;@Qualifier(value="xxx")按照id进行匹配,且只能与@Autowired组合使用,无法单独使用。

    注解只能注入引用类型值,无法注入基本类型、String类型以及集合类型值

    注解方式比xml配置方式效率低。   

    2、类扫描注解

    (1)、首先需要修改applicationContext.xml文件:添加注解命名空间约束,启动类扫描注解解析器,配置要扫描的包

<!-- 添加注解命名空间约束 -->
xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

<!-- 启动类扫描注解解析器,配置要扫描的包 -->
<context:component-scan base-package="ff.ing.pojo"></context:component-scan>

    (2)在类中使用注解

@Component(value="student")
public class Student {
	
	private String name;
	
	public Student() {
		
	}
	……
	……
}


@Component(value="persion")
public class Persion {
	
	private int id;
	private String name;
	
	@Resource(name="student")
	private Student student;
	
	public Persion() {
		
	}
	……
	……
}

    @Component配置了这个类的在spring容器中的id,以Persion类为例@Component(value="persion")相当于<bean id="persion" class="……"></bean>

    Spring还提供了更细分的组件注解:@Repository@Service@Controller,分别代表dao层、业务层和控制层。他们的用法以及功能与@Component一致,只是方便了代码的阅读而已。

    类扫描注解方式比依赖注入注解方式效率还低。

  ● IoC、DI中的类继承问题

      当使用setter和构造器方法给属性输入值时,子类无法继承父类中属性的值,这时需要在<bean>元素中添加parent属性。使用注解不存在这样的问题

  ● IoC与DI的意义

    ● IoC与DI最生动的解释(摘抄他人)

首先想说说IoC(Inversion of Control,控制倒转)。这是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、号码………,想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。

那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的信息,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个mm,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。如果你还不明白的话,我决定放弃。

IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。

    ● 意义

        实现了完全面向接口编程(程序设计依赖于抽象的接口而非具体实现类),在代码中不需要关心具体是哪个实现类在工作。

转载于:https://my.oschina.net/u/1476426/blog/598557

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值