2.1 通过id获取bean
<bean id="person05" class="com.hao.bean.Person" abstract="true">
<property name="lastName" value="张三"></property>
<property name="age" value="18"></property>
<property name="email" value="zhangsan@hao.com"></property>
<property name="gender" value="男"></property>
</bean>
@Test
public void test() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc2.xml");
Person bean = (Person) ioc.getBean("person05");
System.out.println(bean);
}
2.2 通过类型获取bean
从IOC容器中获取bean时,除了通过id值获取,还可以通过bean的类型获取。但如果同一个类型的bean在XML文件中配置了多个,则获取时会抛出异常,所以同一个类型的bean在容器中必须是唯一的。
@Test
public void test() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc2.xml");
Person bean = ioc.getBean("person05",Person.class);
System.out.println(bean);
}
2.3 给bean的属性赋值
2.3.1 赋值的途经
(1)通过bean的setXxx()方法赋值
(2)通过bean的构造器赋值
①按默认顺序赋值
<bean id="book" class="com.atguigu.spring.bean.Book" >
<constructor-arg value= "10010"/>
<constructor-arg value= "Book01"/>
<constructor-arg value= "Author01"/>
<constructor-arg value= "20.2"/>
</bean >
②通过索引值指定参数位置
<bean id="book" class="com.atguigu.spring.bean.Book" >
<constructor-arg value= "10010" index ="0"/>
<constructor-arg value= "Book01" index ="1"/>
<constructor-arg value= "Author01" index ="2"/>
<constructor-arg value= "20.2" index ="3"/>
</bean >
③通过类型不同区分重载的构造器
<bean id="book" class="com.atguigu.spring.bean.Book" >
<constructor-arg value= "10010" index ="0" type="java.lang.Integer" />
<constructor-arg value= "Book01" index ="1" type="java.lang.String" />
<constructor-arg value= "Author01" index ="2" type="java.lang.String" />
<constructor-arg value= "20.2" index ="3" type="java.lang.Double" />
</bean >
(3)给bean的级联属性赋值
ref后面接id:
<bean id="action" class="com.atguigu.spring.ref.Action">
<property name="service" ref="service"/>
<!-- 设置级联属性(了解) -->
<property name="service.dao.dataSource" value="DBCP"/>
</bean>
(4)p名称空间
为了简化XML文件的配置,越来越多的XML文件采用属性而非子元素配置信息。
Spring从2.5版本开始引入了一个新的p命名空间,可以通过
使用p命名空间后,基于XML的配置方式将进一步简化。
<bean id="person06" class="com.hao.bean.Person"
p:lastName="008" p:email="008@hao.com" p:gender="男" p:age="20">
</bean>
2.3.2 可以使用的值
(1)字面量
[1]可以使用字符串表示的值,可以通过value属性或value子节点的方式指定
[2]基本数据类型及其封装类、String等类型都可以采取字面值注入的方式
[3]若字面值中包含特殊字符,可以使用<![CDATA[]]>把字面值包裹起来
(2)null值
<bean class="com.atguigu.spring.bean.Book" id="bookNull" >
<property name= "bookId" value ="2000"/>
<property name= "bookName">
<null/>
</property>
<property name= "author" value ="nullAuthor"/>
<property name= "price" value ="50"/>
</bean >
(3)外部已声明的bean
<bean id="shop" class="com.atguigu.spring.bean.Shop" >
<property name= "book" ref ="book"/>
</bean >
(4)内部bean
当bean实例仅仅给一个特定的属性使用时,可以将其声明为内部bean。内部bean声明直接包含在
内部bean不能被引用在任何其他地方
<bean id="shop2" class="com.atguigu.spring.bean.Shop" >
<property name= "book">
<bean class= "com.atguigu.spring.bean.Book" >
<property name= "bookId" value ="1000"/>
<property name= "bookName" value="innerBook" />
<property name= "author" value="innerAuthor" />
<property name= "price" value ="50"/>
</bean>
</property>
</bean >
2.3.3 集合属性
在Spring中可以通过一组内置的XML标签来配置集合属性,例如:,
(1)数组和List
配置java.util.List类型的属性,需要指定标签,在标签里包含一些元素。这些标签可以通过
数组的定义和List一样,都使用元素。
配置java.util.Set需要使用
<bean id="shop" class="com.atguigu.spring.bean.Shop" >
<property name= "categoryList">
<!-- 以字面量为值的List集合 -->
<list>
<value> 历史</value >
<value> 军事</value >
</list>
</property>
<property name= "bookList">
<!-- 以bean的引用为值的List集合 -->
<list>
<ref bean= "book01"/>
<ref bean= "book02"/>
</list>
</property>
</bean >
(2)Map
Java.util.Map通过
<bean id="cup" class="com.atguigu.spring.bean.Cup">
<property name="bookMap">
<map>
<entry>
<key>
<value>bookKey01</value>
</key>
<ref bean="book01"/>
</entry>
<entry>
<key>
<value>bookKey02</value>
</key>
<ref bean="book02"/>
</entry>
</map>
</property>
</bean>
(3) Properties
使用
<bean class="com.atguigu.spring.bean.DataSource" id="dataSource">
<property name="properties">
<props>
<prop key="userName">root</prop>
<prop key="password">root</prop>
<prop key="url">jdbc:mysql:///test</prop>
<prop key="driverClass">com.mysql.jdbc.Driver</prop>
</props>
</property>
</bean>
(4)集合类型的bean
如果只能将集合对象配置在某个bean内部,则这个集合的配置将不能重用。我们需要将集合bean的配置拿到外面,供其他bean引用。
配置集合类型的bean需要引入util名称空间
<util:list id="bookList">
<ref bean="book01"/>
<ref bean="book02"/>
<ref bean="book03"/>
<ref bean="book04"/>
<ref bean="book05"/>
</util:list>
<util:list id="categoryList">
<value>编程</value>
<value>极客</value>
<value>相声</value>
<value>评书</value>
</util:list>
2.4 通过工厂创建bean
2.4.1 静态工厂
调用静态工厂方法创建bean是将对象创建的过程封装到静态方法中。当客户端需要对象时,只需要简单地调用静态方法,而不用关心创建对象的细节。
声明通过静态方法创建的bean需要在bean的class属性里指定静态工厂类的全类名,同时在factory-method属性里指定工厂方法的名称。最后使用
public class AirPlaneStaticFactory {
public static AirPlane getAirPlane(String jzName) {
System.out.println("静态工厂正在造飞机。。。。");
AirPlane airPlane = new AirPlane();
airPlane.setFdj("太行");
airPlane.setFjsName("lfy");
airPlane.setJzName(jzName);
airPlane.setPersonNum(300);
airPlane.setYc("198.68m");
return airPlane;
}
}
2.4.2 实例工厂
实例工厂方法:将对象的创建过程封装到另外一个对象实例的方法里。当客户端需要请求对象时,只需要简单的调用该实例方法而不需要关心对象的创建细节。
实现方式
①配置工厂类实例的bean
②在factory-method属性里指定该工厂方法的名称
③使用 construtor-arg 元素为工厂方法传递方法参数
public class AirPlaneInstanceFactory {
public AirPlane getAirPlane(String jzName) {
System.out.println("实例工厂正在造飞机。。。。。");
AirPlane airPlane = new AirPlane();
airPlane.setFdj("太行");
airPlane.setFjsName("lfy");
airPlane.setJzName(jzName);
airPlane.setPersonNum(300);
airPlane.setYc("198.68m");
return airPlane;
}
}
2.4.3 FactoryBean
Spring中有两种类型的bean,一种是普通bean,另一种是工厂bean,即FactoryBean。
工厂bean跟普通bean不同,其返回的对象不是指定类的一个实例,其返回的是该工厂bean的getObject方法所返回的对象。
工厂bean必须实现org.springframework.beans.factory.FactoryBean接口。
<bean id="product" class="com.atguigu.spring.bean.ProductFactory">
<property name="productName" value="Mp3" />
</bean>
public class MyFactoryBeanImple implements FactoryBean<Book>{
//从容器获取该工程对象时返回该方法的返回值
@Override
public Book getObject() throws Exception {
System.out.println("MyFactoryBeanImple...造对象");
// TODO Auto-generated method stub
Book book = new Book();
book.setBookName(UUID.randomUUID().toString());
return book;
}
/**
* 返回创建的对象的类型;
* Spring会自动调用这个方法来确认创建的对象是什么类型
*/
@Override
public Class<?> getObjectType() {
// TODO Auto-generated method stub
return Book.class;
}
//是否为单例
@Override
public boolean isSingleton() {
// TODO Auto-generated method stub
return false;
}
}
2.5 bean的高级配置
2.5.1 配置信息的继承
①背景
查看下面两个Employee的配置,其中dept属性是重复的。
<bean id="dept" class="com.atguigu.parent.bean.Department">
<property name="deptId" value="100"/>
<property name="deptName" value="IT"/>
</bean>
<bean id="emp01" class="com.atguigu.parent.bean.Employee">
<property name="empId" value="1001"/>
<property name="empName" value="Tom"/>
<property name="age" value="20"/>
<!-- 重复的属性值 -->
<property name="detp" ref="dept"/>
</bean>
<bean id="emp02" class="com.atguigu.parent.bean.Employee">
<property name="empId" value="1002"/>
<property name="empName" value="Jerry"/>
<property name="age" value="25"/>
<!-- 重复的属性值 -->
<property name="detp" ref="dept"/>
</bean>
②配置信息的继承
<!-- 以emp01作为父bean,继承后可以省略公共属性值的配置 -->
<bean id="emp02" parent="emp01">
<property name="empId" value="1002"/>
<property name="empName" value="Jerry"/>
<property name="age" value="25"/>
</bean>
Spring允许继承bean的配置,被继承的bean称为父bean。继承这个父bean的bean称为子bean
子bean从父bean中继承配置,包括bean的属性配置
子bean也可以覆盖从父bean继承过来的配置
③补充说明
父bean可以作为配置模板,也可以作为bean实例。若只想把父bean作为模板,可以设置
如果一个bean的class属性没有指定,则必须是抽象bean
并不是
也可以忽略父bean的class属性,让子bean指定自己的类,而共享相同的属性配置。但此时abstract必须设为true。
2.5.2 bean之间的依赖
有的时候创建一个bean的时候需要保证另外一个bean也被创建,这时我们称前面的bean对后面的bean有依赖。例如:要求创建Employee对象的时候必须创建Department。这里需要注意的是依赖关系不等于引用关系,Employee即使依赖Department也可以不引用它。
<bean id="emp03" class="com.atguigu.parent.bean.Employee" depends-on="dept">
<property name="empId" value="1003"/>
<property name="empName" value="Kate"/>
<property name="age" value="21"/>
</bean>
2.5.3 bean的作用域
在Spring中,可以在
默认情况下,Spring只为每个在IOC容器里声明的bean创建唯一一个实例,整个IOC容器范围内都能共享该实例:所有后续的getBean()调用和bean引用都将返回这个唯一的bean实例。该作用域被称为singleton,它是所有bean的默认作用域。
当bean的作用域为单例时,Spring会在IOC容器对象创建时就创建bean的对象实例。而当bean的作用域为prototype时,IOC容器在获取bean的实例时创建bean的实例对象。
2.5.4 bean的生命周期
①Spring IOC容器可以管理bean的生命周期,Spring允许在bean生命周期内特定的时间点执行指定的任务。
②Spring IOC容器对bean的生命周期进行管理的过程:
[1]通过构造器或工厂方法创建bean实例
[2]为bean的属性设置值和对其他bean的引用
[3]调用bean的初始化方法
[4]bean可以使用了
[5]当容器关闭时,调用bean的销毁方法
③在配置bean时,通过init-method和destroy-method 属性为bean指定初始化和销毁方法
④bean的后置处理器
[1]bean后置处理器允许在调用初始化方法前后对bean进行额外的处理
[2]bean后置处理器对IOC容器里的所有bean实例逐一处理,而非单一实例。其典型应用是:检查bean属性的正确性或根据特定的标准更改bean的属性。
[3] bean后置处理器时需要实现接口:org.springframework.beans.factory.config.BeanPostProcessor。在初始化方法被调用前后,Spring将把每个bean实例分别传递给上述接口的以下两个方法:
●postProcessBeforeInitialization(Object, String)
●postProcessAfterInitialization(Object, String)
⑤添加bean后置处理器后bean的生命周期
[1]通过构造器或工厂方法创建bean实例
[2]为bean的属性设置值和对其他bean的引用
[3]将bean实例传递给bean后置处理器的postProcessBeforeInitialization()方法
[4]调用bean的初始化方法
[5]将bean实例传递给bean后置处理器的postProcessAfterInitialization()方法
[6]bean可以使用了
[7]当容器关闭时调用bean的销毁方法
public class MyBeanPostProcessor implements BeanPostProcessor{
/**
* 后置处理器:
* (容器启动)构造器-----后置处理器before..----初始化方法
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub
System.out.println("【" + beanName + "】" + "bean将要调用初始化方法。。。");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// 初始化后返回的bean;返回的是什么,容器就保存什么
System.out.println("【" + beanName + "】bean初始化方法调用完了");
return bean;
}
}
2.5.5 引用外部属性文件
当bean的配置信息逐渐增多时,查找和修改一些bean的配置信息就变得愈加困难。这时可以将一部分信息提取到bean配置文件的外部,以properties格式的属性文件保存起来,同时在bean的配置文件中引用properties属性文件中的内容,从而实现一部分属性值在发生变化时仅修改properties属性文件即可。这种技术多用于连接数据库的基本信息的配置。
①直接配置
<!-- 直接配置 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="root"/>
<property name="password" value="root"/>
<property name="jdbcUrl" value="jdbc:mysql:///test"/>
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
</bean>
②创建properties属性文件
prop.userName=root
prop.password=root
prop.url=jdbc:mysql:///test
prop.driverClass=com.mysql.jdbc.Driver
③引入context名称空间
④指定properties属性文件的位置
<!-- 指定properties属性文件的位置 -->
<!-- classpath:xxx 表示属性文件位于类路径下 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
⑤从properties属性文件中引入属性值
<!-- 从properties属性文件中引入属性值 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${prop.userName}"/>
<property name="password" value="${prop.password}"/>
<property name="jdbcUrl" value="${prop.url}"/>
<property name="driverClass" value="${prop.driverClass}"/>
</bean>
2.5.6 自动装配
①自动装配的概念
[1]手动装配:以value或ref的方式明确指定属性值都是手动装配。
[2]自动装配:根据指定的装配规则,不需要明确指定,Spring自动将匹配的属性值注入bean中。
②装配模式
[1]根据类型自动装配:将类型匹配的bean作为属性注入到另一个bean中。若IOC容器中有多个与目标bean类型一致的bean,Spring将无法判定哪个bean最合适该属性,所以不能执行自动装配
[2]根据名称自动装配:必须将目标bean的名称和属性名设置的完全相同
[3]通过构造器自动装配:当bean中存在多个构造器时,此种自动装配方式将会很复杂。不推荐使用。
③选用建议
相对于使用注解的方式实现的自动装配,在XML文档中进行的自动装配略显笨拙,在项目中更多的使用注解的方式实现。
2.6 SpEL
2.6.1 简介
Spring Expression Language,Spring表达式语言,简称SpEL。支持运行时查询并可以操作对象图。
和JSP页面上的EL表达式、Struts2中用到的OGNL表达式一样,SpEL根据JavaBean风格的getXxx()、setXxx()方法定义的属性访问对象图,完全符合我们熟悉的操作习惯。
2.6.2 基本语法
SpEL使用#{…}作为定界符,所有在大框号中的字符都将被认为是SpEL表达式。
2.6.3 使用字面量
●整数:
●小数:
●科学计数法:
●String类型的字面量可以使用单引号或者双引号作为字符串的定界符号
●Boolean:
2.6.4 引用其他bean
<bean id="emp04" class="com.atguigu.parent.bean.Employee">
<property name="empId" value="1003"/>
<property name="empName" value="Kate"/>
<property name="age" value="21"/>
<property name="detp" value="#{dept}"/>
</bean>
2.6.5 引用其他bean的属性值作为自己某个属性的值
<bean id="emp05" class="com.atguigu.parent.bean.Employee">
<property name="empId" value="1003"/>
<property name="empName" value="Kate"/>
<property name="age" value="21"/>
<property name="deptName" value="#{dept.deptName}"/>
</bean>
2.6.6 调用非静态方法
<!-- 创建一个对象,在SpEL表达式中调用这个对象的方法 -->
<bean id="salaryGenerator" class="com.atguigu.spel.bean.SalaryGenerator"/>
<bean id="employee" class="com.atguigu.spel.bean.Employee">
<!-- 通过对象方法的返回值为属性赋值 -->
<property name="salayOfYear" value="#{salaryGenerator.getSalaryOfYear(5000)}"/>
</bean>
2.6.7 调用静态方法
<bean id="employee" class="com.atguigu.spel.bean.Employee">
<!-- 在SpEL表达式中调用类的静态方法 -->
<property name="circle" value="#{T(java.lang.Math).PI*20}"/>
</bean>
2.6.8 运算符
①算术运算符:+、-、*、/、%、^
②字符串连接:+
③比较运算符:<、>、==、<=、>=、lt、gt、eq、le、ge
④逻辑运算符:and, or, not, |
⑤三目运算符:判断条件?判断结果为true时的取值:判断结果为false时的取值
⑥正则表达式:matches
2.7 通过注解配置bean
2.7.1 概述
相对于XML方式而言,通过注解的方式配置bean更加简洁和优雅,而且和MVC组件化开发的理念十分契合,是开发中常用的使用方式。
2.7.2 使用注解标识组件
①普通组件:@Component
标识一个受Spring IOC容器管理的组件
②持久化层组件:@Respository
标识一个受Spring IOC容器管理的持久化层组件(dao)
③业务逻辑层组件:@Service
标识一个受Spring IOC容器管理的业务逻辑层组件
④表述层控制器组件:@Controller
标识一个受Spring IOC容器管理的表述层控制器组件
⑤组件命名规则
[1]默认情况:使用组件的简单类名首字母小写后得到的字符串作为bean的id
[2]使用组件注解的value属性指定bean的id
注意:事实上Spring并没有能力识别一个组件到底是不是它所标记的类型,即使将@Respository注解用在一个表述层控制器组件上面也不会产生任何错误,所以@Respository、@Service、@Controller这几个注解仅仅是为了让开发人员自己明确当前的组件扮演的角色。
2.7.3 导包
2.7.4 扫描组件
组件被上述注解标识后还需要通过Spring进行扫描才能够侦测到。
①指定被扫描的package
<context:component-scan base-package="com.atguigu.component"/>
②详细说明
[1]base-package属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包及其子包中的所有类。
[2]当需要扫描多个包时可以使用逗号分隔。
[3]如果仅希望扫描特定的类而非基包下的所有类,可使用resource-pattern属性过滤特定的类,示例:
<context:component-scan
base-package="com.atguigu.component"
resource-pattern="autowire/*.class"/>
[4]包含与排除
●
注意:通常需要与use-default-filters属性配合使用才能够达到“仅包含某些组件”这样的效果。即:通过将use-default-filters属性设置为false,禁用默认过滤器,然后扫描的就只是include-filter中的规则指定的组件了。
●
●component-scan下可以拥有若干个include-filter和exclude-filter子节点
●过滤表达式
③JAR包
必须在原有JAR包组合的基础上再导入一个:spring-aop-4.0.0.RELEASE.jar
2.7.5 组件装配
①需求
Controller组件中往往需要用到Service组件的实例,Service组件中往往需要用到Repository组件的实例。Spring可以通过注解的方式帮我们实现属性的装配。
②实现依据
在指定要扫描的包时,
③@Autowired注解
[1]根据类型实现自动装配。
[2]构造器、普通字段(即使是非public)、一切具有参数的方法都可以应用@Autowired注解
[3]默认情况下,所有使用@Autowired注解的属性都需要被设置。当Spring找不到匹配的bean装配属性时,会抛出异常。
[4]若某一属性允许不被设置,可以设置@Autowired注解的required属性为 false
[5]默认情况下,当IOC容器里存在多个类型兼容的bean时,Spring会尝试匹配bean的id值是否与变量名相同,如果相同则进行装配。如果bean的id值不相同,通过类型的自动装配将无法工作。此时可以在@Qualifier注解里提供bean的名称。Spring甚至允许在方法的形参上标注@Qualifiter注解以指定注入bean的名称。
[6]@Autowired注解也可以应用在数组类型的属性上,此时Spring将会把所有匹配的bean进行自动装配。
[7]@Autowired注解也可以应用在集合属性上,此时Spring读取该集合的类型信息,然后自动装配所有与之兼容的bean。
[8]@Autowired注解用在java.util.Map上时,若该Map的键值为String,那么 Spring将自动装配与值类型兼容的bean作为值,并以bean的id值作为键。
④@Resource
@Resource注解要求提供一个bean名称的属性,若该属性为空,则自动采用标注处的变量或方法名作为bean的名称。
⑤@Inject
@Inject和@Autowired注解一样也是按类型注入匹配的bean,但没有reqired属性。
2.8 泛型依赖注入
2.8.1 简介
Spring 4.x中可以为子类注入子类对应的泛型类型的成员变量的引用。
2.8.2 实现
[1]组件基类
public class BaseRepository<T> {
public void save() {
System.out.println("Saved by BaseRepository");
}
}
public class BaseService<T> {
@Autowired
private BaseRepository<T> repository;
public void add() {
repository.save();
}
}
[2]组件实体类
UserRepository
@Repository
public class UserRepository extends BaseRepository<User>{
public void save() {
System.out.println("Saved by UserRepository");
}
}
UserService
@Service
public class UserService extends BaseService<User>{
}
[3]模型实体类
public class User {
}
[4]测试
ApplicationContext ioc = new ClassPathXmlApplicationContext("di.xml");
UserService us = (UserService) ioc.getBean("userService");
us.add();
2.9 整合多个配置文件
1、Spring允许通过
2、import元素的resource属性支持Spring的标准的路径资源
在spring的xml文件中:
<import resource="spring.xml"/>