Java Spring5之IOC容器

目录

xml配置文件实现Bean管理操作

创建对象

获取对象实例

注入属性

通过Set方法注入属性

通过构造方法注入属性

注入空值

注入特殊符号

注入外部Bean

注入内部Bean

注入集合类型属性

提取集合类型属性公共部分

引入外部属性文件

 Spring自动装配

通过属性名自动装配

通过属性类型自动装配

普通bean与FactoryBean

注解实现Bean管理操作

创建对象

注入属性

Bean的作用域

Bean的生命周期 


xml配置文件实现Bean管理操作

创建对象

<bean id="设置id" class="类路径"></bean>

<bean id="user" class="com.pojo.User"></bean>

获取对象实例

//加载spring配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
//获取配置创建的对象
User user = context.getBean("user", User.class);//第一个是调用的id,根据id来调用不同方法

注入属性

通过Set方法注入属性

类当中要有属性对应的Set方法

<bean id="book1" class="com.pojo.Book">
        <!--<property>标签设置属性-->
        <property name="bookname" value="第一本书"></property>
        <property name="author" value="作者一号"></property>
</bean>

通过构造方法注入属性

类当中要有对应的构造方法

<bean id="book2" class="com.pojo.Book">
        <!--<property>标签设置属性-->
        <constructor-arg name="bookname" value="第二本书"></constructor-arg>
        <constructor-arg name="author" value="作者二号"></constructor-arg>
</bean>

注入空值

<!--空标签<null>-->

<property name="bookname"><null/></property>

注入特殊符号

<!--CDATA[]中的为属性的内容-->

<property name="bookname">
    <value><![CDATA[<!--特殊符号-->]]></value>
</property>

注入外部Bean

属性为某个类时,通过另一个bean标签实例化这个类

<!--<property name="属性名" ref="bean标签id"></property>-->

<bean id="userService" class="com.service.UserService">
    <property name="userDaoImpl" ref="userDaoImpl"></property>

    <!--除了在userDaoImpl中注入属性,还可以通过如下方式注入属性-->
    <!--  <property name="userDaoImpl.data" value="数据002"></property>  -->

</bean>

<bean id="userDaoImpl" class="com.daoImpl.UserDaoImpl">
    <property name="data" value="数据"></property>
</bean>

注入内部Bean

<bean id="userService" class="com.service.UserService">
    <!--注入内部Bean-->
    <property name="userDaoImpl">
        <bean id="userDaoImpl" class="com.daoImpl.UserDaoImpl">
            <property name="data" value="数据"></property>
        </bean>
    </property>

    <!--下面这种注入属性的方式也是可以的,会覆盖掉前面的数据-->
    <!--  <property name="userDaoImpl.data" value="数据002"></property>   -->

</bean>

注入集合类型属性

<bean id="student" class="com.pojo.Student">
    <!--注入数组-->
    <property name="array">
        <array>
            <value>array1</value>
            <value>array2</value>
            <value>array3</value>
        </array>
    </property>
    <!--注入List集合-->
    <property name="list">
        <list>
            <value>list1</value>
            <value>list2</value>
            <value>list3</value>
        </list>
    </property>
    <!--注入Map-->
    <property name="map">
        <map>
            <entry key="key1" value="value1"></entry>
            <entry key="key2" value="value2"></entry>
            <entry key="key3" value="value3"></entry>
        </map>
    </property>
    <!--注入set集合-->
    <property name="set">
        <set>
            <value>set1</value>
            <value>set2</value>
            <value>set3</value>
        </set>
    </property>
    <!--list集合当中注入类-->
    <property name="courseList">
        <list>
            <ref bean="course1"></ref>
            <ref bean="course2"></ref>
        </list>
    </property>
</bean>

<bean id="course1" class="com.pojo.Course">
    <property name="courseName" value="course1"></property>
</bean>

<bean id="course2" class="com.pojo.Course">
    <property name="courseName" value="course2"></property>
</bean>

提取集合类型属性公共部分

首先修改xml文件配置,添加一行xmlns:util,并复制xsi:schemaLocation的内容,将其中的beans全部替换为util即可。

<!--设置公共部分-->
<util:list id="bookList">
    <value>book1</value>
    <value>book2</value>
</util:list>

<bean id="book" class="com.pojo.Book">
<!--list集合默认数据为公共部分内容-->
    <property name="list" ref="bookList"></property>
</bean>

引入外部属性文件

示例:通过引入外部属性文件配置数据库

首先引入druid的jar包,然后新建一个properties配置文件,在配置文件当中写入数据库对应的连接地址、驱动等信息。

 在Spring的xml文件当中设置一个名称空间,方式和提取集合类型属性公共部分中设置名称空间一样,名称空间为context。

 然后使用标签引入外部属性文件,这样就达到了引入外部属性文件的效果。

