Loading

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();
    }
}

运行结果如下:
lookup-method场景1

在创建 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) 方法,内存截图如下:
cglib内存
此时会从缓存中获取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对象是每次获取时新创建的,是通过拦截器的方式每次需要的时候都去创建最新的对象,而不会把原型对象缓存起来。,不是从缓存中获取的。运行截图如下:
scope运行截图

缓存截图如下
scope内存截图

lookup-method标签是解决单例模式引用原型模式的场景,是通过拦截器的方式每次需要的时候都去创建最新的对象,而不会把原型对象缓存起来。

posted @ 2024-03-13 17:51  zgcy123456  阅读(27)  评论(0)    收藏  举报