好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

如何在Spring中自定义scope的方法示例

大家对于 spring 的 scope 应该都不会默认。所谓 scope,字面理解就是[作用域]、[范围],如果一个 bean 的 scope 配置为 singleton,则从容器中获取 bean 返回的对象都是相同的;如果 scope 配置为prototype,则每次返回的对象都不同。

一般情况下,spring 提供的 scope 都能满足日常应用的场景。但如果你的需求极其特殊,则本文所介绍自定义 scope 合适你。

spring 内置的 scope

默认时,所有 spring bean 都是的单例的,意思是在整个 spring 应用中,bean的实例只有一个。可以在 bean 中添加 scope 属性来修改这个默认值。scope 属性可用的值如下:

 

范围 描述
singleton 每个 spring 容器一个实例(默认值)
prototype 允许 bean 可以被多次实例化(使用一次就创建一个实例)
request 定义 bean 的 scope 是 http 请求。每个 http 请求都有自己的实例。只有在使用有 web 能力的 spring 上下文时才有效
session 定义 bean 的 scope 是 http 会话。只有在使用有 web 能力的 spring applicationcontext 才有效
application 定义了每个 servletcontext 一个实例
websocket 定义了每个 websocket 一个实例。只有在使用有 web 能力的 spring applicationcontext 才有效

 

如果上述 scope 仍然不能满足你的需求,spring 还预留了接口,允许你自定义 scope。

scope 接口

org.springframework.beans.factory.config.scope 接口用于定义scope的行为:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

package org.springframework.beans.factory.config;

 

import org.springframework.beans.factory.objectfactory;

import org.springframework.lang.nullable;

 

public interface scope {

 

  object get(string name, objectfactory<?> objectfactory);

 

  @nullable

  object remove(string name);

 

  void registerdestructioncallback(string name, runnable callback);

 

  @nullable

  object resolvecontextualobject(string key);

 

  @nullable

  string getconversationid();

 

}

一般来说,只需要重新 get 和 remove 方法即可。

自定义线程范围内的scope

现在进入实战环节。我们要自定义一个spring没有的scope,该scope将bean的作用范围限制在了线程内。即,相同线程内的bean是同个对象,跨线程则是不同的对象。

1. 定义scope

要自定义一个spring的scope,只需实现 org.springframework.beans.factory.config.scope接口。代码如下:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

package com.waylau.spring.scope;

 

import java.util.hashmap;

import java.util.map;

 

import org.springframework.beans.factory.objectfactory;

import org.springframework.beans.factory.config.scope;

 

/**

  * thread scope.

  *

  * @since 1.0.0 2019年2月13日

  * @author <a href="https://waylau测试数据" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >way lau</a>

  */

public class threadscope implements scope {

 

  private final threadlocal<map<string, object>> threadloacal = new threadlocal<map<string, object>>() {

  @override

  protected map<string, object> initialvalue() {

   return new hashmap<string, object>();

  }

  };

 

  public object get(string name, objectfactory<?> objectfactory) {

  map<string, object> scope = threadloacal.get();

  object obj = scope.get(name);

 

  // 不存在则放入threadlocal

  if (obj == null ) {

   obj = objectfactory.getobject();

   scope.put(name, obj);

  

   system.out.println( "not exists " + name + "; hashcode: " + obj.hashcode());

  } else {

   system.out.println( "exists " + name + "; hashcode: " + obj.hashcode());

  }

 

  return obj;

  }

 

  public object remove(string name) {

  map<string, object> scope = threadloacal.get();

  return scope.remove(name);

  }

 

  public string getconversationid() {

  return null ;

  }

 

  public void registerdestructioncallback(string arg0, runnable arg1) {

  }

 

  public object resolvecontextualobject(string arg0) {

  return null ;

  }

 

}

在上述代码中,threadloacal用于做线程之间的数据隔离。换言之,threadloacal实现了相同的线程相同名字的bean是同一个对象;不同的线程的相同名字的bean是不同的对象。

同时,我们将对象的hashcode打印了出来。如果他们是相同的对象,则hashcode是相同的。

2. 注册scope

定义一个appconfig配置类,将自定义的scope注册到容器中去。代码如下:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

package com.waylau.spring.scope;

 

import java.util.hashmap;

import java.util.map;

 

import org.springframework.beans.factory.config.customscopeconfigurer;

import org.springframework.context.annotation.bean;

import org.springframework.context.annotation测试数据ponentscan;

import org.springframework.context.annotation.configuration;

 

