Spring IOC(DI)

Spring IOC

IOC(DI) - 控制反转(依赖注入)
所谓的IOC诚挚为控制反转,简单来说就是将对象创建的权力及对象的生命周期的管理过程交由Spring框架来处理,从此在开发过程中不在需要关注对象的创建和生命周期的管理,而是在需需要时由Spring框架提供,这个由Spring框架管理对象创建个生命周期的机制称之为控制反转,而在创建对象的过程中Spring可以依据配置对对象的属性进行设置,这个过程称之为依赖注入,即DI.
IOC入门案例
1. 导入相关的包(普通的java项目也可以,并不是非要在javaweb环境下使用)
2. 创建Spring的配置文件
Spring采用xml文件作为配置文件,xml文件名字随意,但是通常都叫做applicationContext.xml,通常将其放在类加载的src目录下,方便后续使用;
3. 创建bean类,并在spring正进行配置交由spring来管理

<?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">
        <bean id="person" class="cn.tedu.beans.Person"></bean>
</beans>
  1. 在程序中通过Spring容器过去对象并使用
public class Person_Test {
        @Test
        /**
         * SpringIOC的入门案例
         */
        public void  test1(){
                ApplicationContext context = 
                                new ClassPathXmlApplicationContext("applicationContext.xml");
                Person p = (Person) context.getBean("person");
                p.say();
        }
}

IOC实现原理
在初始化一个spring容器时,审判日功能会去解析指定的xml文件,当解析到其中的标签时,会根据该标签中的class属性指定的类型全路径名,通过反射创建该类的对象,并将该对象存入内置的map中管理,其中键就是
该标签的id值,值就是该对象,之后,当通过getBean方法从容器中获取对象时,其实就是根据传入的条件在内置的Map中寻找是否有匹配的键值,如果有则将该键值对中保存的对象返回,如果没有匹配到则抛出异常
由此推测而知:
默认情况下,多次获取同一个id的bean,得到的将是同一个对象
即使是同一个类,如果配置多个标签具有不同的id,每个id都会在内置Map中有一个键值对,其中的值是这个类创建的不同的对象,
同一个标签下不允许配置多个相同id的,如果配置会在启动时抛异常
IOC获取对象的方式
通过context.getbean()方法获取bean时.可以通过传入(1)id值,(2)class类型这两种方式来获取
通过class方式获取bean时,如果同一个类配置多个bean,则在获取时因为无法确定到底要获取哪个bean会抛异常
因为id是唯一的,不存在这样的问题,所以建议尽量使用id获取bean

@Test
/**
 * SpringIOC获取bean的方式
 */
public void  test3(){
        /*
         xml中的配置
         <bean id="person" class="cn.tedu.beans.Person"></bean>
         */
        /*
         <bean id="person" class="cn.tedu.beans.Person"></bean>
         <bean id="personx" class="cn.tedu.beans.Person"></bean>
         */
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //--通过id获取
        Person p1 = (Person) context.getBean("person");
        p1.say();
        //--通过class获取
        Person p2 = context.getBean(Person.class);
        p2.say();
}

别名标签
在spring中提供了别名标签 可以为配置的起一个别名,注意这仅仅是对指定的起的一个别名,不会额外创建对象存入map.

@Test
/**
 * SpringIOC中bean别名
 */
public void  test4(){
        /*
                <bean id="person" class="cn.tedu.beans.Person"></bean>
            <alias name="要起别名的bean的id" alias="要指定的别名"/>
                <alias name="person" alias="personx"/>        
         */
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //--通过id获取
        Person p1 = (Person) context.getBean("personx");
        p1.say();
}

Spring创建对象的方式
a. 通过类的无参构造方法创建对象
入门案例中使用的恩就是这种方式,当用最普通的方式配置一个时,默认就是采用类的无参构造创建对象,在Spring容器初始化时,通过上配置的class属性反射得到字节码对象,通过newInstance()创建对象

