bean方式实现Spring IOC和DI

Spring的核心技术

  • IOC(DI)
    • 控制反转(依赖注入)
  • AOP
    • 面向切面编程

一、Spring的IOC(就是将创建对象的权利交给Spring容器)

   所谓的IOC称之为控制反转,简单来说就是将对象的创建和对象的生命周期管理的过程交给Spring框架处理,从此在开发过程中不再需要关注这些细节。需要使用到对象时,直接从Spring框架中获取。

1.1 IOC的使用

  • 创建项目
  • 导包
  • 导入核心4个包(core context expression beans)和一个日志包(jcl)核心四加一
  • 配置
    1.建立java类
public class Person {
    public void eat(){
        System.out.println("eat....");
    }
}

2.编写配置文件中

<?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">
<!--以上为约束文件-->
    <!--Spring 的核心配置文件以下-->
bean 就代表实体类,
id 唯一标识,一般为类名的小写,不能重复 
class 那个类需要被spring容器进行管理的类,写那个类的全限定名
<bean id ="person" class="com.shengqi.entity.Person"></bean>
别名标签,duiID起别名可以将person的别名改为aaa,并且上面的 id可以改为name,与别名相对应
<bean name ="person" class="com.shengqi.entity.Person"></bean>
<alias name="person" alias="aaa"></alias>
</beans>

3.测试

  • ClassPathXmlApplicationContext 从类文件下寻找对应的xml文件
  • FileSystemXmlApplicationContext 从系统路径(磁盘开始)下找
public class DemoTest {
    @Test
    public void test01(){
        //从spring容器中获取对象
     //1.加载spring的核心配置文件(加载Spring容器)
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        Person person = (Person) applicationContext.getBean("person");
        person.eat();
//        把对象创建的权利和对象生命周期的权利,都交给了Spring进行管理
    }
}

4.解释
ClassPathXmlApplicationContext从类路径下找到applicationContext.xml文件,找到之后,
applicationContext.getBean(“person”)找到对应的Bean 当读到<Bean标签时,根据class中的全路径名创建对象(利用反射)

  • xml 也就是说bean标签底层(利用反射)创建了对象
  • 并且 Spring容器,实际上就是一个map集合,也就是说在Spring容器中还执行了map的添加
  • id作为key,创建好的对象作为value
  • test applicationContext.getBean(“person”);其实就是执行了map.get(id)
//获取字节码文件对象
    Class<?> aClass = Class.forName("com.shengqi.entity.Person");
//通过无参构造获取对象
    Person person = (Person) aClass.newInstance();
        map.put(id,person)

1.2 IOC的使用原理

当初始化一个Sring容器时,会先去指定路径下寻找到xml,然后解析,回去到bean标签的class属性对应的值。根据全路径名通过反射技术获取对象,并且以id的值为key,对象的值为value,存入到map中。

当我们使用getBean获取对象时,从map中通过传入的key获取值,如果获取到,那么就返回对象,如果获取不到,返回null

  Person person = (Person) applicationContext.getBean("person");
  Person person1 = (Person) applicationContext.getBean("person");
  System.out.println(person==person1);

以上代码的结果为True,很明显,因为从同一个map中获取的对象,地址值必然相同,

现在模拟bean(反射创建对象被执行两次)

    <bean id ="person" class="com.shangma.entity.Person"></bean>
    <bean id ="person1" class="com.shangma.entity.Person"></bean>
  Person person = (Person) applicationContext.getBean("person");
  Person person1 = (Person) applicationContext.getBean("person1");
  System.out.println(person==person1);

上面结果为false,bean所代表的的反射执行两次,map集合中,存储了两个对象,再调用map.get(id),时两次取的就不是一个对象了。

  • 默认情况下,多次获取同一个id的bean,得到的是同一个对象
  • 即使是同一个类,如果配置过多个不同id标签,每个id都会在内存中创建一个新的对象,不同的id就是对应的类型相同,但是对象也不同。
  • 同一个beans标签下,不允许出现相同的id

