Java Spring5之IOC容器
Posted 知道什么是码怪吗?
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java Spring5之IOC容器相关的知识,希望对你有一定的参考价值。
目录
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容器主要内容到此结束......
以上是关于Java Spring5之IOC容器的主要内容,如果未能解决你的问题,请参考以下文章
Spring5源码分析(006)——IoC篇之核心类DefaultListableBeanFactory和XmlBeanDefinitionReader