Spring入门之三-------SpringIoC之Scopes
一、singleton和prototype
public class Bean1 { public Bean1() { System.out.println(this.getClass().getSimpleName() + ":" + this.toString() + " has been created"); } }
1. singleton:
每个IoC容器只创建一个该类的Bean。
<bean id="bean1Singleton" class="com.imooc.springClass3.iocOthers.Bean1" scope="singleton"/>
public class BeanTest { @Test public void testBean() throws Exception { for (int i = 0; i < 10; i++) { Bean1 bean1Singleton = context.getBean("bean1Singleton", Bean1.class); System.out.println("bean1Singleton = " + bean1Singleton); } } }
输出
bean1Singleton = com.imooc.springClass3.iocOthers.Bean1@696da30b bean1Singleton = com.imooc.springClass3.iocOthers.Bean1@696da30b bean1Singleton = com.imooc.springClass3.iocOthers.Bean1@696da30b ......
可以看到,每个Bean对象的地址都是一样的。
2. prototype
每次从IoC容器中请求Bean都会创建一个新的实例,包括将Bean注入到另一个Bean中或通过Spring上下文执行getBean方法
<bean id="bean1Prototype" class="com.imooc.springClass3.iocOthers.Bean1" scope="prototype"/>
public class BeanTest { @Test public void testBean() throws Exception { for (int i = 0; i < 10; i++) { Bean1 bean1Prototype = context.getBean("bean1Prototype", Bean1.class); System.out.println("bean1Prototype = " + bean1Prototype); } } }
输出
Bean1:com.imooc.springClass3.iocOthers.Bean1@5f9b2141 has been created bean1Prototype = com.imooc.springClass3.iocOthers.Bean1@5f9b2141 Bean1:com.imooc.springClass3.iocOthers.Bean1@247d8ae has been created bean1Prototype = com.imooc.springClass3.iocOthers.Bean1@247d8ae Bean1:com.imooc.springClass3.iocOthers.Bean1@48974e45 has been created bean1Prototype = com.imooc.springClass3.iocOthers.Bean1@48974e45 ......
可以看到,每个Bean对象的地址都是不同的。
二、request、session、application、websocket
只有在web环境下才可以使用这四个作用域,要引入web环境要求做如下配置(web.xml):
如果使用DispatcherServlet,则不需要增加其他任何配置,例如:
<servlet> <servlet-name>SpringServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>SpringServlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
如果不使用DispatcherServlet,那么需要增加listener或filter:
(1)如果是Servlet 2.4以上
<listener> <listener-class> org.springframework.web.context.request.RequestContextListener </listener-class> </listener>
(2)如果是Servlet 2.4及以下
<filter> <filter-name>requestContextFilter</filter-name> <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class> </filter> <filter-mapping> <filter-name>requestContextFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
配置好了我们开始测试:
1. request
每个Bean的作用域为一个request请求,即每个request请求都会创建一个单独的实例。
@Controller public class RequestScopeController { @GetMapping("testRequestScope") @ResponseBody public String test() { return this.toString(); } }
<bean id="testController" class="com.imooc.springClass3.iocOthers.RequestScopeController" scope="request"/>
运行tomcat,访问:http://127.0.0.1:8080/testRequestScope,刷新几次,可以看到每次刷新后创建的RequestScopeController实例都是不同的地址。
2. session
每个Bean的作用域为一个session会话,即每个session都会创建一个单独的实例
@Controller public class SessionScopeController { @GetMapping("testSessionScope") @ResponseBody public String test() { return this.toString(); } }
<bean id="sessionScopeController" class="com.imooc.springClass3.iocOthers.SessionScopeController" scope="session"/>
运行tomcat,访问:http://127.0.0.1:8080/testRequestScope,刷新几次,可以看到每次刷新后创建的SessionScopeController实例都是一样的地址。但是当我们打开另外一个浏览器或重新打开当前浏览器再次访问该地址,可以看到SessionScopeController的实例的地址发生了变化
3. application
每个Bean的作用域为一个servletContext上下文,即每个servletContext都会创建一个单独的实例
@Controller public class ApplicationScopeController { @GetMapping("testAppScope") @ResponseBody public String test() { return this.toString(); } }
<bean id="applicationScopeController" class="com.imooc.springClass3.iocOthers.ApplicationScopeController" scope="application"/>
运行tomcat,访问:http://127.0.0.1:8080/testRequestScope,刷新几次,可以看到每次刷新后创建的ApplicationScopeController实例都是一样的地址。即使我们打开另外一个浏览器或重新打开当前浏览器再次访问该地址,ApplicationScopeController的实例的地址也不会发生变化。
4. websocket
每个Bean的作用域为一个webSocket连接,即每个webSocket连接都会创建一个新的实例。
三、自定义scope以及SimpleThreadScope
1. 自定义scope
(1)定义一个Class实现org.springframework.beans.factory.config.Scope,本例中定义了一个MyScope,实现:每个Class最多可以生成两个实例。代码如下:
public class MyScope implements org.springframework.beans.factory.config.Scope { private Map<String, Object> map = new ConcurrentHashMap<String, Object>(); public Object get(String s, ObjectFactory<?> objectFactory) { if (map.containsKey(s + "0") && map.containsKey(s + "1")) { return map.get(s + new Random().nextInt(2)); } else { Object object = objectFactory.getObject(); if (!map.containsKey(s + "0")){ map.put(s + "0", object); } else if (!map.containsKey(s + "1")) { map.put(s + "1", object); } return object; } } @Nullable public Object remove(String s) { Object object = null; if (map.containsKey(s + "0") && map.containsKey(s + "1")) { object = map.get(s + new Random().nextInt(2)); } else if (map.containsKey(s + "0")){ object = map.get(s + "0"); } else if (map.containsKey(s + "1")) { object = map.get(s + "1"); } map.remove(s + "0"); map.remove(s + "1"); return object; } public void registerDestructionCallback(String s, Runnable runnable) { } @Nullable public Object resolveContextualObject(String s) { return null; } @Nullable public String getConversationId() { return null; } }
(2)向Spring注册MyScope
<bean id="myScope" class="com.imooc.springClass3.iocOthers.MyScope"/> <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"> <property name="scopes"> <map> <entry key="myScope" value-ref="myScope"/> </map> </property> </bean>
(3)注册一个Bean使用MyScope作用域
<bean id="bean1MyScope" class="com.imooc.springClass3.iocOthers.Bean1" scope="myScope"/>
(4)测试
public class BeanTest { @Test public void testBean() throws Exception { for (int i = 0; i < 10; i++) { Bean1 bean1MyScope = context.getBean("bean1MyScope", Bean1.class); System.out.println("bean1MyScope = " + bean1MyScope); } }
(5)输出
bean1MyScope = com.imooc.springClass3.iocOthers.Bean1@932bc4a bean1MyScope = com.imooc.springClass3.iocOthers.Bean1@932bc4a bean1MyScope = com.imooc.springClass3.iocOthers.Bean1@6bedbc4d bean1MyScope = com.imooc.springClass3.iocOthers.Bean1@932bc4a bean1MyScope = com.imooc.springClass3.iocOthers.Bean1@932bc4a bean1MyScope = com.imooc.springClass3.iocOthers.Bean1@6bedbc4d bean1MyScope = com.imooc.springClass3.iocOthers.Bean1@6bedbc4d bean1MyScope = com.imooc.springClass3.iocOthers.Bean1@932bc4a bean1MyScope = com.imooc.springClass3.iocOthers.Bean1@6bedbc4d
从输出结果可以看到只出现了两个不同的Bean地址,说明我们只创建了两个Bean实例。
2. SimpleThreadScope
每个Bean的作用域为一个线程,即每个线程都会创建一个Bean实例。
(1)注册SimpleThreadScope
<bean id="simpleThreadScope" class="org.springframework.context.support.SimpleThreadScope"/> <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"> <property name="scopes"> <map> <entry key="simpleThreadScope" value-ref="simpleThreadScope"/> </map> </property> </bean>
(2)注册一个Bean使用SimpleThreadScope作用域
<bean id="bean1SimpleThreadScope" class="com.imooc.springClass3.iocOthers.Bean1" scope="simpleThreadScope"/>
(3)测试
public class BeanTest { @Test public void testBean() throws Exception { for (int i = 0; i < 10; i++) { new Thread(new Runnable() { public void run() { for (int i1 = 0; i1 < 10; i1++) { Bean1 bean1SimpleThreadScope = context.getBean("bean1SimpleThreadScope", Bean1.class); System.out.println("bean1SimpleThreadScope = " + bean1SimpleThreadScope); } } }).start(); } Thread.sleep(2000); } }
(4)输出:由于多线程并发,我们没有控制线程同步,所以输出结果可能出现混乱,所以我们就不贴输出结果了。