1.3 IOC的获取对象的方式

  1. 根据id获取(重载)需要强转,就会有风险
  Person person = (Person) classPathXmlApplicationContext.getBean("person");
  1. 根据class(重载的方法)并且相同class的bean标签只能有一个
Person bean = classPathXmlApplicationContext.getBean(Person.class);
  1. 根据id和class确定(重载) 唯一确定,不需要强转,相同class的bean标签可以有多个
Person person = applicationContext.getBean("person", Person.class);

1.4 Spring创建对象的方式(默认都是单例)(设计模式:工厂)

因为

1.4.1 通过无参构造创建

默认调用的是无参构造的方式创建对象,使用这种方式,一定要保证类中有无参构造,否则抛出异常

1.4.2 通过静态工厂创建对象(不能new的情况下抽象类:Calender)

普通方式获取日历类

 Calendar calender = Calendar.getInstance();
        System.out.println(calender);
  • 静态工厂模式
public class CalendarStaticFactory {
    /**
     * 静态方法获取日历的实例
      * @return 日历的实例
     */
public static Calendar getCalendar(){
    return Calendar.getInstance();
}
}

bean 中添加 factory-method=“getCalendar” 获取这个方法的返回值,如果不填则返回的是类的对象,而不是这个类的方法所返回的对象
将class这个类的 factory-method 找到的方法的返回值 作为value 给id对应 在map 中

只有一行,因为不需要创建对象,通过类名.静态方法名
 <bean id="calender" class="com.shangma.factory.CalendarStaticFactory" factory-method="getCalendar"></bean>

1.4.3 通过实例工厂创建对象

xml

<!- 实例工厂模式
    <bean id="calender01" class="com.shangma.factory.CalendarObjectFactory"></bean>
    <bean id="calender" factory-bean="calender01" factory-method="getCalendar"></bean>
<!- 第一行创建对象CalendarObjectFactory, 第二行,actory-bean="calender01"得到第一行创建的对象,
得到factory-method="getCalendar"方法的返回值,也就是得到CalendarObjectFactory对象的方法getCalendar的返回值,存在map中

entity

public class CalendarObjectFactory {
    /**
     * 通过类的实例对象调用方法得到Calendar的返回值
     * @return clendar
     */
    public Calendar getCalendar(){
        return  Calendar.getInstance();
    }
}

1.4.3 内置工厂创建对象

需要实现Spring内置的工厂接口(FactoryBean)。
entity

public class CalendarSpringFactory implements FactoryBean {
    /**
     * 使用工厂接口产生的对象
     */
    @Override
    public Object getObject() throws Exception {
        return Calendar.getInstance();
    }
    /**
     * 获取对象的类型
     */
    @Override
    public Class<?> getObjectType() {
        return Calendar.class;
    }

    /**
     * 是否是单例
     * 在JDK8之后,接口中已经默认实现了该方法。返回的结果是true,说明默认就是单例
     * false:多例
     * true:单例 内存中只有一个,无论new多少次
     * @return
     */
//    @Override
//    public boolean isSingleton() {
//        return false;
//    }
}

xml

  <!--内置工厂-->
    <bean id="calendarspring" class="com.shangma.factory.CalendarSpringFactory"></bean>

1.5 单例和多例

  • pring容器管理的bean在默认情况下是单例的。就是说,一个bean只会创建一个对象,放入到对应的map中。无论根据id获取多少次该bean,都返回的是同一个对象
  • 多例 ,每次getbean就会拿一个,会造成内存资源的浪费

1.5.1验证

 Object calender = classPathXmlApplicationContext.getBean("calendarspring");
        Object calender2 = classPathXmlApplicationContext.getBean("calendarspring");
        System.out.println(calender==calender2);
  • 结果为true

如果在实际开发中有多例的需求,Spring也可以设置。scope=“prototype”

<!--通过spring的内置工厂创建对象-->
    <bean id="person" class="com.shangma.cn.entity.Person" scope="prototype"></bean>

