IoC(上)(xml配置)
Spring通过IoC容器来进行管理、所有的JAVA对象的实例化和初始化,控制对象之间的依赖关系,通过IoC进行管理的对象叫做Java Bean,其在本质上与自己new出来的对象没有区别。
IoC是一种思想,不是一种技术,其将创建对象和维护对象之间关系的权利交出去,降低了耦合度,提高了扩展性。
IoC中的抽象类 BeanDefinitionReader 所实例化的方法,会通过Bean的信息通过BeanFactory+反射的方式对java bean进行实例化,再初始化,最终得到实例对象(使用contex.getBean()可以获取对象)。
依赖注入
DI(依赖注入)实现了控制反转的思想
依赖注入是指在spring创建对象的过程中,将对象依赖属性通过配置注入。
依赖注入常使用set注入或构造注入的方式进行。
IoC容器使用BeanFactory进行实现,但该接口不对开发人员开放,其子接口ApplicationContext面相spring的使用者。
Bean管理的两种方式
基于xml配置文件进行Bean管理
将之前在子模块的POM.xml文件中的以来配置修改到父工程的POM.xml文件中。
在Java中创建一个User类:
package com.qinghe.spring6.iocxml;
public class User {
private String name;
private Integer age;
public void run() {
System.out.println("run....................");
}
}
在Bean.xml中创建一个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">
<bean id="user" class="com.qinghe.spring6.iocxml.User"></bean>
</beans>
Bean的获取
创建一个ApplicationContext对象,根据该ApplicationContext对象获取需要的Bean
- 根据bean.xml中的id进行获取
- 根据类型进行获取
- 根据id和类型获取
//创建一个ApplicationContext对象,用来获取Bean
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//1 利用context对象获取Bean对象(根据id)
User user1 = (User)context.getBean("user");
System.out.println("根据id获取Bean:" + user1);
//2 利用类型获取Bean对象
User user2 = context.getBean(User.class);
System.out.println("根据类型获取Bean:" + user2);
//3 利用id和类型获取Bean对象
User user3 = context.getBean("user", User.class);
System.out.println("根据id和类型获取Bean:" + user3);
若UserDao.class获取到的是一个接口,则会自动获取它的实现类(前提是实现类在bean.xml中进行过配置),但若在bean.xml中配置过多个UserDao的实现类,则无法自动获取(不知道要的是哪个)
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//如果通过类型获取的是一个接口,则获取到的自动是他的实现类userDaoImpl
UserDao userDao = context.getBean(UserDao.class);
System.out.println(userDao);
userDao.run();
}
依赖注入
基本类型依赖注入
依赖注入是指向类中的属性注入数值,有利用set进行注入与利用构造器进行注入等方式
-
基于set方法进行依赖注入
在bean中对属性要生成set方法,在配置文件中进行相应的配置,在测试文件中使用IoC进行创建对象时就会由spring自动进行依赖注入,得到一个有属性值的对象。
bean-di.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"> <!-- 进行类的配置与依赖的注入--> <bean id="book" class="com.qinghe.spring6.iocxml.di.Book"> <!-- property标签调用set方法注入具体的依赖--> <property name="bname" value="芜湖起飞"></property> <property name="author" value="大司马"></property> </bean> </beans>
-
基于构造器进行注入
在配置文件中进行相应的配置、标签为constructor-arg。
<!-- 基于构造器进行依赖注入--> <bean id="bookCon" class="com.qinghe.spring6.iocxml.di.Book"> <!-- 构造器注入的方式--> <constructor-arg name="bname" value="红楼梦"></constructor-arg> <constructor-arg name="author" value="曹雪芹"></constructor-arg> </bean>
若要赋null,则应该在property标签下使用null标签
<property name="others">
<null/>
</property>
特别的,若要在属性中赋"<“或”>"则应该使用xml实体(转义)使用<
表示<, 使用>
表示>。(别忘记分号)
<property name="others" value="<>"></property>
更特别的,xml支持使用CDATA区来对符号(>,<)进行解析,解析的结果会完全成立为CDATA区内的结果,不会被误识别。
<property name="others">
<value>[!CDATA[a > b]]</value>
</property>
对象类型依赖注入
若需要注入每个对象类型的数据,则要提前在bean中声明好,再进行注入
- 引入外部bean的方式
<bean id="dept" class="com.qinghe.spring6.iocxml.ditest.Dept">
<property name="dname" value="安保部"></property>
</bean>
<bean id="emp" class="com.qinghe.spring6.iocxml.ditest.Emp">
<!-- ref属性可以将一个已经在xml中配置完成的类所创建的对象作为参数传递给另一个类-->
<property name="dept" ref="dept"></property>
<property name="ename" value="马冬梅"></property>
<property name="age" value="18"></property>
</bean>
这种通过ref属性引入另一个Bean标签的方式来注入对象的方式叫做通过引入外部bean的方式进行对象类型的依赖注入。
- 内部Bean方式进行依赖注入
<bean id="emp2" class="com.qinghe.spring6.iocxml.ditest.Emp">
<property name="dept">
<bean id="dept2" class="com.qinghe.spring6.iocxml.ditest.Dept">
<property name="dname" value="财务部"></property>
</bean>
</property>
<property name="ename" value="mary"></property>
<property name="age" value="3"></property>
</bean>
在一个Bean中创建另一个Bean的方式叫做内部Bean的方式进行依赖注入
-
级联赋值
级联赋值可以在xml文件中实现对已有属性的赋值:
<bean id="dept" class="com.qinghe.spring6.iocxml.ditest.Dept"></bean> <bean id="emp" class="com.qinghe.spring6.iocxml.ditest.Emp"> <property name="ename" value="tom"></property> <property name="age" value="999"></property> <property name="dept" ref="dept"></property> <!-- 使用级联赋值进行部门信息的更新操作 --> <property name="dept.dname" value="使用级联赋值更新了部门信息"></property> </bean>
数组类型依赖注入
若bean对象中存在一个数组,要通过如下配置方式对数组进行依赖注入
<bean id="dept" class="com.qinghe.spring6.iocxml.ditest.Dept">
<property name="dname" value="数组测试部"></property>
</bean>
<bean id="emp" class="com.qinghe.spring6.iocxml.ditest.Emp">
<property name="ename" value="Huying"></property>
<property name="age" value="18"></property>
<property name="dept" ref="dept"></property>
<!-- 对数组类型的成员变量进行赋值 -->
<property name="loves">
<array>
<value>吃饭</value>
<value>睡觉</value>
<value>赖床</value>
</array>
</property>
</bean>
集合类型依赖注入
- List型依赖注入方式:
<!-- 注入emp类型对象-->
<bean id="emp1" class="com.qinghe.spring6.iocxml.ditest.Emp">
<property name="ename" value="huying"></property>
<property name="age" value="18"></property>
</bean>
<!-- 再引入一个emp类型的对象-->
<bean id="emp2" class="com.qinghe.spring6.iocxml.ditest.Emp">
<property name="ename" value="yinghu"></property>
<property name="age" value="81"></property>
</bean>
<!-- 注入一个dept类型的对象-->
<bean id="dept" class="com.qinghe.spring6.iocxml.ditest.Dept">
<!-- 注入普通属性-->
<property name="dname" value="List部"></property>
<!-- 注入list类型的属性(若list保存的是对象,则要用ref标签,若保存的是普通的数据类型,则可以使用value标签)-->
<property name="empList">
<list>
<ref bean="emp1"></ref>
<ref bean="emp2"></ref>
</list>
</property>
</bean>
- Map型依赖注入的方式
<bean id="student" class="com.qinghe.spring6.iocxml.dimap.Student">
<property name="sid" value="1200"></property>
<property name="sname" value="张三"></property>
<!-- 在student对象之中注入map,-->
<property name="teacherMap">
<map>
<!-- 每一个键值对使用entry标签注入-->
<entry>
<key>
<value>000001</value>
</key>
<!-- 对象类型的属性要用ref标签注入-->
<ref bean="teacher1"></ref>
</entry>
<entry>
<key>
<value>000002</value>
</key>
<ref bean="teacher2"></ref>
</entry>
</map>
</property>
</bean>
<!-- 注入两个teacher类型的普通对象用于演示-->
<bean id="teacher1" class="com.qinghe.spring6.iocxml.dimap.Teacher">
<property name="teacherId" value="110"></property>
<property name="teacherName" value="西门"></property>
</bean>
<bean id="teacher2" class="com.qinghe.spring6.iocxml.dimap.Teacher">
<property name="teacherId" value="111"></property>
<property name="teacherName" value="上官"></property>
</bean>
-
引入bean类型的方式进行list、map的注入
在使用bean类型引入的方式进行注入时,需要提前对命名空间进行配置:
在beans标签的属性中添加
xmlns:util="http://www.springframework.org/schema/util"
并在xsi:schemaLocation中添加两行网址:
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
最终效果(bean-diref.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"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<!-- util:list标签在外引入list-->
<util:list id="lessonList">
<ref bean="lesson1"></ref>
<ref bean="lesson2"></ref>
</util:list>
<!-- util:map标签在外注入map-->
<util:map id="teacherMap">
<!-- 注入120021 - teacher1的键值对-->
<entry>
<key>
<value>120021</value>
</key>
<ref bean="teacher1"></ref>
</entry>
<entry>
<key>
<value>120012</value>
</key>
<ref bean="teacher2"></ref>
</entry>
</util:map>
<!-- 创建一个student对象-->
<bean id="student" class="com.qinghe.spring6.iocxml.dimap.Student">
<property name="sid" value="10086"></property>
<property name="sname" value="金毛"></property>
<!-- 注入生成的list和map-->
<property name="lessonList" ref="lessonList"></property>
<property name="teacherMap" ref="teacherMap"></property>
</bean>
<!-- 创建两个teacher对象进行准备-->
<bean id="teacher1" class="com.qinghe.spring6.iocxml.dimap.Teacher">
<property name="teacherId" value="001"></property>
<property name="teacherName" value="上官"></property>
</bean>
<bean id="teacher2" class="com.qinghe.spring6.iocxml.dimap.Teacher">
<property name="teacherId" value="002"></property>
<property name="teacherName" value="西门"></property>
</bean>
<!-- 创建两个Lesson对象进行准备-->
<bean id="lesson1" class="com.qinghe.spring6.iocxml.dimap.Lesson">
<property name="lesson" value="屠龙剑法"></property>
</bean>
<bean id="lesson2" class="com.qinghe.spring6.iocxml.dimap.Lesson">
<property name="lesson" value="走为上计"></property>
</bean>
</beans>
p命名空间注入
在beans的属性配置中添加一行:
xmlns:p="http://www.springframework.org/schema/p"
就引入了p命名空间,可以在bean的配置中快速进行属性以及数据的配置
<bean id="studentp" class="com.qinghe.spring6.iocxml.dimap.Student"
<!-- 使用p空间进行配置,-ref代表引入外部bean标签 -->
p:sid="002" p:sname="Dasima" p:lessonList-ref="lessonList" p:teacherMap-ref="teacherMap">
</bean>
引入外部属性文件
通常在开发过程中,手动进行配置xml文件是相当复杂且难以维护的,故添加引入外部属性文件的方式进行管理
-
添加演示使用依赖:mysql、druid
<dependencies> <!-- mysql数据库依赖--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.32</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.31</version> </dependency> </dependencies>
-
配置jdbc.properties文件
jdbc.user=root
jdbc.password=123456
jdbc.url=jdbc:mysql://loaclhost:3306/spring?serverTimezone=UTC
jdbc.driver=com.mysql.cj.Driver
- 配置命名空间:
添加这几样命名空间的配置
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"
- 添加文件的引入以及属性的配置
<!-- 引入外部属性文件,之后可以在属性添加中进行操作-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!-- 进行依赖注入,使用${}符号可以动态表示-->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${jdbc.url}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="username" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
这样就配置好了数据库的信息,若要修改数据库信息则只需要修改jdbc.properties文件,不再需要进入.xml文件中进行配置
关于Bean
Bean的作用域
Spring中可以配置Bean的scope属性来配置Bean的作用域,singleton代表该对象在IoC容器中始终为单例,在IoC容器初始化时进行创建
prototype代表这个bean在IoC容器中有多个实例,在获取bean时才进行创建。
若是singleton则会在ApplicationContext对象创建时就创建了注入的对象(这体现在日志信息中)(scope默认为singleton)。
而proptotype会在.getBean()时才创建对象。
若是在WebApplicationContext环境下还有另外两种作用域
request指在一次请求中有效、session指在一个会话范围内有效
Bean的生命周期
- 调用无参构造器创建对象
- 为bean设置内部属性(调用set方法)
- bean后置处理器(初始化前)(调用spring文件中的某一个BeanPostProcess接口的一个实现类,该实现类的两个方法中的postProcessBeforeInitialization方法会在作为后置处理器先调用)
- bean对象初始化(会调用指定的初始化方法(该方法可以在bean标签中通过init-method属性设置))(该方法可以在类中设置)
- bean后置处理器(初始化后)(调用spring文件中的某一个BeanPostProcess接口的一个实现类,该实现类的两个方法中的postProcessAfterInitialization方法会在作为后置处理器后调用)
- bean对象创建完成、可以调用了
- bean对象销毁(会调用指定固定销毁方法,(该方法可以在bean标签中通过destroy-method属性设置))(该方法可以在类中设置)
- IoC容器关闭
ClassPathXmlApplicationContext中有.close()方法可以对对象进行销毁
注意,后置处理器中的方法也可以自己定义,要自己建立一个BeanPostProcessor接口的实现类并且重写两个后置处理器的方法,之后在xml文件夹中进行如下配置:
<bean id="起一个名字" class="实现类的全路径"></bean>
后置处理器的自定义:
public class MyBeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("3 bean后置处理器,初始化之前执行");
System.out.println(beanName + "::" + bean);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("5 bean后置处理器,初始化之后执行");
System.out.println(beanName + "::" + bean);
return bean;
}
}
FactoryBean
FactoryBean是spring中的一个接口,实现了这个接口的类要实现两个方法:
@Override
public User getObject() throws Exception {
return new User();
}
@Override
public Class<?> getObjectType() {
return User.class;
}
在xml文件中正常配置这个java类。
其中特殊的是,FactoryBean创建的是getObject()方法中返回的对象,而不是本身类的对象。
xml自动装配
xml文件中的配置不需要自己编写,Bean标签下的autowire属性可以自动在配置文件中找到合适的bean并配置到自己需要的属性中。
autowire有 byType、byName两种方法。
byType会根据自身bean需要的类型在配置文件(IoC容器)中寻找合适的对象。
byName会根据其他bean的id与自己需要的属性的属性名(自己定义的那个名字)来寻找合适的对象并进行注入。
<bean id="userController" class="com.qinghe.spring6.iocxml.auto.controller.UserController" autowire="byType">
</bean>
<bean id="userService" class="com.qinghe.spring6.iocxml.auto.service.UserServiceImpl" autowire="byType">
</bean>
<bean id="userDao" class="com.qinghe.spring6.iocxml.auto.dao.UserDaoImpl">
</bean>