Default Scope
In Spring IOC containers, there are two default bean scopes:
- Singleton, the Bean's lifetime and container are synchronized once the bean is created, the bean is destroyed only when the container is closed, the bean with the same ID is instantiated only once during the lifetime of the container, and the same instance is returned when the container is requested by the same ID.
- Prototype, this scoped bean will not be handed over to the container, and will not be registered to the container after creation, its birth and death is completely determined by the application layer, each time the container requests the bean through the same ID, a different instance will be created
Custom scopes
The Spring Framework provides an extensibility mechanism for consumer custom scopes, and the framework provides two key classes to implement custom scopes
Org.springframework.beans.factory.config.Scope interface, you need to implement the following key methods:
- Object Get (String name, objectfactory<?> objectfactory), where the consumer can get the bean instance of the scope by calling this method, the approximate logic of the implementation should be, Scopes the scope to find out if there is a bean of the corresponding name, if there is a direct return, if there is no GetObject method that calls the Objectfactory parameter to create the bean and cache it within that scope
- Object Remove (String name), which the consumer can call to delete the bean of the specified name under the scope
- void Registerdestructioncallback (String name, Runnable callback), registers the Bean's destructor callback, the scope object needs to save the write callback and triggers these callbacks when the bean is destroyed
- Object Resolvecontextualobject (String key)
Org.springframework.beans.factory.config.CustomScopeConfigurer, this is a BFPP, and its duty is to register the scope in the container
Sample code
Here is an example of how to customize the scope and how the code customization scope in the analysis framework is implemented, and the function of this custom scope is to cache the bean in an LRU cache, triggering a destructor callback when the Bean is kicked out of the cache
Implement the scope interface, the LRU cache can only put up to two beans, kicked out of the bean will violate the destructor callback, in the Removeeldestentry method, the destructor callback is saved in the Destructioncallback hash table:
public class Lrucachescope implements Scope {Private class Beancache extends Linkedhashmap<string, object> {private Static final Long serialversionuid = -887300667768355251l; @Overrideprotected boolean removeeldestentry (entry< String, object> eldest) {Boolean flag = size () > maxbeannumber;if (flag) {Executedescallback (Eldest.getkey ());} return flag;}} private static final int default_max_bean_number = 2;private map<string, object> beancache = Collections.synchroniz Edmap (New Beancache ());p rivate int maxbeannumber;private map<string, runnable> destructioncallback = new HashMap& Lt String, runnable> ();p ublic Lrucachescope () {this (default_max_bean_number);} Public lrucachescope (int maxbeannumber) {super (); this.maxbeannumber = Maxbeannumber;} @Overridepublic Object Get (String name, objectfactory<?> objectfactory) {Object bean = beancache.get (name); Bean = = null) {bean = Objectfactory.getobject (); Beancache.put (name, Bean);} return bean;} @Overridepublic Object REmove (String name) {destructioncallback.remove (name); return beancache.remove (name);} @Overridepublic void Registerdestructioncallback (String name, Runnable callback) {Destructioncallback.put (name, callback);} @Overridepublic Object resolvecontextualobject (String key) {return null;} @Overridepublic String Getconversationid () {return null;} private void Executedescallback (String beanname) {Runnable callBack = Destructioncallback.get (beanname); if (callBack! = NULL) {Callback.run ();} Destructioncallback.remove (Beanname);}}
Define Customscopeconfigurer register scope, and define other test beans
<bean id= "ScopedBean1" class= "Spring.beans.scope.ScopedBean" scope= "LruCache" ></bean><bean id= " ScopedBean2 "class=" Spring.beans.scope.ScopedBean "scope=" LruCache "></bean><bean id=" ScopedBean3 " class= "Spring.beans.scope.ScopedBean" scope= "LruCache" ></bean><bean class= " Org.springframework.beans.factory.config.CustomScopeConfigurer "><property name=" scopes "><map> <entry key= "LruCache" ><bean class= "Spring.beans.scope.LRUCacheScope" ></bean></entry>< /map></property></bean>
JUnit Test Code
@Testpublic void Test () {beanfactory context = new Classpathxmlapplicationcontext ("Spring/beans/scope/scope.xml"); Scopedbean bean1 = (Scopedbean) context.getbean ("ScopedBean1"); Scopedbean bean11 = (Scopedbean) context.getbean ("ScopedBean1"); Assertequals (Bean1, bean11); Scopedbean bean2 = (Scopedbean) context.getbean ("scopedBean2"); Scopedbean bean3 = (Scopedbean) context.getbean ("ScopedBean3"); bean11 = (Scopedbean) context.getbean ("ScopedBean1"); Assertnotequals (Bean1, bean11);}
Executing the test Code Discovery Code execution through, you can find that the last ScopedBean1 and the previous scopedBean1 is not an instance. View the console log discovery with the following information:
22:28:48,738 Debug Defaultlistablebeanfactory:432-creating instance of Bean ' scopedBean1 ' 22:28:48,738 debug Defaultlistablebeanfactory:460-finished creating instance of Bean ' scopedBean1 ' 22:28:48,738 DEBUG defaultlistablebeanfactory:432-creating instance of beans ' scopedBean2 ' 22:28:48,738 DEBUG defaultlistablebeanfactory : 460-finished creating instance of Bean ' scopedBean2 ' 22:28:48,738 DEBUG defaultlistablebeanfactory:432-creating Instan Ce of Bean ' scopedBean3 ' 22:28:48,738 DEBUG defaultlistablebeanfactory:460-finished Creating instance of beans ' Scopedbean 3 ' 22:28:48,738 DEBUG disposablebeanadapter:227-invoking destroy () on beans with name ' ScopedBean1 ' destroy:[email protected]22:28:48,738 Debug Defaultlistablebeanfactory:432-creating instance of Bean ' scopedBean1 ' 22:28:48,738 debug Defaultlistablebeanfactory:460-finished creating instance of Bean ' scopedBean1 ' 22:28:48,738 DEBUG Disposablebeanadapter:227-invoking destroy () on beans with name ' scopedBean2 ' Destroy:[emaIl protected]
From the log it can be seen that after the creation of SCOPEDBEAN3 and added to the cache ScopedBean1 was kicked off and triggered a destructor callback, we scopedbean implemented the Disposablebean interface, its destroy method was called:
public class Scopedbean implements Disposablebean {@Overridepublic void Destroy () throws Exception {System.out.println (" Destroy: "+ This);}}
Framework Implementation principle
Now a rough look at how custom scopes are implemented
First look at the Customscopeconfigurer class and look at its Postprocessbeanfactory method:
public void Postprocessbeanfactory (Configurablelistablebeanfactory beanfactory) throws Beansexception {if ( This.scopes! = null) {for (map.entry<string, object> entry:this.scopes.entrySet ()) {String Scopekey = Entry.getkey (); Object value = Entry.getvalue (); if (value instanceof Scope) {Beanfactory.registerscope (Scopekey, (Scope) value);} else if (value instanceof Class) {Class Scopeclass = (Class) value; Assert.isassignable (Scope.class, Scopeclass); Beanfactory.registerscope (Scopekey, (Scope) Beanutils.instantiateclass (Scopeclass));} else if (value instanceof String) {Class Scopeclass = Classutils.resolveclassname ((String) value, This.beanclassloader); Assert.isassignable (Scope.class, Scopeclass); Beanfactory.registerscope (Scopekey, (Scope) Beanutils.instantiateclass (Scopeclass));} else {throw new IllegalArgumentException ("Mapped value [" + Value + "] for scope key [" +scopekey + "] was not an instance of required type ["+ Scope.class.getName () +"] or a corresponding class or String ValUE indicating a Scope implementation ");}}}
In this method, register all scopes in Beanfactory, and look at the Registerscope method of the Bean factory, in the Abstractbeanfactory class, In this method, all scope objects are stored in the Scopes Hash table attribute, which is called the hash table key:
public void Registerscope (String scopeName, scope scope) {Assert.notnull (scopeName, "Scope identifier must not being null"); Ssert.notnull (scope, "Scope must not is null"); if (Scope_singleton.equals (scopeName) | | Scope_prototype.equals (ScopeName)) {throw new IllegalArgumentException ("Cannot replace existing scopes ' singleton ' and ' Prototype ');} This.scopes.put (scopeName, scope);}
Next look at the bean fetch method, in the Abstractbeanfactory Dogetbean method, look at the code snippet of the Dogetbean method:
if (Mbd.issingleton ()) {...} else if (Mbd.isprototype ()) {...} else {String scopeName = Mbd.getscope (), final scope scope = This.scopes.get (ScopeName), if (Scope = = null) {throw new Illeg Alstateexception ("No scope registered for scope '" + scopeName + "'");} try {Object scopedinstance = Scope.get (Beanname, New objectfactory<object> () {public Object getObject () throws Bean sexception {beforeprototypecreation (beanname); try {return Createbean (Beanname, mbd, args);} finally {afterprototypecreation (beanname);}}); Bean = getobjectforbeaninstance (scopedinstance, name, Beanname, mbd);} catch (IllegalStateException ex) {throw new Beancreationexception (Beanname, "Scope '" + ScopeName + "is not active for th e current thread; "+" consider defining a scoped proxy for this bean if you intend to refer to it from a singleton ", Ex);}}
You can see that the bean that gets the custom scope calls the Get method of scope, and if the scope is not cached to find the bean, it calls Createbean to create an instance, which is the same logic as the prototype bean that created the bean instance.
Here's a look at the code to register the destructor callback, in the Registerdisposablebeanifnecessary method of the Abstractbeanfactory class, After the bean creation (Abstractautowirecapablebeanfactory Docreatebean method) is complete:
protected void Registerdisposablebeanifnecessary (String beanname, Object Bean, Rootbeandefinition mbd) {AccessControlContext acc = (System.getsecuritymanager () = null? Getaccesscontrolcontext (): Nu ll), if (!mbd.isprototype () && requiresdestruction (Bean, mbd)) {if (Mbd.issingleton ()) {//Register a Disposablebean implementation that performs all destruction//work for the given bean:destructionawarebeanpostprocessors ,//Disposablebean interface, custom Destroy Method.registerdisposablebean (beanname,new disposablebeanadapter (Bean, Beanname, MBD, Getbeanpostprocessors (), ACC));} else {//a bean with A custom scope ... Scope scope = This.scopes.get (Mbd.getscope ()), if (scope = = null) {throw new IllegalStateException ("No scope registered for Scope ' "+ mbd.getscope () +" ' ");} Scope.registerdestructioncallback (beanname,new disposablebeanadapter (Bean, Beanname, MBD, Getbeanpostprocessors (), ACC));}}}
You can see in the code that a custom scope bean is created and a Disposablebeanadapter destructor callback is registered to the scope to see the code of the Disposablebeanadapter class. The Run method is called when the callback is executed in scope, and the Run method calls the Destroy method directly, and the main code is in the Destroy method, As you can see from the code, the destructor callbacks for all the beans executed in the Destroy method include the Destructionawarebeanpostprocessor destructor, Disposablebean destroy, The destroy-method in the bean definition.
public void Destroy () {if (this.beanpostprocessors! = null &&!this.beanpostprocessors.isempty ()) {for ( Destructionawarebeanpostprocessor processor:this.beanPostProcessors) {processor.postprocessbeforedestruction ( This.bean, This.beanname);}} if (This.invokedisposablebean) {if (logger.isdebugenabled ()) {Logger.debug ("invoking Destroy () on beans with name ' + ') . Beanname + "'");} try {if (System.getsecuritymanager ()! = null) {accesscontroller.doprivileged (new Privilegedexceptionaction<object > () {public Object run () throws Exception {((Disposablebean) bean). Destroy (); return null;}}, ACC);} else {(Disposablebean) bean). Destroy ();}} catch (Throwable ex) {String msg = "Invocation of Destroy method failed on beans with name '" + this.beanname + "'"; if (log Ger.isdebugenabled ()) {Logger.warn (msg, ex);} else {Logger.warn (msg + ":" + Ex);}}} if (This.destroymethod! = null) {Invokecustomdestroymethod (this.destroymethod);} else if (this.destroymethodname! = null) {Method Methodtocall =Determinedestroymethod (); if (methodtocall! = null) {Invokecustomdestroymethod (Methodtocall);}}}
Framework customization Scope
Some custom scopes are also defined in the Spring framework:
- The Request:bean of the web framework is shared within the request scope, implementing class Org.springframework.web.context.request.RequestScope
- The Session:bean of the web framework is shared within the session scope, implementing class Org.springframework.web.context.request.SessionScope
- Web framework Application:servletcontextscope,bean is shared in Web applications, Implementing Class Org.springframework.web.context.support.ServletContextScope
- Org.springframework.context.support.SimpleThreadScope:bean in-thread sharing
Scope of Spring Bean