/**

  * app config.

  *

  * @since 1.0.0 2019年2月13日

  * @author <a href="https://waylau测试数据" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >way lau</a>

  */

@configuration

@componentscan

public class appconfig {

 

  @bean

  public static customscopeconfigurer customscopeconfigurer() {

  customscopeconfigurer customscopeconfigurer = new customscopeconfigurer();

 

  map<string, object> map = new hashmap<string, object>();

  map.put( "threadscope" , new threadscope());

 

  // 配置scope

  customscopeconfigurer.setscopes(map);

  return customscopeconfigurer;

  }

}

[threadscope]就是自定义threadscope的名称。

3. 使用scope

接下来就根据一般的scope的用法,来使用自定义的scope了。代码如下:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

package com.waylau.spring.scope.service;

 

import org.springframework.context.annotation.scope;

import org.springframework.stereotype.service;

 

/**

  * message service impl.

  *

  * @since 1.0.0 2019年2月13日

  * @author <a href="https://waylau测试数据" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >way lau</a>

  */

@scope ( "threadscope" )

@service

public class messageserviceimpl implements messageservice {

 

  public string getmessage() {

  return "hello world!" ;

  }

 

}

其中@scope("threadscope")中的[threadscope]就是自定义threadscope的名称。

4. 定义应用入口

定义spring应用入口:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

package com.waylau.spring.scope;

 

import org.springframework.context.applicationcontext;

import org.springframework.context.annotation.annotationconfigapplicationcontext;

 

import com.waylau.spring.scope.service.messageservice;

 

/**

  * application main.

  *

  * @since 1.0.0 2019年2月13日

  * @author <a href="https://waylau测试数据" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >way lau</a>

  */

public class application {

 

  public static void main(string[] args) {

  @suppresswarnings ( "resource" )

  applicationcontext context =

       new annotationconfigapplicationcontext(appconfig. class );

 

  messageservice messageservice = context.getbean(messageservice. class );

  messageservice.getmessage();

 

  messageservice messageservice2 = context.getbean(messageservice. class );

  messageservice2.getmessage();

 

  }

 

}

运行应用观察控制台输出如下:

not exists messageserviceimpl; hashcode: 2146338580
exists messageserviceimpl; hashcode: 2146338580

输出的结果也就验证了threadscope[相同的线程相同名字的bean是同一个对象]。

如果想继续验证threadscope[不同的线程的相同名字的bean是不同的对象],则只需要将application改造为多线程即可。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

package com.waylau.spring.scope;

 

import java.util.concurrent测试数据pletablefuture;

import java.util.concurrent.executionexception;

 

import org.springframework.context.applicationcontext;

import org.springframework.context.annotation.annotationconfigapplicationcontext;

 

import com.waylau.spring.scope.service.messageservice;

 

/**

  * application main.

  *

  * @since 1.0.0 2019年2月13日

  * @author <a href="https://waylau测试数据" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >way lau</a>

  */

public class application {

 

  public static void main(string[] args) throws interruptedexception, executionexception {

  @suppresswarnings ( "resource" )

  applicationcontext context =

   new annotationconfigapplicationcontext(appconfig. class );

 

  completablefuture<string> task1 = completablefuture.supplyasync(()->{

       //模拟执行耗时任务

       messageservice messageservice = context.getbean(messageservice. class );

   messageservice.getmessage();

 

   messageservice messageservice2 = context.getbean(messageservice. class );

   messageservice2.getmessage();

       //返回结果

       return "result" ;

     });

 

  completablefuture<string> task2 = completablefuture.supplyasync(()->{

       //模拟执行耗时任务

       messageservice messageservice = context.getbean(messageservice. class );

   messageservice.getmessage();

 

   messageservice messageservice2 = context.getbean(messageservice. class );

   messageservice2.getmessage();

       //返回结果

       return "result" ;

     });

 

  task1.get();

  task2.get();

 

  }

}

观察输出结果;

not exists messageserviceimpl; hashcode: 1057328090
not exists messageserviceimpl; hashcode: 784932540
exists messageserviceimpl; hashcode: 1057328090
exists messageserviceimpl; hashcode: 784932540

上述结果验证threadscope[相同的线程相同名字的bean是同一个对象;不同的线程的相同名字的bean是不同的对象]

源码

见 的 s5-ch02-custom-scope-annotation项目。

以 https://github测试数据/waylau/spring-5-book 上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

原文链接:https://my.oschina.net/waylau/blog/3009991

查看更多关于如何在Spring中自定义scope的方法示例的详细内容...

  阅读:19次