<!--    引入外部属性文件配置连接池    -->
<context:property-placeholder location="classpath:jdbc.properties"/><!-- location属性表示配置文件的地址 -->

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${prop.driverClass}"></property><!-- value的值为配置文件当中前面的属性名 -->
    <property name="url" value="${prop.url}"></property>
    <property name="username" value="prop.userName"></property>
    <property name="password" value="prop.password"></property>
</bean>

 Spring自动装配

前面我们所写的property标签等手动写代码设置属性对应的值称为手动装配,然而Spring还提供了一个自动装配的功能(这种方式实际使用情况不多)。

Spring的自动装配会根据指定的装配规则(属性名称或者属性类型),自动将匹配的属性值进行注入。下面主要演示通过属性名自动装配和属性类型自动装配两种方式。

通过设置标签属性autowire来设置自动装配的方式,ByName是通过属性名称注入,ByType是通过属性类型注入。

Employee类对应的代码

package com.autoWire;

public class Employee {

    private Dept dept1;
    private Dept dept2;

    public void setDept1(Dept dept1) {
        this.dept1 = dept1;
    }

    public void setDept2(Dept dept2) {
        this.dept2 = dept2;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "dept1=" + dept1 +
                ", dept2=" + dept2 +
                '}';
    }
}

通过属性名自动装配

<!-- 通过设置标签属性autowire来设置自动装配的方式,ByName是通过属性名称注入,ByType是通过属性类型注入 -->
<bean id="employee" class="com.autoWire.Employee" autowire="byName"></bean>

<bean id="dept1" class="com.autoWire.Dept">
    <property name="dept" value="dept1  Test"></property>
</bean>

<bean id="dept2" class="com.autoWire.Dept">
    <property name="dept" value="dept2  Test"></property>
</bean>

测试代码

@Test
public void test6() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml");
    //属性名自动装配
    Employee employee001 = context.getBean("employee001", Employee.class);
    System.out.println(employee001.toString());
}

输出内容

通过属性类型自动装配

通过属性类型自动装配有一个缺点,当配置的同一个类型超过一个时,无法识别具体用哪一个配置。例如,如果我们将属性名自动装配的代码标签属性autowire修改为byType,那么就会报错,因为有两个dept类型,无法识别具体用哪一个类型。

<bean id="employee002" class="com.autoWire.Employee" autowire="byType"></bean>
<bean id="dept" class="com.autoWire.Dept">
    <property name="dept" value="dept  Test"></property>
</bean>

测试代码

@Test
public void test6() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml");
    //属性类型自动装配
    Employee employee002 = context.getBean("employee002", Employee.class);
    System.out.println(employee002.toString());
}

输出结果

普通bean与FactoryBean

1.Spring有两种类型的bean,一种是普通的bean,另一种是FactoryBean。

2.普通bean的返回类型和xml文件中配置的类型一致,而FactoryBean类型可以和返回的类型不一样。

xml文件内容

<bean id="mybean" class="com.factoryBean.MyBean"></bean>

MyBean类

通过修改泛型,可以设置实际返回的类型。下面的代码实际返回的类型为Course类。

注解实现Bean管理操作

使用注解可以简化xml配置,代码量大大减少。

创建对象

可以通过以下4个注解来实现创建对象的功能,根据规范来选择使用哪一个注解,但是功能都是一样的,只是名字不一样。

 首先将Spring的jar包全部导入进去,然后在xml文件当中开启组件扫描。

<!--base-package属性设置扫描包的范围,如果有多个包需要扫描,用逗号隔开写。当然也可以直接扫描包的上级目录-->
<context:component-scan base-package="com"></context:component-scan>

然后新建一个类使用注解设置value(这里只演示怎么创建对象,创建一个空类即可)

@@Controller(value = "person")//这里的value相当于Bean标签的id属性,可以不写value值,默认为类名首字母小写
public class Person {
}

测试代码

@Test
public void test2(){
    ApplicationContext context=new ClassPathXmlApplicationContext("bean1.xml");
    Person person = context.getBean("person", Person.class);
    System.out.println(person);
}

这样就通过注解完成了Person类的实例化,在组件扫描过程当中,可以自己设置扫描的方式有选择性的扫描,具体代码如下

<!--
use-default-filters属性设置不使用默认的扫描方式
include-filter属性设置扫描包含Controller注解的类
-->
<context:component-scan base-package="com" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

<!--exclude-filter属性设置扫描不包含Controller注解的类,和include-filter正好相反-->
<context:component-scan base-package="com" use-default-filters="false">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

注入属性

实验过程:首先编写一个UserDao接口类,让UserDaoImpl类和UserDaoImpl002两个类都实现这个接口。然后编写一个Data类测试通过@Autowired来注入属性。当有多个类都实现了UserDao类时,使用@Autowired无法确认具体通过哪个类来实例化,再加上@Qualifier(value = “userDaoImpl”)来测试这两个注释的结合使用,同时也通过@Resource测试了通过名称注入和通过类型注入,很显然通过类型注入是不行的,原因和注解@Autowired一样。最后普通属性的注入就很简单了,直接通过@Value(value = "对应的值")来设置即可。

