IOC
-
IOC:所谓的IOC称之为控制反转,简单来说就是将对象的创建的权利及对象的生命周期的管理过程交由Spring框架来处理,从此在开发过程中不再需要关注对象的创建和生命周期的管理,而是在需要时由Spring框架提供,这个由spring框架管理对象创建和生命周期的机制称之为控制反转。
-
原理:在初始化一个Spring容器时,Spring会去解析指定的xml文件,当解析到其中的标签时,会根据该标签中的class属性指定的类的全路径名,通过反射创建该类的对象,并将该对象存入内置的Map中管理。其中键就是该标签的id值,值就是该对象。之后,当通过getBean方法来从容器中获取对象时,其实就是根据传入的条件在内置的Map中寻找是否有匹配的键值,如果有则将该键值对中保存的对象返回,如果没有匹配到则抛出异常。
-
Spring采用xml文件作为配置文件,xml文件名字任意,通常取名为applicatoinContext.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"> </beans>
-
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml"); Person p=(Person) context.getBean("person"); p.eat(); p.say(); ((ClassPathXmlApplicationContext)context).close(); <bean id="person" class="cn.hbh.domain.Person"></bean>
-
注意
- 多次获取同一个id的bean,得到的将是同一个对象,因此不可以配置多个id相同的bean。
- 可以配置多个id不同,但class相同的bean
IOC获取对象的方式
-
传入id值
- 如果找不到id,就会抛出异常NoSuchBeanDefinitionException
- 如果找到唯一的,就会返回对象
- 因为id不重复,不可能找到多个
-
传入class类型
- 通过class方式获取bean时,如果同一个类配置过多个bean,则在获取时因为无法确定到底要获取哪个bean就会抛出异常。
- 如果找不到,就会抛出异常NoSuchBeanDefinitionException
- 如果找到唯一,返回对象
- 如果找到多个,抛出异常NoUniqueBeanDefinitionException
Person p=context.getBean(Person.class); //如果这样,就会因为无法获取哪个bean而报错,所以一般推荐用id的方式来获取对象。 <bean id="person" class="cn.hbh.domain.person"></bean> <bean id="person1" class="cn.hbh.domain.person"></bean>
-
当用class方式获取bean,如果找不到该类型的bean,会去找此类子孙类型的bean,如果有返回对象,没有抛出异常,符合java中多态的思想。
//比如Teacher类继承了person类 class Teacher extends perosn{ public void doing(){ System.out.println("我会讲课"); } } <bean id="teacher" class="cn.hbh.domain.Teahcer"></bean>
别名标签
- 在 Spring中提供了别名标签可以为配置的起一个别名,要注意的是这仅仅是对指定的起的一个额外的名字,并不会额外的创建对象存入map。
<bean id="person" class="cn.hbh.domain.Person"></bean>
//别名标签
<alias name="person" alias="per"></alias>
Spring创建对象的方式
-
通过类的无参构造创建对象
//入门案例中使用的就是这种方式。 //用最普通的方式配置一个bean时,默认就是采用类的无参构造创建对象。 //Spring容器初始化时,通过<bean>上配置的class属性反射得到字节码对象,通过newInstance()创建对象 //这种方式下spring创建对象,要求类必须有无参的构造,否则无法通过反射创建对象,会抛出异常。 Class c=Class.forName("cn.hbh.domain.Person"); Object o=c.newInstance(); Person p3=(person)o; p3.say();
-
通过静态工厂创建对象
-
很多时候,我们面对的类是无法通过无参构造去创建的,例如该类没有无参构造、是一个抽象类等情况,此时无法要求spring通过无参构造创建对象,此时可以使用静态工厂方式创建对象。
abstract class Person(){ //抽象类的无参构造 public Person(){ } public void print(){ System.out.println("我会写字"); } public abstract void say(); } public class Student extends Person{ public Student(String name){ System.out.println("Student对象被创建"); } //强制子类重写say方法 public void say(){ System.out.println("我会讲课"); } } //用于创建student对象,因为student中出现有参构造,无参构造不会自动创建出来。 //另外,抽象方法不能实例化,必须父类引用指向子类。用子类对象去调用抽象方法。 public class StudentStaticfactory{ public static Student getInstance(){ return new Student("zs"); } } <bean id="student" class="cn.hbh.factory.StudentStaticfactory" factory-method="getInstance"></bean>
-
-
通过实例工厂创建对象
-
实例工厂也可以解决类无法通过无参构造创建的问题,解决的思路和静态工厂类似,只不过实例工厂提供的方法不是静态的。
-
Spring需要先创建出实例工厂的对象,在调用实例工厂对象上指定的普通方法来创建对象。所以实例工厂也需要配置Spring中管理。
public class PersonInstanceFactory{ public Person getInstance(){ return new Person("zs"); } } public class Person(){ public Person(String name){ System.out.println("Person对象被创建"); } } <bean id="personinstancefactory" class="cn.hbh.factory.PersonInstanceFactory"></bean> <bean id="person" factory-bean="personinstancefactory" factory-method="getInstance"></bean>
-
-
通过Spring工厂创建对象
-
Spring内置了工厂接口,也可以通过实现这个接口来开发Spring工厂,通过这个工厂创建对象。
public class PersonSpringFactory implements FactoryBean{ //生产该bean的对象 @Override public Object getObject() throws Exception { return new Person("ls"); } //获取该Bean类型的方法 @Override public Class<?> getObjectType() { return Person.class; } //是否单例 @Override public boolean isSingleton() { return true; } } <bean id="person" class="cn.hbh.factory.PersonSpringFactory"></bean>
-
单例和多例
-
Spring容器管理的bean在默认情况下是单例的,也即,一个bean只会创建一个对象,存在内置 map中,之后无论获取多少次该bean,都返回同一个对象。
-
Spring默认采用单例方式,减少了对象的创建,从而减少了内存的消耗。但是在实际开发中是存在多例的需求的,Spring也提供了选项可以将bean设置为多例模式。
-
bean在单例模式下的生命周期
-
首先spring会去解析xml文件,然后找到bean标签,通过bean标签中的class属性指定的全路径名,反射创建出该对象,并将该bean对象存入内部的map中保存,此后无论调用多少次getBean()获取该bean都是从map中获取该对象返回,一直都是一个对象。此对象一直被Spring容器持有,直到该容器退出时,随着容器的退出对象被销毁。
-
默认方式为单例模式 singleton
<bean id="person" class="cn.hbh.domain.Person" scope="singleton"></bean> ApplicationContext context=new ClassPathXmlApplicationContext("ApplicationContext.xml"); Person p=(Person)context.getBean("person"); Person p1=(Person)context.getBean("person"); //控制台 cn.tedu.domain.Person@3b0143d3 cn.tedu.domain.Person@3b0143d3
-
-
bean在多例模式下的生命周期
- 首先spring会去解析xml文件,发现该bean标签后,只是将该bean进行管理,并不会创建对象,此后每次使用 getBean()获取该bean时,spring都会重新创建该对象返回,每次都是一个新的对象。这个对象spring容器并不会持有,什么销毁取决于使用该对象的用户自己什么时候销毁该对象。
<bean id="person" class="cn.hbh.domain.Person" scope="prototype"></bean> ApplicationContext context=new ClassPathXmlApplicationContext("ApplicationContext.xml"); Person p=(Person)context.getBean("person"); Person p1=(Person)context.getBean("person"); //控制台 cn.tedu.domain.Person@18eed359 cn.tedu.domain.Person@3e9b1010
懒加载机制
- Spring默认会在容器初始化的过程中,解析xml,并将单例的bean创建并保存到map中,这样的机制在bean比较少时问题不大,但一旦bean非常多时,spring需要在启动的过程中花费大量的时间来创建bean 花费大量的空间存储bean,但这些bean可能很久都用不上,这种在启动时在时间和空间上的浪费显得非常的不值得。所以Spring提供了懒加载机制。所谓的懒加载机制就是可以规定指定的bean不在启动时立即创建,而是在后续第一次用到时才创建,从而减轻在启动过程中对时间和内存的消耗。
- 注意
- 懒加载机制只对单例bean有作用,对于多例bean设置懒加载没有意义。
- 全局配置懒加载default-lazy-init="true"
- 对于全局配置懒加载,如果该bean局部配置覆盖全局配置。
<bean id="person" class="cn.hbh.domain.Person" lazy-init="true"></bean>
//也可以为全局配置懒加载
<?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-3.2.xsd"
default-lazy-init="true"
>
<bean id="cart" class="cn.hbh.beans.Cart"></bean>
</beans>
配置初始化和销毁的方法
-
如果spring中如果某个bean在初始化之后或销毁之前要做一些额外的操作可以为该bean配置初始化和销毁的方法,在这些方法中完成该功能。
-
在Spring创建bean对象时,先创建对象(通过无参构造或工厂),之后立即调用init方法来执行初始化操作,之后此bean就可以哪来调用其它普通方法,而在对象销毁之前,spring容器调用其destory方法来执行销毁操作。
<bean id="person" class="cn.hbh.domain.Person" init-method="init()" destory-method="destory()"></bean> public class Person(){ public Person(){ System.out.println("Person对象被创建了"); } public void init(){ System.out.println("连接数据库"); } public void destory(){ System.out.println("销毁数据库"); } public void addUser(){ System.out.println("增加用户"); } } Person p=(Person)context.getbean("person"); p.addUser(); //控制台 Person对象被创建了 连接数据库 增加用户 销毁数据库