Class c = Class .forName("类的全路径名称")
Object obj = c.newInstance()

这种方式下spring创建对象,要求类必须有无参的构造,否则无法通过反射创建对象,会抛出异常。

package cn.tedu.beans;
 
public class Person {
        public Person(String arg) {
                System.out.println("Person的无参构造执行了。。。");
        }
        public void say(){
                System.out.println("person hello spring~");
        }
}
@Test
/**
 * SpringIOC 创建对象方式 1 - 通过无参构造方法创建对象
 */
public void test5(){
        /*
                 <bean id="person" class="cn.tedu.beans.Person"></bean>
         */
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Person p  = (Person) context.getBean("person");
        p.say();
}

在这里插入图片描述

b. 通过静态工厂创建对象
很多的时候,我们面对的类是无法通过无参构造去创建的,例如该类没有无参构造、是一抽象类 等等情况 ,此时无法要求spring通过无参构造创建对象,此时可以使用静态工厂 方式创建对象。

public class CalendarStaticFactory {
        public static Calendar getCalendar(){
                return Calendar.getInstance();
        }
}
<?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">
        
        <bean id="calendar"  class="cn.tedu.factory.CalendarStaticFactory" factory-method="getCalendar"></bean>
 
</beans>
@Test
/**
 * SpringIOC 创建对象方式 2 - 静态工厂
 */
public void test6(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Calendar calendar = (Calendar) context.getBean("calendar");
        System.out.println(calendar);
}

c. 实例工厂创建对象
实例工厂也可以解决类是无法通过无参构造创建的问题,解决的思路和静态 工厂类似,只不过实例工厂提供的方法不是静态的。spring需要先创建出实例工厂的对象,在调用实例工厂对象上指定的普通方法来创建对象。所以实例工厂也需要配置到Spring中管理。

package cn.tedu.factory;
 
import java.util.Calendar;
 
public class CalendarFactory {
        public Calendar getCalendar(){
                return Calendar.getInstance();
        }
}
<?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">
        
        <bean id="calendarFactory"  class="cn.tedu.factory.CalendarFactory"></bean>
        <bean id="calendar" factory-bean="calendarFactory" factory-method="getCalendar"/>
 
</beans>
@Test
/**
 * SpringIOC 创建对象方式 3 - 实例工厂
 */
public void test7(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Calendar calendar = (Calendar) context.getBean("calendar");
        System.out.println(calendar);
}

d. Spring工厂创建对象
Spring内置了工厂接口,也可以通过实现这个接口来开发Spring工厂,通过这个工厂创建对象。

package cn.tedu.factory;
 
import java.util.Calendar;
 
import org.springframework.beans.factory.FactoryBean;
 
public class CalendarSpringFactory implements FactoryBean<Calendar>{
 
        /**
         * Spring工厂生产对象的方法
         */
        @Override
        public Calendar getObject() throws Exception {
                return Calendar.getInstance();
        }
 
        /**
         * 获取当前工厂生产的对象的类型的方法
         */
        @Override
        public Class<?> getObjectType() {
                return Calendar.class;
        }
 
        /**
         * Spring工厂生产对象时是否采用单例模式
         * 如果返回true,则在spring中该对象只创建一次 后续 重复使用
         * 如果返回false,则每次获取该bean 都重新 创建对象
         */
        @Override
        public boolean isSingleton() {
                return true;
        }
 
}
<?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">
        
        <bean id="calendar" class="cn.tedu.factory.CalendarSpringFactory"></bean>
        
</beans>
@Test
/**
 * SpringIOC 创建对象方式 4 - spring工厂
 */
public void test8(){
        /*
                <bean id="calendar" class="cn.tedu.factory.CalendarSpringFactory"></bean> 
        */
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Calendar calendar = (Calendar) context.getBean("calendar");
        System.out.println(calendar);
}

