Spring共四天
大纲
第一天:Spring矿建的概述以及Spring中基于xml的IOC配置
第二天:Spring中基于注解的IOC和ioc的案例
第三天:Spring中的aop和基于XML以及注解的AOP配置
第四天:Spring中的JdbcTemplate以及Spring事务控制
第一天:今日内容
1.Spring的概述
Spring是什么
Spring的两大核心
Spring的发展历程和优势
Spring体系结构
2.程序的耦合以及解耦
曾经案例中的问题
工厂模式解耦
3. IOC概念和Spring中的IOC
Spring中基于XML的IOC环境搭建
4. 依赖注入(Dependency Injection DI)
5. 作业:
1. Spring概述
1.1 Spring是什么
Spring 是分层的JavaSE/EE应用full-stack轻量级开源框架,以IOC(Inverse Of Control:反转控制)
和AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层SpringMVC和持久层Spring
JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成
为使用最多的Java EE 企业应用开源框架。
我们目前学习的只是Spring体系中的冰山一角:SpringFramework部分
1.2 Spring的发展历程
1.3 Spring的优势
方便解耦,简化开发
AOP编程的支持
声明式事务的支持
方便程序的测试
方便集成各种优秀框架
降低JavaEE API的使用难度
Java源码是经典学习范例
1.4 Spring的体系结构
2. 程序的耦合以及解耦
2.1 理解耦合
1.分析这段熟悉的代码
```
public class JdbcDemo1 {
public static void main(String[] args) throws Exception{
//1.注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//2.获取连接
Connection coon = DriverManager.getConnection("jdbc:mysql://localhost:3306/txl_spring", "root", "root");
//3.获取操作数据库的预处理对象
PreparedStatement ps = coon.prepareStatement("select * from account");
//4.执行SQL:得到结果集
ResultSet rs = ps.executeQuery();
//5.遍历结果集
while(rs.next()){
System.out.println(rs.getString("name"));
}
//6.释放资源
rs.close();
ps.close();
coon.close();
}
}
```
2.我在pom.xml中导入了mysql的驱动,上面的程序是可以正常运行的。
```
<!--添加mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
```
显然,这个程序的运行,没有这个com.mysql.jdbc是不可能的,这也是我们长久的一个认识。
3. 当我把pox中的mysql依赖注释掉:编译期的异常
提示一个编译期的错误: om.mysql.jdbc程序包不存在
这是一个编译期的异常,不是运行期异常。
我们在写代码时,可能会遇到很多在编译器就给我们报错的情况。这种编译期的报错,说明
我们这个类在运行时,如果没有这个依赖类是不能正常编译的。
我们可以理解为程序的耦合。
2.2 耦合:程序间的依赖关系
1.包括:
类之间的依赖:我们是无法避免的,只能尽量降低。
方法间的依赖
2.解耦:降低程序间的依赖关系
如果一个类在编译时就会非常依赖某个类/jar包,那么这个类的独立性就显得非常的差。
3.实际开发中:
编译期不依赖,运行时才依赖。
4.改进上面的代码:
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
new com.mysql.jdbc.Driver() 代表一个类。
Class.forName("com.mysql.jdbc.Driver");
com.mysql.jdbc.Driver : 是一个字符串。
这种方式的驱动加载:不再依赖某个具体的驱动类。
上面的程序,就会更加的独立。
由于没有导入依赖,虽然还是无法运行。但是这是一个运行时异常,不是一个编译期错误。
5.我可以得到一些其他的知识:
* 加载驱动方式1: DriverManager.registerDriver(new com.mysql.jdbc.Driver());
依赖:
```
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
```
* 加载驱动方式2: Class.forName("com.mysql.jdbc.Driver")
依赖:<scope>runtime</scope>
```
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
<scope>runtime</scope>
</dependency>
```
* 因为第二种加载驱动方式在编译器不依赖jar包,所以可以指定依赖的范围是运行时
6. 解耦的思路:
第一步:使用反射来创建对象,而避免使用new关键字。
一个依赖一个具体的驱动类
一个只是依赖一个字符串
会导致一个小问题:
这里只能指定mysql的依赖,如果将来想要连接oracle...等数据库,这个驱动要修改。
第二步:通过读取配置文件来获取要创建的对象全限定类型,然后在使用反射来创建对象。
2.3 工厂模式解耦
在实际开发中我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,
让一个类中方法通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用过程中,直接
拿过来用就好了。
那么,这个读取配置文件,创建和获取三层对象的类就是工厂。
1.小扩展:
Bean:在计算机英语中,有可重用(在很多地方都可能被用到)组件的含义。
JavaBean:用java语言编写的可重用组件,不等于实体类,实体类只是可重用组件的一部分。
Bean工厂:它就是创建我们的service和dao对象的,需要两个东西。
1.需要一个配置文件来配置我们的service和dao。
配置的内容:全限定类名,取出全限定类名的标志
标志 = 全限定类名(key = value)
2.通过读取配置文件中配置的内容,反射创建对象。
我的配置文件可以是xml也可以是properties
2. BeanFactory类:用来生产service层对象和dao层对象
* 注意:获取properties文件流对象时的一些细节。
1.不建议用new FileInputStream()的方式
虽然在此处我们可以写下这样一个相对路径:src/main/resources/bean.properties
但是如果是web项目,那么该项目已部署,部署的目录根本不会有src这个目录,所以相对
路径也不能用。
磁盘绝对路径也不能用,虽然在当前工程下可以使用,但是不能保证以后的工程都有
C/D/E/F盘吗?所以我们采用一种适用性更广的方式
```
InputStream in = new FileInputStream("src/main/resources/bean.properties");
```
2.采用反射的方式获取配置文件流
创建在resources目录下的文件,最终会成为classes根路径下的文件,这时候可以不用写任何包名。
```
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
```
3.BeanFactory代码
1. properties配置文件
```
accountService=com.itheima.service.impl.IAccountServiceImpl
accountDao=com.itheima.dao.impl.IAccountDaoImpl
```
2.BeanFactory代码
```
public class BeanFactory {
//定义一个Properties对象
public static Properties props;
//使用静态代码块为Properties对象赋值
static {
try {
//实例化对象
props = new Properties();
//获取properties文件的流对象
//InputStream in = new FileInputStream("src/main/resources/bean.properties");
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
props.load(in);
} catch (IOException e) {
throw new ExceptionInInitializerError("初始化properties失败!");
}
}
/**
* 根据bean名称,获取bean对象
* @param beanName
* @return
*/
public static Object getBean(String beanName){
Object bean = null;
try{
String beanPath = props.getProperty(beanName);
bean = Class.forName(beanPath).newInstance();
}catch (Exception e){
e.printStackTrace();
}
return bean;
}
}
```
4. 小结:
获取个层对象的方式变了,不再是相互依赖实例化:new
```
IAccountService as = new IAccountServiceImpl();
private IAccountDao accountDao = new IAccountDaoImpl();
```
而是通过一个统一工厂来生产。
```
IAccountService as = (IAccountService)BeanFactory.getBean("accountService");
IAccountDao accountDao = (IAccountDao)BeanFactory.getBean("accountDao");
```
这样各层类之间的依赖关系就降低了很多,不会出现因为少了某个类就报编译错误(比如我们将Dao类删掉)
运行时才会出现错误。通过工厂模式实现了一定程度上的解耦。
5. 单例和多例
每次都实例化了新的对象,那么就会重新实例化每个对象中的属性。
5.1.工厂模式创建的对象是:多例。
```
/**
* 模拟一个表现层:用于调用业务层
*/
public class Client {
public static void main(String[] args) {
//IAccountService as = new IAccountServiceImpl();
for (int i = 0; i < 5; i++) {
IAccountService as = (IAccountService)BeanFactory.getBean("accountService");
System.out.println(as);
}
}
}
```
我在这个模拟的表现层中用工厂模式创建了5个业务层对象IAccountService,打印发现
这5个对象是不同的对象。
5.2.单例模式:会出现线程问题
对象只被创建一次,从而类中的成员也就只会初始化一次。
当你想操作类中的属性时,只有第一次是最初的属性值。后面再操作都会是改变后的值。
所以说,在多个线程访问或多个对象访问时,由于对象的实例只有一个。那你操作类成员的话,
单例对象会有限制。
servlet就是单例:所以不建议在servlet中写类成员。
5.3. 避免单例模式造成的线程安全问题
多例对象执行效率不高,单例对象有线程问题。但是其实我们在代码中可以避免出现方法中有
类的属性,和对类中属性进行操作的方法。
一般在service和dao中也不存在这些成员和方法,从而进一步分析。我们其实是没有线程问题的。
我们一般都是在方法中定义变量,而方法每次都会初始化,所以保证每次得到的都是初始值。
那么从这一点分析:我们不需要多例模式创建对象。
分析BeanFactory代码:
bean = Class.forName(beanPath).newInstance();
每次都会调用默认构造函数创建对象,所以每次都是一个新的对象。
6. 我们对代码继续进行改善:单例模式
我们只用一次 bean = Class.forName(beanPath).newInstance();创建出来后,将其马上保存起来。
用什么存:容器
定义一个Map,用于存储我们要创建的对象,我们把它称之为容器
key:是properties中的key,value:是创建出来的对象
private static Map<String,Object> beans;
```
public class BeanFactory {
//定义一个Properties对象
public static Properties props;
//定义一个Map,用于存储我们要创建的对象,我们把它称之为容器
private static Map<String,Object> beans;
//使用静态代码块为Properties对象赋值
static {
try {
//实例化对象
props = new Properties();
//获取properties文件的流对象
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
props.load(in);
//实例化容器
beans = new HashMap<String, Object>();
//取出配置文件中所有的key
Enumeration keys = props.keys();
//遍历枚举
while(keys.hasMoreElements()){
//取出每个key
String key = keys.nextElement().toString();
//根据key获取value
String beanPath = props.getProperty(key);
//反射创建对象
Object value = Class.forName(beanPath).newInstance();
//把key和value存入容器之中
beans.put(key, value);
}
} catch (IOException e) {
throw new ExceptionInInitializerError("初始化properties失败!");
} catch (Exception e) {
e.printStackTrace();
}
}
//根据bean名称,获取bean对象:单例模式
public static Object getBean(String beanName){
return beans.get(beanName);
}
}
```
3 使用Spring的IOC解决程序耦合
IOC 控制反转:将创建Bean对象的权利交给了BeanFactory。
概念:Inversion of Control。
把创建对象的权利交给框架,是框架的重要特征,并非面向对象编程的专用术语。
它包括依赖注入(Dependendcy Injection DI)和依赖查找(Dependency Lookup)。
作用:
削减计算机程序的耦合,降低我们代码中的依赖关系。
3.1 案例的前期准备
这次我们使用的案例是,账户的业务层和持久层的依赖关系解决。在开始spring的配置之前,
我们要先准备一下环境。由于我们是使用spring解决依赖关系,并不是真正的要增删该查操作,
所以此时我们没有必要写实体类,而且用的是java工程。
1. 导入依赖:
2. 编写配置文件bean.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">
<!--把对象的创建交给spring来管理-->
<bean id="accountService" class="com.itheima.service.impl.IAccountServiceImpl"></bean>
<bean id="accountDao" class="com.itheima.dao.impl.IAccountDaoImpl"></bean>
</beans>
```
这样核心容器就创建好了。
3. 测试代码
```
public static void main(String[] args) {
//1.获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2.根据id获取Bean对象:要强转
IAccountService as = (IAccountService)ac.getBean("accountService");
//2.1这种获取方式不用强转
IAccountDao aDao = ac.getBean("accountDao", IAccountDao.class);
System.out.println(as);
System.out.println(aDao);
}
}
```
3.2 ApplicationContext的三个常用实现类
ClassPathXmlApplicationContext:它可以加载类路径下的配置文件,要求配置文件必须在类路径下
FileSystemXmlApplicationContext:它可以加载磁盘任意路径下的配置文件(必须有访问权限)
AnnotationConfigApplicationContext:它是用于读取注解创建程序的
```
ApplicationContext ac = new FileSystemXmlApplicationContext("
E:\\Idea_Projects\\day0607_spring(1.3)\\src\\main\\resources\\bean.xml");
```
```
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
```
3.3 核心容器的两个接口引发的问题:
1. ApplicationContext:单例模式适用
* 它在创建核心容器是,创建对象采用的策略是立即加载方式。也就是说,只要一读取配置文件
bean.xml,马上就创建配置文件中的配置的对象:
```
<bean id="accountService" class="com.itheima.service.impl.IAccountServiceImpl"></bean>
<bean id="accountDao" class="com.itheima.dao.impl.IAccountDaoImpl"></bean>
```
* 我们来分析下这个标签:
<bean id="accountService" class="com.itheima.service.impl.IAccountServiceImpl"></bean>
这句话的意思就是:
我要用id="accountService" 来取创建的IAccountServiceImpl对象
所以走完这一步:ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");加载
bean.xml配置文件之后,spring就会根据反射,调用IAccountServiceImpl中的默认构造犯法,创建出相
应的对象。
2. BeanFactory:多例模式适用
BeanFactory是一个顶层的接口,显然功能不是那么完善。
它在构建核心容器时,创建对象采取的是延迟加载的方式。也就是说,什么时候根据id获取对象了,
什么时候才真正的创建对象。
3.4 Spring对bean的管理细节
3.4.1 创建bean的三种方式
1. 第一种方式:使用默认构造函数创建
<bean id="accountService" class="com.itheima.service.impl.IAccountServiceImpl">
在spring的配置文件中使用bean标签,仅仅以id和class属性。采用的就是默认构造函数创建bean对象。
如果此类中没有默认构造方法,则对象无法创建。
Failed to instantiate [com.itheima.service.impl.IAccountServiceImpl]: No default constructor found;
也可以说:这个配置就是在找IAccountServiceImpl的默认构造方法。
2. 第二种方式:使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)
* 普通工厂:普通方法
```
public class InstanceFactory {
public IAccountService getAccountService(){
return new IAccountServiceImpl();
}
}
```
* 看到这个类,我们可能会出现这样的困惑:
getAccountService()方法中也使用了new,这不也是耦合了吗?
我们要知道可能存在的这种情况:这是一个模拟的工厂类,该类可能是存在jar包中的,他是一个class文件我们
无法通过修改其源码的方式来提供默认构造函数。
所以既然是jar包中的类,我们没必要着眼与方法中的具体实现,是要我们不new这个类即可。
我们要的是。将其中的方法的返回值return new IAccountServiceImpl()放到我们的spring容器中。
```
<bean id="instanceFactory" class="com.itheima.factory.InstanceFactory"></bean>
<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>
```
* 分析一下这两句配置:
首先,public IAccountService getAccountService()这是一个普通的方法,需要对象调用。
1.先指定这个工厂的默认构造函数:得到instanceFactory对象
<bean id="instanceFactory" class="com.itheima.factory.InstanceFactory"></bean>
2.怎么得到这个方法呢?
bean id="accountService" : 指定要得到的对象的id——IAccountService
factory-bean="instanceFactory" : 指定那个工厂bean
factory-method="getAccountService" : 指定这个工厂类中我们要用到的方法
3. 第三种方式:使用静态工厂中的静态方法(使用某个类中的静态方法创建对象,并存入spring容器)
* 静态工厂:静态方法
```
public class StaticFactory {
public static IAccountService getAccountService(){
return new IAccountServiceImpl();
}
}
```
* 我们要意识到,需要调用的是静态方法,所以不用实例化那个工厂类。
即我们直接指定对应的静态方法即可。
```
<bean id="accountService" class="com.itheima.factory.StaticFactory"
factory-method="getAccountService"></bean>
```
* 分析一下这句配置
我要用id="accountService"来取StaticFactory中的getAccountService得到的结果。
而且我们没有创建StaticFactory对象,所以getAccountService是一个静态方法。
3.4.2 bean对象的作用范围
我们知道ApplicationContext是一个很智能的接口,它可以跟据我们对象的多例还是单例来采用
延迟加载还是立即加载。
* 那么我们怎么判断要创建的对象是单例还是多例呢?
* bean标签的scope属性:
作用:用于指定bean的作用范围
取值:
singleton:单例(默认值)
prototype:多例
request:作用于web应用的请求范围
session:作用于web应用的会话范围
global-session:作用于集群环境的会话范围(全局会话范围,当不是集群环境是就是session)
* 相应的配置:
```
<bean id="accountService" class="com.itheima.service.impl.IAccountServiceImpl" scope="singleton"></bean>
```
* 扩展:global-session全局session
由于实际情况下后台的服务器是多个的。我们需要多台服务器都能共享一些数据,那么就将这些数据
放到global中,这是一个全局会话,这几台服务器都能得到里面的信息。
3.4.3 bean对象的生命周期
1. 单例对象
出生:当容器创建时,对象出生。
活着:只要容器还在,对象一直活着
死亡:容器销毁,对象消亡
```
<bean id="accountService" class="com.itheima.service.impl.IAccountServiceImpl"
scope="prototype" init-method="init" destroy-method="destroy"></bean>
```
init-method:指定类中的初始化方法名称
destroy-method:指定类中的销毁方法名称
可以在destroy方法中写:销毁容器的这一步
* 怎么理解当容器创建时,对象出生这句话?
怎么理解这个容器,容器是一个map形式东西。我暂时的理解是这样的:
当解析完bean.xml中的<bean/>标签时,就会创建出来一个map容器,同时将calss=""
对应的对象创建出来放到map的value中,而key是id="accountService"
2. 手动关闭容器
main是一切应用程序的入口,当main方法结束之后,当前应用中线程占据的内存全部释放。
当然也包含创建的容器,但是此时并没有调用销毁方法destroy,就已经把内存释放了。所以说还没
有来得及调用销毁,容器就已经消失了。
要是想要调用那么销毁方法,怎么做呢?———— 手动关闭容器。
* 注意:
close()方法存在于ApplicationContext的子类中,ApplicationContext本类没有该方法
前面applicationContext对象我们是通过多态的方式实例化的。
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
此时applicationContext被看成了父类的对象,那么调用的方法编译时看的是父类的方法。如果
父类有这个方法,那么可以编译通过,执行的是子类的方法。
close()方法存在于ApplicationContext的子类ClassPathXmlApplicationContext中,所以我们要
想用.close()方法,实例化ClassPathXmlApplicationContext,就不要用多态。
ClassPathXmlApplicationContext cac = new ClassPathXmlApplicationContext("bean.xml");
classApplicationContext.close();
3. 多例对象
出生:当使用对象时,spring框架才为我们创建对象
活着:对象在使用过程中一直活着。
死亡:spring虽然很强大,但是它仍然无法知道我们这个对象要用到什么时候,什么时候销毁。
所以它不会轻易的将这个对象销毁的。
* 当对象长时间不用且没有别的对象引用时,由java的垃圾回收器回收
4. 依赖注入(Dependency Injection DI)
4.1 依赖注入的概述
* 依赖注入::Dependency Injection。它是spring框架的具体实现
我们要清楚这样一个情况,我们在编写程序时,通过控制反转,把对象的创建交给了spring。
但是代码中不可能出现没有依赖的情况。IOC解耦只是降低他们的依赖关系,但不会消除。例如:
我们的业务层仍会调用持久层的办法。
那这种业务层和持久层的依赖关系,在使用spring之后,就让spring来维护了。
依赖关系的维护,就称为依赖注入。
既然有依赖:那么涉及到的调用方法,对象,赋值等等操作都由我来(Spring)帮你管理。
* 依赖注入的方式:
1. 使用构造方法提供
2. 使用set方法提供
3. 使用注解提供
* 能注入的数据:
基本数据类型和String
其他的bean类型(在配置文件中/注解 配置过的bean,即在spring容器中体现过的bean)
复杂类型/集合类型
4.2 使用构造函数注入
背景:
假如两个类产生了依赖关系,而且是不常变化的成员变量。比如:A类中要用到: new B(a,b,c)
来实例化B,我们可以通过构造函数来给成员变量赋值。
注意/思想:
赋值的操作不是我们自己做的,而是通过配置的方式,让spring框架来为我们注入。
1. 构造方法
```
public IAccountServiceImpl(String name, Integer age, Date birthday){
this.name = name;
this.age = age;
this.birthday = birthday;
}
```
分析:三个成员变量分别是 String Integer 其他的Bean类型(Date)
2. Spring配置: bean.xml
使用的标签: constructor-arg
标签出现的位置:bean标签内部
标签中的属性:
type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
如果有两个String属性的参数,type就无法指定哪个第一个哪个放第二个
index:用于指定要注入的参数在构造函数中的索引位置。参数索引从0开始
name:用于指定,给构造函数中指定名称的参数赋值(常用)
* 以上三个用于指定给构造函数中哪个参数赋值
value:用于提供基本类型和String类型的数据的值
ref:用于指定其他的bean类型数据,它指的就是在spring的IOC核心容器中出现过的bean对象。
就是在xml或注解中配置过这个bean,那都可以通过ref来引用。
3. 配置文件bean.xml代码
```
<bean id="accountService" class="com.itheima.service.impl.IAccountServiceImpl">
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="name" value="test"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
<bean id="now" class="java.util.Date"></bean>
```
* 分析一下:
<bean id="accountService" class="com.itheima.service.impl.IAccountServiceImpl">
指定了要注入的类的构造方法
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="name" value="test"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>
指定了其中的参数名和值
<constructor-arg name="birthday" ref="now"></constructor-arg>
<bean id="now" class="java.util.Date"></bean>
注意第三个参数要注入的是一个Date数据类型,我们可以在bean.xml中先配置一个Date对象。
然后在用ref引进去。
class="java.util.Date":通过反射创建一个对象,再用id="now"获取这个对象,ref="now"引入。
4. 优势和弊端
优势:
当我们某个类中的数据就想用某些值来提供时,就用构造函数来注入更直接,不会让人忽略这一要求。
弊端:
改变了这个bean对象的实例化方式,是我们在创建时,必须注入数据,否则对象无法创建成功。
我们在创建时,如果用不到这些数据时也必须提供。
5. 小结:除非必要,一般不用。
4.3 使用set函数注入 ----- 更灵活,更常用的方式
1. 需要注入的Bean类
```
private String name;
private Integer age;
private Date birthday;
public void setQQName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
```
2. bean.xml配置:
使用的标签:property
出现的位置:bean标签的位置
标签的属性:
name:用于指定,给构造函数中指定名称的参数赋值(常用)
value:用于提供基本类型和String类型的数据的值
ref:用于指定其他的bean类型数据,它指的就是在spring的IOC核心容器中出现过的bean对象。
就是在xml或注解中配置过这个bean,那都可以通过ref来引用。
3. bean.xml代码
```
<bean id="accountService2" class="com.itheima.service.impl.IAccountServiceImpl2">
<property name="age" value="21"></property>
<property name="birthday" ref="now2"></property>
<property name="QQName" value="撕撕撕"></property>
</bean>
<bean id="now2" class="java.util.Date"></bean>
```
4. 优势:
很灵活,没有明确的限制,不影响使用默认构造方法
4.4. 复杂类型的注入
1.要注入的Bean类:成员变量都是数组,集合,map等。
```
private String[] myStr;
private List<String> myList;
private Set<String> mySet;
private Map<String, String> myMap;
private Properties myProps;
public void setMyStr(String[] myStr) {
this.myStr = myStr;
}
public void setMyList(List<String> myList) {
this.myList = myList;
}
public void setMySet(Set<String> mySet) {
this.mySet = mySet;
}
public void setMyMap(Map<String, String> myMap) {
this.myMap = myMap;
}
public void setMyProps(Properties myProps) {
this.myProps = myProps;
}
```
2. 相应的bean.xml配置
```
<bean id="accountService3" class="com.itheima.service.impl.IAccountServiceImpl3">
<property name="myStr" >
<array>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</array>
</property>
<property name="myList" >
<list>
<value>qweq</value>
<value>q55q</value>
<value>888q</value>
</list>
</property>
<property name="myMap">
<map>
<entry key="testa" value="aaaa"></entry>
<entry key="testb">
<value>bbbb</value>
</entry>
</map>
</property>
<property name="mySet">
<set>
<value>12..as</value>
<value>%$^.as</value>
<value>omg</value>
</set>
</property>
<property name="myProps">
<props>
<prop key="testC">cccc</prop>
<prop key="testD">dddd</prop>
</props>
</property>
</bean>
```