UserDao接口类

public interface UserDao {
    public void test();
}

UserDaoImpl实现类(UserDaoImpl002只是修改名称和输出即可)

@Component
public class UserDaoImpl implements UserDao {
    @Override
    public void test() {
        System.out.println("userDaoImpl");
    }
}

Data类

@Component
public class Data {
    private String location="data1";
    private String phoneNumber="data2";

    @Override
    public String toString() {
        return "Data{" +
                "location='" + location + '\'' +
                ", phoneNumber='" + phoneNumber + '\'' +
                '}';
    }
}

Person类(主要的类)

@Component
public class Person {
   
    @Value(value = "17")    //注入普通属性
    private int age;
    @Value(value = "小明")
    private String name;

    @Autowired  //根据属性类型进行自动装配,这里会直接实例化Data类
    private Data data;

    @Autowired
    @Qualifier(value = "userDaoImpl")//@Qualifier注解要和@Autowired一起使用,表示通过这一类型下的这一名称进行注入
    private UserDao userDao;//UerDao是一个接口,这里是多态的写法,当有多个类都实现了这一接口的时候,只根据属性类型无法判断具体注入哪一个类,需要加上类的名称

    //@Resource   //既可以通过类型注入(和@Autowired类似),也可以通过名称注入
    @Resource(name = "userDaoImpl002")
    private UserDao userDao002;


    public void test() {
        System.out.println("age=" + age + "  " + "name=" + name);
        System.out.println(data.toString());
        userDao.test();
        userDao002.test();
    }
}

测试代码

@Test
public void test2() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    Person person = context.getBean("person", Person.class);
    person.test();
}

输出结果

不难看出,通过注解的方式代码量更少,简化了xml配置。 

Bean的作用域

Spring默认情况下,bean是单实例对象。可以通过修改参数设置bean是单实例还是多实例。

xml文件如下

<bean id="person" class="com.pojo.Person">
        <property name="age" value="18"></property>
        <property name="location" value="北京"></property>
        <property name="name" value="小王"></property>
</bean>

 测试代码如下

@Test
public void test4() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
    Person person1 = context.getBean("person", Person.class);
    Person person2 = context.getBean("person", Person.class);

    System.out.println(person1+"  "+person2);
    System.out.println("person1.hashCode()="+person1.hashCode()+"  person2.hashCode()="+person2.hashCode());
}

输出结果

 

 两个Person类的hashCode和地址相同,所以Spring中的bean默认为单实例对象。

 通过设置bean标签的scope属性可以设置是单实例对象还是多实例对象。

<bean id="person" class="com.pojo.Person" scope="prototype"></bean>

修改过后再次运行测试代码输出如下

 

Bean的生命周期 

 没有加入后置处理器的情况下,bean的生命周期分为5步,加入了后置处理器之后,分为7步,配置了后置处理器之后,xml文件当中所有的bean标签都拥有了这个后置处理器,具体如下所示。

xml配置

<!-- init-method 设置初始化时调用的方法,destroy-method 设置销毁时调用的方法 -->
<bean id="orders" class="com.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
    <property name="orderName" value="数据"></property>
</bean>

<!--配置后置处理器,因为MyBeanPost实现了BeanPostProcessor接口,Spring会自动把这个类当成后置处理器使用-->
<bean id="myBeanPost" class="com.bean.MyBeanPost"></bean>

bean对应的类

package com.bean;

public class Orders {

    private String orderName;

    public Orders() {
        System.out.println("第一步 通过构造器创建实例");
    }

    public void setOrderName(String orderName) {
        this.orderName = orderName;
        System.out.println("第二步 通过Set方法设置属性值");
    }

    public void initMethod(){
        System.out.println("第三步 执行初始化的方法");
    }

    public void destroyMethod(){
        System.out.println("第五步 销毁bean对象");
    }

}

配置后置处理器的类

package com.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPost implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化之前的方法");
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化之后的方法");
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}

测试代码

@Test
public void test5() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
    Orders orders = context.getBean("orders", Orders.class);
    System.out.println("第四步 获取bean实例");
    System.out.println("输出orders:"+orders);
    //ApplicationContext中并没有close方法,转换成ClassPathXmlApplicationContext
    ((ClassPathXmlApplicationContext) context).close();
}

输出结果

通过结果我们可以看到,如果没有配置后置处理器,只会出现第一步到第五步,配置了后置处理器之后,在初始化之前和之后分别可以调用一个方法,这就被分成了7步。

Spring IOC容器主要内容到此结束......

  • 16
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

算不出来没办法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值