单例和多例
Spring容器管理的bean在默认情况下是单例的,也即,一个bean只会创建一个对象,存在内置 map中,之后无论获取多少次该bean,都返回同一个对象。
Spring默认采用单例方式,减少了对象的创建,从而减少了内存的消耗。但是在实际开发中是存在多例的需求的,Spring也提供了选项可以将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">
        
        <!-- 
                scope属性控制当前bean的创建模式:
                        singleton:则当前bean处在单例模式中,默认就是此模式
                        prototype:则当前bean处在多例模式中
         -->
        <bean id="cart" class="cn.tedu.beans.Cart" scope="prototype"></bean>
        
</beans>

bean在单例模式下的生命周期:
bean在单例模式下,spring容器启动时解析xml发现该bean标签后,直接创建该bean的对象存入内部map中保存,此后无论调用多少次getBean()获取该bean都是从map中获取该对象返回,一直是一个对象。此对象一直被Spring容器持有,直到容器退出时,随着容器的退出对象被销毁。

bean在多例模式下的生命周期:
bean在多例模式下,spring容器启动时解析xml发现该bean标签后,只是将该bean进行管理,并不会创建对象,此后每次使用 getBean()获取该bean时,spring都会重新创建该对象返回,每次都是一个新的对象。这个对象spring容器并不会持有,什么销毁取决于使用该对象的用户自己什么时候销毁该对象。

懒加载机制
Spring默认会在容器初始化的过程中,解析xml,并将单例的bean创建并保存到map中,这样的机制在bean比较少时问题不大,但一旦bean非常多时,spring需要在启动的过程中花费大量的时间来创建bean 花费大量的空间存储bean,但这些bean可能很久都用不上,这种在启动时在时间和空间上的浪费显得非常的不值得。
所以Spring提供了懒加载机制。所谓的懒加载机制就是可以规定指定的bean不在启动时立即创建,而是在后续第一次用到时才创建,从而减轻在启动过程中对时间和内存的消耗。
懒加载机制只对单例bean有作用,对于多例bean设置懒加载没有意义。
懒加载的配置方式:
为指定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"
        >
        
        <bean id="cart" class="cn.tedu.beans.Cart" lazy-init="true"></bean>
        
</beans>

为全局配置懒加载

<?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.tedu.beans.Cart"></bean>
        
</beans>

**如果同时设定全局和指定bean的懒加载机制,且配置不相同,则对于该bean局部配置覆盖全局配置。

配置初始化和销毁的方法
在Spring中如果某个bean在初始化之后 或 销毁之前要做一些 额外操作可以为该bean配置初始化和销毁的方法 ,在这些方法中完成要功能。
实验:通过断点调试模式,测试初始化方法 和 销毁方法的执行

package cn.tedu.beans;
 
public class ProdDao {
        
        public ProdDao() {
                System.out.println("ProdDao 被创建。。。");
        }
        
        public void init(){
                System.out.println("init。。连接数据库。。。。。");
        }
        
        
        public void destory(){
                System.out.println("destory。。断开数据库。。。。。");
        }
        
        public void  addProd(){
                System.out.println("增加商品。。");
        }
        public void  updateProd(){
                System.out.println("修改商品。。");
        }
        public void  delProd(){
                System.out.println("删除商品。。");
        }
        public void  queryProd(){
                System.out.println("查询商品。。");
        }
}
<?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"
        >
        
        <bean id="prodDao" class="cn.tedu.beans.ProdDao" 
                init-method="init" destroy-method="d y"></bean>
        
</beans>
@Test
/**
 * SpringIOC 初始化和 销毁方法
 */
public void test11(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        ProdDao prodDao = (ProdDao) context.getBean("prodDao");
        prodDao.addProd();
        context.close();
}

Spring中关键方法的执行顺序:
在Spring创建bean对象时,先创建对象(通过无参构造或工厂),之后立即调用init方法来执行初始化操作,之后此bean就可以哪来调用其它普通方法,而在对象销毁之前,spring容器调用其destory方法来执行销毁操作。

猜你喜欢

转载自blog.csdn.net/qq_43658452/article/details/85117835