10 lookup-method标签使用
spring 中默认的对象都是单例的,spring会在一级缓存中持有该对象,方便下次直接获取,如果对象作用域是原型作用域,则会创建一个新的对象。
1 错误示例--单例对象引用多例对象
code如下
package com.gientech.methodOverrides.loopup;
public class Apple extends Fruit{
private Banana banana;
public Apple(){
System.out.println("i get a fresh apple");
}
public Banana getBanana() {
return banana;
}
public void setBanana(Banana banana) {
this.banana = banana;
}
}
package com.gientech.methodOverrides.loopup;
public class Banana extends Fruit{
public Banana(){
System.out.println("I get a fresh Banana");
}
}
<?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="apple" class="com.gientech.methodOverrides.loopup.Apple">
<property name="banana" ref="banana"></property>
</bean>
<bean id="banana" class="com.gientech.methodOverrides.loopup.Banana" scope="prototype"></bean>
</beans>
public class methodOverridesTest {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("methodOverride.xml");
Apple bean = ac.getBean(Apple.class);
System.out.println(bean.getBanana());
System.out.println(bean.getBanana());
}
}
或启动类code 如下:
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("methodOverride.xml");
Apple bean1 = ac.getBean(Apple.class);
System.out.println(bean1.getBanana());
Apple bean2 = ac.getBean(Apple.class);
System.out.println(bean2.getBanana());
}
运行结果都是如下图所示:
为什么banana对象是同一个呢,是apple对象是单例,会缓存在一级缓存中,同时也会将banana写入缓存中,缓存截图如下:
问题:如果需要在一个单例模式的bean下引用一个原型模式的bean,需要怎么做呢?
此时需要引用lookup-method标签来解决此问题,具体实现和之心过程如下。
2 look-upmethod 标签执行过程
2.1 创建abstract class
package com.gientech.methodOverrides.loopup;
public abstract class FruitPlate {
// 抽象方法获取新鲜水果
public abstract Fruit getFruit();
}
2.2 创建其他类
package com.gientech.methodOverrides.loopup;
public class Apple extends Fruit{
public Apple(){
System.out.println("i got apple");
}
}
package com.gientech.methodOverrides.loopup;
public class Banana extends Fruit{
public Banana(){
System.out.println("I got Banana");
}
}
package com.gientech.methodOverrides.loopup;
public class Fruit {
public Fruit(){
System.out.println("I got Fruit");
}
}
2.3 创建配置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="apple" class="com.gientech.methodOverrides.loopup.Apple"></bean>
<bean id="banana" class="com.gientech.methodOverrides.loopup.Banana"></bean>
<bean id="fruitplate1" class="com.gientech.methodOverrides.loopup.FruitPlate">
<lookup-method name="getFruit" bean="apple"></lookup-method>
</bean>
<bean id="fruitplate2" class="com.gientech.methodOverrides.loopup.FruitPlate">
<lookup-method name="getFruit" bean="banana"></lookup-method>
</bean>
</beans>
2.4 创建启动类
package com.gientech.methodOverrides.loopup;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class methodOverridesTest {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("methodOverride.xml");
FruitPlate fruitplate1 = (FruitPlate) ac.getBean("fruitplate1");
fruitplate1.getFruit();
FruitPlate fruitplate2 = (FruitPlate) ac.getBean("fruitplate2");
fruitplate2.getFruit();
}
}
运行结果如下:
在创建 FruitPlate 的对象 fruitplate1时,prepareMethodOverrides() 方法中设置Overloaded属性为false,即标记methodOverride暂未被覆盖,避免参数类型检查的开销,在doCreateBean实例化时,可直接进行调用,getInstantiationStrategy() 方法获取实例化策略,默认返回cglib(CglibSubclassingInstantiationStrategy),再次对象中有PASSTHROUGH,LOOKUP_OVERRIDE,METHOD_REPLACER 三个那个属性,
此时容器一级缓存中的数据如下图,
当执行
fruitplate1.getFruit();
时,直接进入CglibSubclassingInstantiationStrategy.intercept(Object obj, Method method, Object[] args, MethodProxy mp) 方法,内存截图如下:
此时会从缓存中获取Apple对象并返回。
3 look-up标签示例--单例对象引用多例对象,
问题:在如下代码的情况下,获取的Apple是同一个对象吗?
配置文件如下:
<?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="apple" class="com.gientech.methodOverrides.loopup.Apple" scope="prototype"></bean>
<bean id="banana" class="com.gientech.methodOverrides.loopup.Banana" scope="prototype"></bean>
<bean id="fruitplate1" class="com.gientech.methodOverrides.loopup.FruitPlate">
<lookup-method name="getFruit" bean="apple"></lookup-method>
</bean>
<bean id="fruitplate2" class="com.gientech.methodOverrides.loopup.FruitPlate">
<lookup-method name="getFruit" bean="banana"></lookup-method>
</bean>
</beans>
其他代码如流程,启动类代码如下,
package com.gientech.methodOverrides.loopup;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class methodOverridesTest {
public static void main(String[] args) {
FruitPlate fruitplate1 = (FruitPlate) ac.getBean("fruitplate1");
Apple apple1 = (Apple) fruitplate1.getFruit();
System.out.println(apple1);
FruitPlate fruitplate2 = (FruitPlate) ac.getBean("fruitplate1");
Apple apple2 = (Apple) fruitplate2.getFruit();
System.out.println(apple2);
}
}
结论是:获取的apple对象不一致,因为此时apple对象是每次获取时新创建的,是通过拦截器的方式每次需要的时候都去创建最新的对象,而不会把原型对象缓存起来。,不是从缓存中获取的。运行截图如下:
缓存截图如下
lookup-method标签是解决单例模式引用原型模式的场景,是通过拦截器的方式每次需要的时候都去创建最新的对象,而不会把原型对象缓存起来。