1.5.2单例模式下bean对象的生命周期

bean在单例模式下,Spring容器启动时解析xml发现该bean标签,直接将对象创建成功并且存入内置的map中保存。此后无论调用多少次getBean()获取该bean属性,都是从map中获取该对象,所以一直是同一个对象。此对象一直被Spring容器持有,直到容器退出时对象才被销毁。

   ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
  //bean在单例模式下,上面这行代码执行完,对象已经被创建了 
        Object calender = classPathXmlApplicationContext.getBean("calendarspring");
        Object calender2 = classPathXmlApplicationContext.getBean("calendarspring");
        System.out.println(calender==calender2);
        //无论多少次getBean()获取的都是一个对象,此对象一直被spring容器持有

1.5.3 多例模式下bean对象的生命周期

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

1.6 懒加载机制

为什么引入懒加载?: Spring默认是所有的Bean都在加载容器时创建,所以有很多Bean加载,创建对象也会造成资源的浪费,时间上效率低,并且我只需要一个,却全部都加载了,不需要那么多,所以需要懒加载机制(用到的时候才需要,不用不创建)

所谓的懒加载机制就是可以规定指定的bean不在启动时马上加载,而是在用户真正第一次使用到时才去加载对象。

懒加载机制只对单例bean有作用。对于多例bean设置懒加载没有意义。
局部配置

<bean id="person" class="com.shangma.cn.entity.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.xsd"
       全局设置:
        default-lazy-init="true"
>

java就近原则,全局设置不懒加载,局部设置,局部会生效,局部优先级高

1.7 Spring的初始化和销毁方法

xml

<!--
    配置初始化和销毁的方法
    
-->
init-method和 destroy-method想让那个方法先执行,后执行都可以放在这两个方法里
    <bean id="productDao" class="com.shangma.cn.dao.ProductDao" init-method="init" destroy-method="destory"></bean>

test

    public void test06(){
        ProductDao productDao = (ProductDao) classPathXmlApplicationContext.getBean("productDao");
        productDao.add();
        //没有手动销毁时,未执行 销毁方法,因为这个类,仍然被Spring容器把持
//        需要手动销毁才会被执行
        classPathXmlApplicationContext.close();
    }
}

entity

public class ProductDao {
    public ProductDao() {
        System.out.println("无参构造");
    }
    public void init(){
        System.out.println("init");
    }
    public void destory(){
        System.out.println("关闭容器");
    }
    public void add(){
        System.out.println("close");
    }
}

结果
在这里插入图片描述


二. Spring的DI(属性注入值)

在创建对象的过程中Spring可以根据配置对对象中的属性进行赋值,这个过程就称之为依赖注入。

依赖注入分为两大方式:

  • set方法注入
  • 构造方法注入

2.1 set方法注入

通常的javabean属性都会私有化,所以前提条件是提供了set方法才可以赋值
set方法可以注入的内置类型:基本数据类型,内置引用数据类型(String、int[]、List、Set、Map、Properties)
set方法可以注入自定义类型
xml

entity

