Spring framework不同Scope的Bean注入时使用代理类的理解

最近没事在看Spring framework的参考文档,对部分有疑问的地方写些简单的demo测试一下。

以下是一个例子测试使用@Bean注解配置不同Scope的Bean注入时的效果是否与XML配置时一致。

1.首先写一个Bean的配置类,在singleton的MyService中注入prototype的MyMode类。这里使用接口为了能自由选择JDK动态代理或CGLIB


Mode接口

package com.bean;

public interface IMode {
    public void print();
}


Mode实现

package com.bean;

import java.util.concurrent.atomic.AtomicInteger;

public class MyMode implements IMode {

	private static AtomicInteger i = new AtomicInteger(0);

	private int value;

	public MyMode() {
		System.out.println("MyMode constructor call");
		this.value = i.incrementAndGet();
	}

	public void print() {
		System.out.println("MyMode print called");
		System.out.println(value);
	}

}



Service接口

package com.bean;

public interface IService {

	public IMode getMode();
}

Service实现

package com.bean;

public class MyService implements IService{

	private IMode mode;
	
	public MyService() {

	}

	public MyService(IMode mode) {
		this.mode = mode;
	}
	
	public IMode getMode() {
		return mode;
	}

	public String toString() {
		if(mode==null){
			return "none mode in service";
		}
		return  "bingo";
	}
}


Bean配置类

package com;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;

import com.bean.IMode;
import com.bean.MyMode;
import com.bean.MyService;


@Configuration("test")
public class TestBeanConfigration {

	@Bean
	public IService myService(){
		return new MyService(myMode());
	}

	@Bean
	@Scope(value=BeanDefinition.SCOPE_PROTOTYPE)
	public IMode myMode(){
		return new MyMode();
	}
}
2、以下是Spring Boot的启动类,以及一个简单的Web Controller,修改自 Getting Started Building a RESTful Web Service
package com;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

package com;

import java.util.concurrent.atomic.AtomicLong;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.bean.IService;
import com.bean.MyMode;
import com.bean.IMode;

@RestController
public class GreetingController {

	private static final String template = "Hello, %s!";
	private final AtomicLong counter = new AtomicLong();

	@Autowired
	private IService myService;

	@Autowired
	private IMode mode;

	@RequestMapping("/greeting")
	public Greeting greeting(
			@RequestParam(value = "name", defaultValue = "World") String name) {
		System.out.println(myService.toString());
		System.out.println(mode == myService.getMode());
		mode.print();
		mode.print();

		myService.getMode().print();
		myService.getMode().print();
		return new Greeting(counter.incrementAndGet(), String.format(template,
				name));
	}

	private static class Greeting {

		private final long id;
		private final String content;

		public Greeting(long id, String content) {
			this.id = id;
			this.content = content;
		}

		public long getId() {
			return id;
		}

		public String getContent() {
			return content;
		}
	}
}



 3、运行Application。 

会发现在启动的时候,控制台输出两次:MyMode constructor call。原因有两个Singleton的Bean(MyService和GreetingController)分别注入了MyMode实例,而Singleton默认在启动时实例化,因此必须同时实例化其依赖对象。

在浏览器中输入链接调用greeting方法。输出:

bingo

false

MyMode print call2

MyMode print call2

MyMode print call1

MyMode print call1

这样就违背了初衷,既然是prototype类型的Bean,当然就是希望在每次取时都是不同对象。

在XML配置中需要在MyMode的<Bean>标签中加入子标签<aop:scoped-proxy/>启用代理类,它的默认策略该Bean对应的类存在接口则使用JDK动态代理,否则使用CGLIB。当然也可以用proxy-target-class属性强制指定。

在注解的配置中则是使用@Scope的proxyMode属性,与XML不同地方是这里必须指定代理策略 如果选用Default则默认使用@ComponentScan的代理策略。

4、因此在配置MyMode为@Scope(value=BeanDefinition.SCOPE_PROTOTYPE,proxyMode=ScopedProxyMode.INTERFACES)

再次启动,启动时没有出现MyMode constructor call,说明没有调用MyMode的构造方法。那MyService和GreetingController引用的IMode是什么?

后面再说,调用greeting方法,输出:

bingo

true

MyMode constructor call

MyMode print call1

MyMode constructor call

MyMode print call2

MyMode constructor call

MyMode print call3

MyMode constructor call

MyMode print call4

从上面结果可以看出,MyService和GreetingController引用是一个代理对象,而且是同一个,这个代理对象是singleton的。而每次IMode的方法调用,代理对象都重新生成一个源对象再调用源对象的方法。

5、将MyMode的Scope改为@Scope(value=WebApplicationContext.SCOPE_REQUEST,proxyMode=ScopedProxyMode.INTERFACES)

在重新启动并调用两次,得到结果:

bingotrueMyMode constructor callMyMode print call1MyMode print call1MyMode print call1MyMode print call1

bingotrueMyMode constructor callMyMode print call2MyMode print call2MyMode print call2MyMode print call2

每次request请求一个MyMode实例。

接下来对比XML配置时的运行结果,做一些修改

1、修改Bean配置类,引入XML配置

package com;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;


@Configuration("test")
@ImportResource("classpath:applicationContext.xml")
public class TestBeanConfigration {

	//@Bean
//	public IService myService(){
//		return new MyService(myMode());
//	}

	//@Bean
	//@Scope(value=WebApplicationContext.SCOPE_REQUEST,proxyMode=ScopedProxyMode.INTERFACES)
//	public IMode myMode(){
//		return new MyMode();
//	}
//	

}


2、添加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:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

	<bean id="myService" class="com.bean.MyService" depends-on="myMode">
		<constructor-arg ref="myMode" />
	</bean>

	<bean id="myMode" class="com.bean.MyMode" scope="prototype">
	   <aop:scoped-proxy/>
	</bean>

</beans>

3、启动,一样没有调用MyMode的构造函数。

调用方法输出结果:


bingo
true
MyMode constructor call
MyMode print call
1
MyMode constructor call
MyMode print call
2
MyMode constructor call
MyMode print call
3
MyMode constructor call
MyMode print call
4


当然也可以试试其他Scope,得出结论与上面注解配置表现是一致的(想过去也是一致的,写个例子熟悉下配置)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值