问题如下
Ioc03Test.java(要求配置Spring,让测试方法全部测试通过)
package com.yc.test;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.yc.test.bank.bean.Account;
/**
* 配置Spring,让测试方法全部测试通过
*/
public class Ioc03Test {
ClassPathXmlApplicationContext cxt = new ClassPathXmlApplicationContext("IOC02.xml");
@Test
public void test1() {
Account a = cxt.getBean(Account.class);
Account b = cxt.getBean(Account.class);
Account c = (Account) cxt.getBean("account");
Assert.assertEquals(a, b); // a == b
Assert.assertEquals(c, b); // c == b
}
@Test
public void test2() {
Account a = (Account) cxt.getBean("account1");
Account b = (Account) cxt.getBean("account1");
Account c = (Account) cxt.getBean("account1");
Assert.assertNotEquals(a, b);
Assert.assertNotEquals(c, b);
Assert.assertNotEquals(c, a);
}
@Test
/**
* 工厂方法 (动态 or 静态)
*/
public void test3() {
Account a1 = (Account) cxt.getBean("account2");
Account a2 = (Account) cxt.getBean("account2");
Account a3 = (Account) cxt.getBean("account3");
Account a4 = (Account) cxt.getBean("account3");
Assert.assertEquals(a1, a2);// a1 == a2
Assert.assertEquals(a3, a4);// a3 == a4
Assert.assertEquals(a1, a3);
Assert.assertEquals(a2, a4);
}
/**
* 坑。。。
*/
@Test
public void test4() {
Account a1 = (Account) cxt.getBean("account4");
Account a2 = (Account) cxt.getBean("account4");
Account a3 = (Account) cxt.getBean("account4");
Account a4 = (Account) cxt.getBean("account4");
Account a5 = (Account) cxt.getBean("account4");
Account a6 = (Account) cxt.getBean("account4");
Account a7 = (Account) cxt.getBean("account4");
Assert.assertNotEquals(a1, a2);
Assert.assertNotEquals(a1, a3);
Assert.assertNotEquals(a1, a4);
Assert.assertEquals(a4, a5);
Assert.assertEquals(a4, a6);
Assert.assertEquals(a4, a7);
}
}
Account.java
package com.yc.test.bank.bean;
import org.springframework.stereotype.Component;
/**
* 银行账户
* @author Administrator
*
*/
@Component("account") // 组件 表示该类是spring管理的 bean @Bean
public class Account {
private Integer id; // 主键
private String name; // 姓名
private Double money; // 余额
public static int i = 0;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
/**
* 这是一个不完整的单例模式,少了私有的构造方法
*/
private static Account singleAccount;
public static Account getInstance() {
if(singleAccount == null) {
singleAccount = new Account();
}
return singleAccount;
}
@Override
public String toString() {
return "Account [id=" + id + ", name=" + name + ", money=" + money + "]";
}
}
接下来的代码就是要你配置的Spring(也就是答案)
IOC02.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: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/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="account" class="com.yc.test.bank.bean.Account" primary="true"> </bean>
<bean id="account1" class="com.yc.test.bank.bean.Account" scope="prototype" >
</bean>
<bean id="accfactory" class="com.yc.test.factory.AccountFactory"> </bean>
<!-- 创建一次AccountFactory实例 -->
<bean id="account2" class="com.yc.test.bank.bean.Account"
factory-bean="accfactory" factory-method="getAccount" scope="prototype">
</bean>
<!-- 创建一次AccountFactory实例 -->
<bean id="account3" class="com.yc.test.bank.bean.Account"
factory-bean="accfactory" factory-method="getAccount" scope="prototype">
</bean>
<!-- 创建一次AccountFactory实例 -->
<bean id="account4" class="com.yc.test.bank.bean.Account" scope="prototype"
factory-bean="accfactory" factory-method="getAccount" >
</bean>
</beans>
AccountFactory.java
package com.yc.test.factory;
import com.yc.test.bank.bean.Account;
public class AccountFactory {
public Account getAccount(){
System.out.println("您好,我是实例工厂");
return Account.getInstance();
}
}
分析
上面的IOC02.xml是要你写的但是只能成功运行Test1到3。无法运行test4
在这里我讲解一下test4的解决方案
@Test
public void test4() {
Account a1 = (Account) cxt.getBean("account4");
Account a2 = (Account) cxt.getBean("account4");
Account a3 = (Account) cxt.getBean("account4");
Account a4 = (Account) cxt.getBean("account4");
Account a5 = (Account) cxt.getBean("account4");
Account a6 = (Account) cxt.getBean("account4");
Account a7 = (Account) cxt.getBean("account4");
Assert.assertNotEquals(a1, a2);
Assert.assertNotEquals(a1, a3);
Assert.assertNotEquals(a1, a4);
Assert.assertEquals(a4, a5);
Assert.assertEquals(a4, a6);
Assert.assertEquals(a4, a7);
}
根据代码可以看出要想使test4能够执行必须满足 (a1与a2,a3,a4不是一个对象 a4与a5,a6,a7是一个对象)
a1与a2,a3,a4不是一个对象 我们知道可以将scope设置为原型prototype
a4与a5,a6,a7是一个对象 我们知道可以将scope设置为单例singleton 或者用静态工厂或者实例工厂(动态工厂)
但要两者都满足所以只能将scope设置为原型prototype 并且 采用静态工厂或者实例工厂 在这里我用实例工厂
所以IOC02.xml代码为
<bean id="account4" class="com.yc.test.bank.bean.Account" scope="prototype"
factory-bean="accfactory" factory-method="getAccount" >
</bean>
但是这样还不行因为单单这样只能保证得到的都是单例对象
scope="prototype"和factory结合的原理
首先给大家看一个现象
将Ioc03Test.java的test4改为如下
@Test
public void test4() {
Account a1 = (Account) cxt.getBean("account4");
Account a2 = (Account) cxt.getBean("account4");
Account a3 = (Account) cxt.getBean("account4");
Account a4 = (Account) cxt.getBean("account4");
Account a5 = (Account) cxt.getBean("account4");
Account a6 = (Account) cxt.getBean("account4");
Account a7 = (Account) cxt.getBean("account4");
System.out.println(a1 == a2);
System.out.println(a1 == a3);
System.out.println(a1 == a4);
System.out.println(a4 == a5);
System.out.println(a4 == a6);
System.out.println(a4 == a7);
// Assert.assertNotEquals(a1, a2);
// Assert.assertNotEquals(a1, a3);
// Assert.assertNotEquals(a1, a4);
// Assert.assertEquals(a4, a5);
// Assert.assertEquals(a4, a6);
// Assert.assertEquals(a4, a7);
}
输出结果为
您好,我是实例工厂
您好,我是实例工厂
您好,我是实例工厂
您好,我是实例工厂
您好,我是实例工厂
您好,我是实例工厂
您好,我是实例工厂
true
true
true
true
true
true
通过结果我们可以知道当用scope="prototype"时,每实例话一个对象就会调用一次AccountFactory类的getAccount方法。
根据这个原理我们只要通过实例化的次数来控制是需要产生相同的对象还是不同的对象。
所以最终AccountFactory.java的代码如下
package com.yc.test.factory;
import com.yc.test.bank.bean.Account;
public class AccountFactory {
public static int i = 0;//来得到实例化的次数
public Account getAccount(){
i++;
if(i>=5&&i<=7){
return new Account();//在产生a1到a3时创建不同对象
}
return Account.getInstance();//a4到a7产生相同对象
}
}
这边i从5开始是由于test3那边我用了scope="prototype"和实例工厂的缘故
要求
执行Ioc03Test时,必须将tset1到test4一起执行。否则无法通过测试