public class Dog {
    private int id;
    private String name;
    public void setId(int id) {
        this.id = id;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Dog{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

public class Hero {
    // 英雄id
    private int id;
    // 英雄名
    private String name;
    // 数组
    private int[] array;
    // list
    private List<String> list;
    //  Set
    private Set<String> set;
    // Map
    private Map<String,String> map;
    // properties
    private Properties properties;

    // dog
    private Dog dog;

    public void setId(int id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setArray(int[] array) {
        this.array = array;
    }

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

    public void setSet(Set<String> set) {
        this.set = set;
    }

    public void setMap(Map<String, String> map) {
        this.map = map;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    @Override
    public String toString() {
        return "Hero{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", array=" + Arrays.toString(array) +
                ", list=" + list +
                ", set=" + set +
                ", map=" + map +
                ", properties=" + properties +
                ", dog=" + dog +
                '}';
    }
}

《创建对象:bean》
  <bean id="hero" class="com.shangma.entity.Hero" >
  《给属性赋值: property》
        <property name="id" value="10"></property>
        <property name="name" value="压缩"></property>
        <property name="array">
            <array>
                <value>6</value>
                <value>11</value>
                <value>15</value>
            </array>
        </property>
        <property name="list">
            <list>
                <value></value>
                <value></value>
                <value></value>
            </list>
        </property>
        <property name="set">
            <set>
                <value></value>
                <value>adc</value>
                <value>法师</value>
            </set>
        </property>
        <property name="map">
            <map>
                <entry key="" value="8000"></entry>
                <entry key="" value="12000"></entry>
            </map>
        </property>
        <property name="properties">
            <props>
                <prop key="">money</prop>
                <prop key="">ok</prop>
            </props>
        </property>
        <!--自定义类型
        如果是对象里嵌套对象使用
        ref导入(引用)-->
        <property name="dog" ref="dog"></property>
    </bean>

    <bean id="dog" class="com.shangma.entity.Dog">
        <property name="id" value="5"></property>
        <property name="name" value="5"></property>
    等同与 Dog gog=new dog
    dog.setName(“5”)
    dog.setid(5)
    </bean>
   public void test07(){
        Hero bean = classPathXmlApplicationContext.getBean(Hero.class);
        System.out.println(bean);

    }

结果

Hero{id=10, name='压缩', array=[6, 11, 15], list=[,,], set=[, adc, 法师], map={=8000,=12000}, properties={=money,=ok}, dog=Dog{id=5, name='5'}}

2.2 自动装配

  • 自动装配:
    可以根据id名或者类型自动进行赋值
  • 参数解析
    1.autowire设定自动装配:
    2.byName:根据类中需要注入的属性的名字,在Spring容器中找对应id的bean标签,如果找到,自动匹配
    <bean id="dog1" class="com.shangma.entity.Dog" autowire="byName">
        <property name="id" value="15"></property>
        <property name="name" value="456"></property>
        <property name="cat" ref="cat111"></property>
    </bean>
    <bean id="cat111" class="com.shangma.entity.Cat">
        <property name="name" value="123"></property>
    </bean>

3.byType:根据类中需要注入的属性的类型,在Spring容器中找对应的class类型的bean标签。将该bean标签的对象赋值给当前的属性
*有两个相同的bean时,会报错,使用范围低最好使用byName

    <bean id="dog1" class="com.shangma.entity.Dog" autowire="byType">
        <property name="id" value="15"></property>
        <property name="name" value="456"></property>
				不需要引入cat属性
    </bean>
    <bean id="cat111" class="com.shangma.entity.Cat">
        <property name="name" value="123"></property>
    </bean>

4.byType的方式 根据类型进行匹配,如果找不到对应的类型,匹配null。如果Spring容器中有多个相同的类型,会抛出异常
5.全局自动装配

<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"
       全局懒加载
        default-lazy-init="true"
        全局自动装配
       default-autowire="byName"
>

2.3 构造方法注入

<bean id="catt" class="com.shangma.entity.Cat" >
        <constructor-arg index="0" name="name" value="11"></constructor-arg>
        <constructor-arg index="1"   ref="dogg"></constructor-arg>
    </bean>
    <bean id="dogg" class="com.shangma.entity.Dog">
        <property name="id" value="1"></property>
        <property name="name" value="12"></property>
    </bean>
  public Cat(String name, Dog dog) {
        this.name = name;
        this.dog = dog;
    }

构造器自动装配 autowire=“constructor” 可以不用写属性

    <bean id="catt" class="com.shangma.entity.Cat" autowire="constructor" >
        <constructor-arg index="0" name="name" value="11"></constructor-arg>
        
    </bean>
    <bean id="dogg" class="com.shangma.entity.Dog">
        <property name="id" value="1"></property>
        <property name="name" value="12"></property>
    </bean>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值