Spring中的@Lookup注解和<lookup-method>标签

@Lookup 是xml配置标签look-up的注解实现。

一、<lookup-method/>标签

假设一个单例模式的bean A需要引用另外一个非单例模式的bean B,为了在我们每次引用的时候都能拿到最新的bean B,我们可以让bean A通过实现ApplicationContextWare来感知applicationContext(即可以获得容器上下文),从而能在运行时通过ApplicationContext.getBean(String beanName)的方法来获取最新的bean B。但是如果用ApplicationContextAware接口,就让我们与Spring代码耦合了,违背了反转控制原则(IoC,即bean完全由Spring容器管理,我们自己的代码只需要用bean就可以了)。
所以Spring为我们提供了方法注入的方式来实现以上的场景。方法注入方式主要是通过标签。

实例

下面我们用一个例子来说明lookup-method的用法。
假设有一个果盘,果盘里放了一些水果,比如苹果,香蕉等,我们希望我们每次在果盘里拿到的都是最新鲜的水果。
java代码:

// 定义一个水果类
public class Fruit {
    public Fruit() {
        System.out.println("I got Fruit");
    }
}

// 苹果
public class Apple extends Fruit {
    public Apple() {
        System.out.println("I got a fresh apple");
    }
}

// 香蕉
public class Bananer extends Fruit {
    public Bananer () {
        System.out.println("I got a  fresh bananer");
    }
}

// 水果盘,可以拿到水果
public abstract class FruitPlate{
    // 抽象方法获取新鲜水果
    protected abstract Fruit getFruit();
}

spring配置:

<!-- 这是2个非单例模式的bean -->
<bean id="apple" class="cn.com.willchen.test.di.Apple" scope="prototype"/>
<bean id="bananer" class="cn.com.willchen.test.di.Bananer " scope="prototype"/>
<bean id="fruitPlate1" class="cn.com.willchen.test.di.FruitPlate">
    <lookup-method name="getFruit" bean="apple"/>
</bean>
<bean id="fruitPlate2" class="cn.com.willchen.test.di.FruitPlate">
    <lookup-method name="getFruit" bean="bananer"/>
</bean>

测试代码:

public static void main(String[] args) {
    ApplicationContext app = new ClassPathXmlApplicationContext("classpath:resource/applicationContext.xml");

    FruitPlate fp1= (FruitPlate)app.getBean("fruitPlate1");
    FruitPlate fp2 = (FruitPlate)app.getBean("fruitPlate2");

    fp1.getFruit();
    fp2.getFruit();
}

测试结果:

I got Fruit
I got a fresh apple
I got Fruit
I got a fresh bananer

示例说明:
从上面例子我们可以看到,在代码中,我们没有用到Spring的任何类和接口,实现了与Spring代码的耦合。
其中,最为核心的部分就是lookup-method的配置和FruitPlate.getFruit()方法。上面代码中,我们可以看到getFruit()方法是个抽象方法,我们并没有实现它啊,那它是怎么拿到水果的呢。
这里的奥妙就是Srping应用了CGLIB(动态代理)类库。Spring在初始化容器的时候对配置的bean做了特殊处理,Spring会对bean指定的class做动态代理,代理标签中name属性所指定的方法,返回bean属性指定的bean实例对象。每次我们调用fruitPlate1或者fruitPlate2这2个bean的getFruit()方法时,其实是调用了CGLIB生成的动态代理类的方法。关于CGLIB大家可自行在网上查阅。
lookup-method实现方式说明:

<bean class="beanClass">
	<lookup-method name="method" bean="non-singleton-bean"/>
</bean>
二、@Lookup注解

实例
有一个单例的Boy类:

@Component
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON) //原型 也就是非单例
public class Boy {
    public void playToy() {
        Toy toy = getToy();
        System.out.println(toy);
    }

    @Lookup
    public Toy getToy() {
        return null;
    }
}

然后他每次要playToy()的时候,都会生成一个新的Toy对象,Toy是原型类型的,并且用@Lookup注解修饰playToy()方法,方法体里面返回空,Spring会自动生成一个代理对象:

@Component
@Scope(scopeName= ConfigurableBeanFactory.SCOPE_PROTOTYPE) //原型 也就是非单例
public class Toy {

    public Toy() {
        System.out.println("Buy a new toy...");
    }
}

THE END.

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
适合初学 springboot 的同学 --------------------------- maven配置:pom.xml --------------------------- <?xml version="1.0"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> --------------------------- src/main/resources/application.yml --------------------------- spring: # 指定静态资源的路径 resources: static-locations: classpath:/static/,classpath:/views/ thymeleaf: prefix: classpath:/templates/ server: port:8080 context-path:"/" --------------------------- DemoApplication 运行 main 方法即可启动 springboot --------------------------- package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; @SpringBootApplication @ComponentScan(basePackages = {"com.example.*"}) //指定扫描包路径 public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } --------------------------- HelloWorldController --------------------------- package com.example.controller; import java.util.HashMap; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; // @RestController返回的是json @RestController public class HelloWorldController { // http://localhost:8080/hello 返回的是文本"Hello World" @RequestMapping("/hello") public String index() { return "Hello World"; } /** * 本地访问内容地址 :http://localhost:8080/hello3 ;返回的是文本 */ @RequestMapping("/hello3") public String helloHtml(HashMap<String, Object> map) { map.put("hello", "欢迎进入HTML页面"); return "/index"; } } --------------------------- HelloWorldController --------------------------- package com.example.controller; import java.util.HashMap; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class SpringBootController { /** * 本地访问内容地址 :http://localhost:8080/hello2 ; 可访问到 * src/main/resources/views/index.html */ @RequestMapping("/hello2") public String helloHtml(HashMap<String, Object> map) { // 传参数hello到html页面 map.put("hello", "欢迎进入HTML页面"); return "/index"; } } --------------------------- src/main/resources/views/index.html --------------------------- <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> aaaaaaaaaaaaaaaaaaaaaaacccccccccccccc <p th:text="${hello}">dddd</p> </body> </html> --------------------------- 直接访问静态页面 --------------------------- http://localhost:8080/index.html 可直接访问到 src/main/resources/templates/index.html

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值