IOC 具体操作之Bean管理【基于xml】
什么是Bean管理?
Bean管理里面其实里面指的是两个操作。第一个就是由Spring给我们创建对象,就是通过bean标签,然后创建对象。第二个就是由Spring进行属性注入。比如一个Javabean中有一个private String userName属性,我们可以通过setter来设置属性。那么现在这个过程也可以交给Spring来做到。这个过程就叫做注入属性。
对象创建
在Spring的xml配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象的创建。
<bean id="sutdent" class="com.zxb.spring.bean.Student"></bean>
这样我们就创建了一个对象(通过无参构造创建)。
在bean标签中有很多属性,这里介绍常用的属性。
*id属性:唯一标识。表示给对象取一个标识或者说别名,然后通过标识或别名可以得到那个对象。
*class属性:类全路径(包类路径)
*name属性:name属性和id的作用是一样的,区别是id属性中不能加特殊符号,但是name中可以加一些特殊符号,比如/。现在基本没什么人用了,当初为struts1而生。
注意!创建对象的时候,默认也是执行无参数的构造方法完成对象的创建。框架中大都采用newInstance()的方式来创建对象,此方法调用的也是无参构造,所以都要把无参构造写上。如果写了有参没写无参就会报错。
属性注入
DI(Dependency Injection):依赖注入,就是注入属性。
第一种注入方式
使用set方法进行注入。需要在类中创建属性和对应的set方法。
<bean id="book" class="com.zxb.spring.Book">
<!--注入属性要写在bean标签里面,相当于对象调方法-->
<!--
使用property完成属性注入
name:类里面属性的名称
value: 向属性注入的值
-->
<property name="bname" value="java从入门到放弃"></property>
<property name="bauthor" value="头发护理秘籍"></property>
</bean>
当使用ApplicationContext context = new ClassPathXmlApplicationContext(“xxx.xml”);来加载配置文件时,这一过程中,会帮我们完成对象创建,以及属性注入。
通过set注入,需要在bean标签内部写上property属性,较为麻烦。还有一种简化xml配置的操作,叫做p名称空间注入。此方式是简化set注入,而不是构造器注入的。
第一步:添加p名称空间在配置文件中。
xmlns:p="http://www.springframework.org/schema/p"
第二步:进行属性注入,在bean标签里面进行操作
<bean id="book" class="com.zxb.spring.bean.Book" p:bname="九阳神功" p:bauthor="无名氏"></bean>
这种通过p名称空间注入底层使用的还是set方法注入。只不过写法简化了。
第二种注入方式
使用有参构造进行属性注入
第一步:创建类,定义属性,创建属性对应的有参构造方法。
第二步:在Spring配置文件中进行配置。
<!--通过有参构造注入属性-->
<bean id="orders" class="com.zxb.spring.bean.Orders">
<constructor-arg name="oName" value="电脑"></constructor-arg>
<constructor-arg name="address" value="China"></constructor-arg>
<!--
<constructor-arg index="0" value="电脑"></constructor-arg>
<constructor-arg index="1" value="China"></constructor-arg>
-->
</bean>
通过index也是可以的,索引表示构造方法的第几个参数。但是一般用name,name更加准确。
注意!
一、
如果类中没有构造方法,只写了属性,在xml文件中,只能写上
<bean id="student1" class="com.zxb.spring.bean.Student"></bean>
,这样会帮我们创建一个对象,属性都是默认值。如果这么定义bean标签,那么不能在bean标签内部写property,因为没有对应的setter。只有写了setter才能帮我们注入属性。
二、
如果类中写了属性,没写无参构造,但是有对应的有参构造方法,那么只写上
<bean id="student1" class="com.zxb.spring.bean.Student"></bean>
会报错,这样写是找的无参构造,而我们没有写无参构造,我们要在bean标签中添加constructor-arg标签来给参数赋值。
总而言之,只写上<bean id="student1" class="com.zxb.spring.bean.Student"></bean>
找的是无参构造,bean标签里面写属性了,那么找的就是对应的setter,如果没有就会报错。
注入其他类型属性
1、字面量
设置使用的固定值就叫做字面量。比如private String oName=“zs”; 这里zs就是字面量,固定死了。也可以通过property标签中的name和value设置固定值,这些固定值也叫做字面量。
(1)null值
<property name="address" >
<null></null>
<null/>单标签双标签都可以
</property>
(2)属性值中包含特殊符号< >等
方式一:使用& l t;或者& g t;
<!--属性值中包含特殊符号-->
<property name="address" value="<杭州>"></property>
方式二:CDATA域
注意 value可以当做标签写在property标签内部。
<property name="address">
<value> <![CDATA[<杭州>]]> </value>
</property>
注入属性—外部bean
举个例子,比如操作中有三层,web层调用service层,service层调用dao层。现在先写两层,一个是service层,一个是dao层。通过service去调用dao,这个过程就叫做引入外部bean。
//原始方式:创建UserDao对象
//UserDao userDao = new UserDaoImpl();
//userDao.update();
(1)创建两个类,service和dao类
(2)在service调用dao里面的方法
(3)在Spring配置文件中进行配置
java代码:
public class UserService {
//创建UserDao类型属性,生成set方法
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void add() {
System.out.println("service add...");
userDao.update();
}
}
xml配置:
<!--1 service和dao对象创建-->
<bean id="userService" class="com.zxb.spring.service.UserService">
<!--2 注入userDao对象
name属性,类里面规范的属性的名字
ref属性,创建userDao对象的bean的标签的id,表示我需要外部bean,ref就是外部bean的id。
-->
<property name="userDao" ref="userDaoImpl"></property>
</bean>
<bean id="userDaoImpl" class="com.zxb.spring.dao.UserDaoImpl"></bean>
**外部bean的注入,在当前类中需要使用到其他类时,我们只需要去创建一个外部类的变量名,随后创建该变量名的set方法。然后在xml中配置当前类和外部类也就是我们需要的其他类的bean标签。在当前类的bean标签中,使用property标签的ref属性,来注入外部bean。ref属性的值就是外部bean的id值。
**
注入属性–内部bean
(1)一对多关系:部门和员工
一个部门里面可以有多个员工,一个员工属于一个部门。
部门是一,员工是多。
(2)在实体类之间表示一对多关系,员工表示所属部门,使用对象类属性进行表示。
package com.zxb.spring.bean;
//员工类
public class Emp {
private String eName;
private String gender;
//员工属于一个部门,使用对象形式表示
private Dept dept;
public void setDept(Dept dept) {
this.dept = dept;
}
public void seteName(String eName) {
this.eName = eName;
}
public void setGender(String gender) {
this.gender = gender;
}
}
package com.zxb.spring.bean;
//部门类
public class Dept {
private String dName;
public void setdName(String dName) {
this.dName = dName;
}
}
(3)在Spring配置文件中进行配置
<!--内部bean-->
<bean id="emp" class="com.zxb.spring.bean.Emp">
<!--先设置两个普通的属性-->
<property name="eName" value="lucy"></property>
<property name="gender" value="女"></property>
<!--设置对象类型属性 可以在外部建一个bean(对象),再通过ref引入
但是这样是外部bean的写法,我们可以在property标签中嵌套一个bean
-->
<property name="dept">
<bean id="dept" class="com.zxb.spring.bean.Dept">
<property name="dName" value="安保部"></property>
</bean>
</property>
</bean>
内部bean就是在property标签里面嵌套一个bean标签。
注入属性–级联赋值(通过外部bean)
还是使用员工和部门的例子,比如想实现向emp中赋值的时候,把dept中的dname也赋值。前面其实也做到了,现在是换一种写法来做级联赋值。你可以同时向你有关联的属性中设值。比如让部门中的属性都设值。就是向多个实体类中设值它的属性值。
写法一:
<!--级联赋值-->
<bean id="emp" class="com.zxb.spring.bean.Emp">
<!--先设置两个普通的属性-->
<property name="eName" value="lucy"></property>
<property name="gender" value="女"></property>
<!--级联赋值,第一种写法-->
<property name="dept" ref="dept"></property>
</bean>
<bean id="dept" class="com.zxb.spring.bean.Dept">
<property name="dName" value="财务部"></property>
</bean>
写法2:
<!--级联赋值-->
<bean id="emp" class="com.zxb.spring.bean.Emp">
<!--先设置两个普通的属性-->
<property name="eName" value="lucy"></property>
<property name="gender" value="女"></property>
<property name="dept" ref="dept"></property>
<!--级联赋值,第二种写法
需要get方法获取属性(对象)
因为都是私有的方法,set用来赋值,
get用来获取,然后获取的是对象类型,然后通过对象.属性,
再通过value进行赋值。
-->
<property name="dept.dName" value="安保部"></property>
</bean>
<bean id="dept" class="com.zxb.spring.bean.Dept"></bean>
注入属性–集合属性
1、注入数组类型属性
2、注入List结婚属性
3、注入Map集合类型
(1)、创建类,定义数组、list、map、set类型属性,并且生成它们的set方法。
package com.zxb.spring.collectiontype;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Stu {
//1、数组类型属性
private String[] courses;
//2、list集合类型属性
private List<String> list;
//3、map集合类型属性
private Map<String, String> map;
//4、set集合类型属性
private Set<String> set;
public void setCourses(String[] courses) {
this.courses = courses;
}
public void setList(List<String> list) {
this.list = list;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setSet(Set<String> set) {
this.set = set;
}
}
(2)在Spring配置文件中进行配置
数组类型注入因为有多个值,property标签的value属性只能赋一个值,所以需要
<property name="courses">
<array>
<value></value>
...
</array>
</property>
或
<property name="courses">
<list>
<value></value>
...
</list>
</property>
<!--集合类型属性注入-->
<bean id="stu" class="com.zxb.spring.collectiontype.Stu">
<!--数组类型属性注入-->
<property name="courses">
<array>
<value>java人</value>
<value>java魂</value>
</array>
</property>
<!--list类型属性注入-->
<property name="list">
<array>
<value>张三</value>
<value>小三</value>
</array>
</property>
<!--map类型属性注入-->
<property name="maps">
<map>
<entry key="JAVA" value="java"></entry>
<entry key="PHP" value="php"></entry>
</map>
</property>
<!--set类型属性注入-->
<property name="sets">
<set>
<value>MySQL</value>
<value>Redis</value>
</set>
</property>
</bean>
4、在集合里面设置对象类型值
使用ref标签中的bean属性,bean里面写的是多个对象的id值。
在xml中如下配置
<property name="courseList"> <!-- 这个属性是List<Course>型的,注入的是Course类型 -->
<list>
<ref bean="course1"></ref>
<ref bean="course2"></ref>
</list>
</property>
<!--在外部创建多个course对象-->
<bean id="course1" class="com.zxb.spring.collectiontype.Course">
<property name="cname" value="Spring框架"></property>
</bean>
<bean id="course2" class="com.zxb.spring.collectiontype.Course">
<property name="cname" value="MyBatis框架"></property>
</bean>
5、把集合注入部分提取出来,作为公共部分
(1)在Spring配置文件中引入名称空间util。
第一步,在bean标签中添加
xmlns:util="http://www.springframework.org/schema/util"
第二步,继续在bean标签中添加如下
http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd
就是将beans修改成util即可。
结果如下
<?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:p="http://www.springframework.org/schema/p"
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 https://www.springframework.org/schema/util/spring-util.xsd">
(2)使用util标签完成list集合属性抽取,使得其他bean也可以使用该属性。
<!-- 1、使用util提取list集合属性 -->
<!--util可以抽取list、map、set等
这个id相当于名字,相当于一个公共类里面取一个公共的名字
-->
<util:list id="bookList">
<!--如果集合里面是基本数据类型,就用value标签
如果集合里面是类类型,那么就用ref标签的bean属性
-->
<value>易筋经</value>
<value>九阴真经</value>
<value>九阳神功</value>
</util:list>
<!-- 2、将提取出来的属性注入对象 -->
<bean id="book" class="com.zxb.spring5.collectiontype.Book">
<!--使用ref标签将提取出来的属性注入,这样就将bookList中的值直接注入到属性中
这样如果有多个bean,就可以通过ref来注入属性。
-->
<property name="list" ref="bookList"></property>
</bean>
注入属性(xml自动装配)
1、什么是自动装配?
之前通过property标签的name和value属性手动设置哪个名称里面有什么值,这个值可以是普通数据类型,也可以是引用类型。通过value属性和ref属性指定你的属性值,这属于手动装配。
那么自动装配就是不需要写property标签,根据你的属性类型或名称Spring会自动帮我们注入属性。根据指定装配规则(属性名称或者属性类型)。Spring自动将匹配的属性值注入。
2、演示自动装配过程
自动装配中主要由两种方式。一种是根据你的属性名称进行装配。另外一种是根据属性类型进行装配。
在bean标签中有一个属性 autowire,通过它来配置自动装配
autowire属性有两个常用值:
byName 根据属性的名称进行注入,注入值的bean的id值要和类的属性名称一样,比如bean的id叫dept,而类里面属性名也叫dept,这样才能完成自动装配,否则就是null。应该说是和setXxx中set的属性一致。
byType根据属性类型注入,byType要注意,相同类型的bean不能定义多个,只能有一个,定义多个就会找不到注入哪一个,就会报错。
byName:
<bean id="emp" class="com.zxb.spring5.autowire.Emp" autowire="byName"></bean>
<bean id="dept" class="com.zxb.spring5.autowire.Dept"></bean>
byType:
<bean id="emp" class="com.zxb.spring5.autowire.Emp" autowire="byType"></bean>
<bean id="dept" class="com.zxb.spring5.autowire.Dept"></bean>
byType错误示范:
Could not autowire. There is more than one bean of ‘Dept’ type. Beans: dept,dept. Properties: ‘dept’
因为是根据类型做匹配,但是现在又两个bean,它就不知道去匹配哪一个bean,无法确定用哪一个bean去注入。这种情况下只能使用byName。
<bean id="emp" class="com.zxb.spring5.autowire.Emp" autowire="byType"></bean>
<bean id="dept" class="com.zxb.spring5.autowire.Dept"></bean>
<!--不能有多个同种类型的bean对象-->
<bean id="dept1" class="com.zxb.spring5.autowire.Dept"></bean>
注入属性(外部属性文件)
首先有这么一个场景。当我们创建对象时,往里面注入属性需要property属性来设置name 和 value值,那么当属性特别多的时候,设置起来就很麻烦。以后如果属性发生改变还需要修改xml文件,这样并不是很方便。所以我们一般将比较固定的值放在一个其他的文件当中,比如放到property文件中,然后引入到xml中。比如操作数据库的时候就是把固定的值放在properties文件当中。
1、直接配置数据库信息
配置德鲁伊连接池
引入德鲁伊连接池jar包
<!--直接配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://3306/test"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
这种方式数据都是写死的。
2、引入外部属性配置数据库信息
(1)创建外部属性文件,properties格式文件,里面写数据库配置信息。
这里为了减少单词冲突,加上pro.的前缀
pro.driverClass=com.mysql.cj.jdbc.Driver
pro.url=jdbc:mysql://3306/test?characterEncoding=utf8&serverTimezone=Asia/Shanghai
pro.username=root
pro.password=root
(2)把外部properties属性文件引入到spring配置文件
引入context名称空间。引入namespace(命名空间)其实就是为了解析那个标签,Spring默认引入的只有四个默认的标签。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context/spring-context.xsd">
(3)在Spring配置文件使用标签引入外部文件
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
因为我的文件放置在src下,所以就是classpath下的jdbc.properties。src下就是类路径下。
通过${}表达式来获取properties中对应key的值。{}中写的就是properties中key的值。
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${pro.driverClass}"/>
<property name="url" value="${pro.url}"></property>
<property name="username" value="${pro.username}"></property>
<property name="password" value="${pro.password}"></property>
</bean>
这种场景经常用于数据库的配置中。