{"id":2382,"date":"2014-01-31T20:37:43","date_gmt":"2014-01-31T19:37:43","guid":{"rendered":"http:\/\/sahits.ch\/blog\/?p=2382"},"modified":"2014-01-31T20:37:43","modified_gmt":"2014-01-31T19:37:43","slug":"custom-spring-scope-done-right","status":"publish","type":"post","link":"http:\/\/sahits.ch\/blog\/blog\/2014\/01\/31\/custom-spring-scope-done-right\/","title":{"rendered":"Custom Spring scope done right"},"content":{"rendered":"<p>You can define your own Spring Scope. One thing to consider is that the beans that are defined in a scope must somehow be backed by a store. For the singlton scope this is the application context itself, for a session scope it is the session, for a request scope it is the request. With this setup the beans lifecycle is bound to the lifecycle of the backing store, e.g. when a session terminates it is deleted and with it all the corresponding beans.<\/p>\n<p>There are <a href=\"http:\/\/www.programmingforliving.com\/2012\/10\/how-to-write-custom-bean-scope-in-spring.html\">other examples<\/a> out there that implement a custom scope by implementing the store in the scope. This then requires that the lifecycle has to be managed from the outside. So let&#8217;s do this the right way:<\/p>\n<ul>\n<li>We want a scope that matches the lifecycle of a dialog. The dialog is opened some content is displayed, some work is done and finally the dialog is closed.<\/li>\n<li>The application code should not be polluted with logic to manage the bean life cycle of the scope.<\/li>\n<\/ul>\n<p><!--more--><\/p>\n<p>To simplify the example let&#8217;s assume that there can only be one dialog at a time. The Scope implementation follows <a href=\"http:\/\/www.programmingforliving.com\/2012\/10\/how-to-write-custom-bean-scope-in-spring.html\">this example<\/a> closely. The main difference is that there are two methods which define the start of the scope boundary and the end. A bean of in this scope may only be created after the start and before the end. This is necessary as the lifecycle of the Scope bean is that of a singleton and therefore the store backing the scope would have the same lifecycle. With designating the start and end the access control to the store can be limited.<\/p>\n<p>So here is the Scope implementation:<\/p>\n<pre class=\"brush:java\">public class DialogScope implements Scope {\r\n    private Map&lt;String, Object&gt; objectMap = Collections.synchronizedMap(new HashMap&lt;String, Object&gt;());\r\n    private volatile boolean scopeOpen = false;\r\n\r\n    public DialogScope() {\r\n        System.out.println(getClass().getName()+\": Creating scope\");\r\n    }\r\n\r\n    \/**\r\n     * Mark the begining of the scope, before the scope is opened no beans are registered.\r\n     *\/\r\n    public void openScope() {\r\n        scopeOpen = true;\r\n        System.out.println(getClass().getName()+\": Opened the scope\");\r\n    }\r\n\r\n    \/**\r\n     * Mark the end of the scope, after the scope is closed, no beans are registered.\r\n     *\/\r\n    public void closeScope() {\r\n        scopeOpen = false;\r\n        clear();\r\n        System.out.println(getClass().getName()+\": Closed the scope\");\r\n    }\r\n\r\n    @Override\r\n    public Object get(String name, ObjectFactory&lt;?&gt; objectFactory) {\r\n        if (scopeOpen &amp;&amp; !objectMap.containsKey(name)) {\r\n            \/\/ Only add the bean if the scope is open and the bean is not yet already registered\r\n            objectMap.put(name, objectFactory.getObject());\r\n        }\r\n        return objectMap.get(name);\r\n    }\r\n\r\n    @Override\r\n    public Object remove(String name) {\r\n        return objectMap.remove(name);\r\n    }\r\n\r\n    @Override\r\n    public void registerDestructionCallback(String name, Runnable callback) {\r\n       \/\/ do nothing\r\n    }\r\n\r\n    @Override\r\n    public Object resolveContextualObject(String key) {\r\n        return null;\r\n    }\r\n\r\n    \/**\r\n     * Clear the cached beans in the scopes backing store\r\n      *\/\r\n    private void clear() {\r\n        objectMap.clear();\r\n        System.out.println(getClass().getName()+\": Clear scope\");\r\n    }\r\n\r\n    @Override\r\n    public String getConversationId() {\r\n        return \"Dialog\";\r\n    }\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<p>And the configuration in the spring context.<\/p>\n<p>&nbsp;<\/p>\n<pre class=\"brush:xml\">    &lt;bean id=\"dialogScope\" class=\"ch.sahits.spring.DialogScope\" \/&gt;\r\n\r\n    &lt;bean class=\"org.springframework.beans.factory.config.CustomScopeConfigurer\"&gt;\r\n        &lt;property name=\"scopes\"&gt;\r\n            &lt;map&gt;\r\n                &lt;entry key=\"dialog\"&gt;\r\n                    &lt;ref bean=\"dialogScope\" \/&gt;\r\n                &lt;\/entry&gt;\r\n            &lt;\/map&gt;\r\n        &lt;\/property&gt;\r\n    &lt;\/bean&gt;<\/pre>\n<p>Up until now this is nothing really novel. The clever bit comes now. It&#8217;s not terribly complicated but I have not seen any example that combine these two techniques to that effect: We are using Aspect Oriented Programming to control the scope. Somewhere in the application code there will be a method opening a dialog and a method closing the dialog. These are the two points we want to connect to the start and end of the scope. I have chosen to mark these methods with custom annotations. This is not really necessary as one can also use the direct method name, however there are two potential pitfalls:<\/p>\n<ul>\n<li>When refactoring the method names or the defining class this is not reflected in the aspect<\/li>\n<li>What if there is not a single method that opens or closes the dialog?<\/li>\n<\/ul>\n<p>So here are the two annotations:<\/p>\n<pre class=\"brush:java\">@Retention(RetentionPolicy.RUNTIME)\r\n@Target(ElementType.METHOD)\r\npublic @interface DialogOpening {\r\n}\r\n\r\n@Retention(RetentionPolicy.RUNTIME)\r\n@Target(ElementType.METHOD)\r\npublic @interface DialogCloseing {\r\n}<\/pre>\n<p>Now the most complex part is the definition of the aspect:<\/p>\n<pre class=\"brush:java\">@Aspect\r\npublic class DialogAspect {\r\n    @Autowired\r\n    private DialogScope scope;\r\n\r\n    @Pointcut(value=\"execution(public * *(..))*\")\r\n    public void anyPublicMethod() {\r\n    }\r\n    @Before(\"anyPublicMethod() &amp;&amp; @annotation(dialogOpening)\")\r\n    public void openDialog(JoinPoint pjp, DialogOpening dialogOpening) throws Throwable {\r\n        System.out.println(getClass().getName()+\": In opening aspect:\");\r\n        for (java.lang.Object o : pjp.getArgs()) {\r\n            System.out.println(o);\r\n        }\r\n        scope.openScope();\r\n    }\r\n    @After(\"anyPublicMethod() &amp;&amp; @annotation(dialogCloseing)\")\r\n    public void openDialog(JoinPoint pjp, DialogCloseing dialogCloseing) throws Throwable {\r\n        System.out.println(getClass().getName()+\": In closing aspect:\");\r\n        for (java.lang.Object o : pjp.getArgs()) {\r\n            System.out.println(o);\r\n        }\r\n        scope.closeScope();\r\n    }\r\n}<\/pre>\n<p>The aspect takes nicely care of starting the scope before the dialog is opened and ending it again on closing.<\/p>\n<p>And finally there is some configuration needed in the spring context.<\/p>\n<pre class=\"brush:xml\">&lt;aop:aspectj-autoproxy \/&gt;\r\n\r\n&lt;bean id=\"DialogScopeAspect\" class=\"ch.sahits.spring.aop.DialogAspect\" \/&gt;<\/pre>\n<p>There are some dependencies that are required here defined in the pom.xml:<\/p>\n<pre class=\"brush:xml\">&lt;dependency&gt;\r\n    &lt;groupId&gt;org.springframework&lt;\/groupId&gt;\r\n    &lt;artifactId&gt;spring&lt;\/artifactId&gt;\r\n    &lt;version&gt;3.2.0.RELEASE&lt;\/version&gt;\r\n&lt;\/dependency&gt;\r\n&lt;dependency&gt;\r\n    &lt;groupId&gt;org.springframework&lt;\/groupId&gt;\r\n    &lt;artifactId&gt;spring-beans&lt;\/artifactId&gt;\r\n    &lt;version&gt;3.2.0.RELEASE&lt;\/version&gt;\r\n&lt;\/dependency&gt;\r\n&lt;dependency&gt;\r\n    &lt;groupId&gt;org.springframework&lt;\/groupId&gt;\r\n    &lt;artifactId&gt;spring-core&lt;\/artifactId&gt;\r\n    &lt;version&gt;3.2.0.RELEASE&lt;\/version&gt;\r\n&lt;\/dependency&gt;\r\n&lt;dependency&gt;\r\n    &lt;groupId&gt;org.springframework&lt;\/groupId&gt;\r\n    &lt;artifactId&gt;spring-context&lt;\/artifactId&gt;\r\n    &lt;version&gt;3.2.0.RELEASE&lt;\/version&gt;\r\n&lt;\/dependency&gt;\r\n&lt;dependency&gt;\r\n    &lt;groupId&gt;org.springframework&lt;\/groupId&gt;\r\n    &lt;artifactId&gt;spring-aop&lt;\/artifactId&gt;\r\n    &lt;version&gt;3.2.0.RELEASE&lt;\/version&gt;\r\n&lt;\/dependency&gt;\r\n&lt;dependency&gt;\r\n    &lt;groupId&gt;org.aspectj&lt;\/groupId&gt;\r\n    &lt;artifactId&gt;aspectjrt&lt;\/artifactId&gt;\r\n    &lt;version&gt;1.6.11&lt;\/version&gt;\r\n&lt;\/dependency&gt;\r\n\r\n&lt;dependency&gt;\r\n    &lt;groupId&gt;org.aspectj&lt;\/groupId&gt;\r\n    &lt;artifactId&gt;aspectjweaver&lt;\/artifactId&gt;\r\n    &lt;version&gt;1.6.11&lt;\/version&gt;\r\n&lt;\/dependency&gt;        &lt;dependency&gt;\r\n    &lt;groupId&gt;javax.xml.bind&lt;\/groupId&gt;\r\n    &lt;artifactId&gt;jaxb-api&lt;\/artifactId&gt;\r\n    &lt;version&gt;2.1&lt;\/version&gt;\r\n&lt;\/dependency&gt;\r\n&lt;dependency&gt;\r\n    &lt;groupId&gt;com.sun.xml.bind&lt;\/groupId&gt;\r\n    &lt;artifactId&gt;jaxb-impl&lt;\/artifactId&gt;\r\n    &lt;version&gt;2.1.13&lt;\/version&gt;\r\n&lt;\/dependency&gt;\r\n&lt;dependency&gt;\r\n    &lt;groupId&gt;org.springframework&lt;\/groupId&gt;\r\n    &lt;artifactId&gt;spring-oxm&lt;\/artifactId&gt;\r\n    &lt;version&gt;3.2.0.RELEASE&lt;\/version&gt;\r\n&lt;\/dependency&gt;\r\n&lt;dependency&gt;\r\n    &lt;groupId&gt;org.springframework&lt;\/groupId&gt;\r\n    &lt;artifactId&gt;spring-test&lt;\/artifactId&gt;\r\n    &lt;version&gt;3.2.0.RELEASE&lt;\/version&gt;\r\n&lt;\/dependency&gt;\r\n&lt;dependency&gt;\r\n    &lt;groupId&gt;junit&lt;\/groupId&gt;\r\n    &lt;artifactId&gt;junit&lt;\/artifactId&gt;\r\n    &lt;version&gt;4.11&lt;\/version&gt;\r\n&lt;\/dependency&gt;<\/pre>\n<p>Finally we want to see if that all worked out. We can do this with this nice little Test.<\/p>\n<pre class=\"brush:java\">@Test\r\npublic void testScope() {\r\n\tClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(\"spring-context.xml\");\r\n\tWindowOpening instance = context.getBean(WindowOpening.class);\r\n\r\n\tassertNull(\"Dialog scoped bean Location may not be available before scope is started\", context.getBean(Location.class));\r\n\tinstance.openDialog(\"GenericDialog\");\r\n\tassertNotNull(\"Dialog scoped bean Location must be available after scope is started\", context.getBean(Location.class));\r\n\tThread.currentThread();\r\n\ttry {\r\n\t    Thread.sleep(5000);\r\n\t} catch (InterruptedException e) {\r\n\t    e.printStackTrace();\r\n\t}\r\n\tinstance.closeDialog(\"GenericDialog\");\r\n\tassertNull(\"Dialog scoped bean Location may not be available after scope is closed\", context.getBean(Location.class));\r\n}<\/pre>\n<p>As the asserts show the Location bean which is defined with the dialog scope is only present after the dialog is opened before it is closed again.<\/p>\n<p>The output of the various sysouts gives us this:<\/p>\n<pre class=\"brush:plain\">ch.sahits.spring.DialogScope: Creating scope\r\nch.sahits.spring.aop.DialogAspect: In opening aspect:\r\nGenericDialog\r\nch.sahits.spring.DialogScope: Opened the scope\r\nch.sahits.spring.WindowOpening: Opened a dialog: GenericDialog\r\nCreating the location\r\nch.sahits.spring.WindowOpening: Close a dialog: GenericDialog\r\nch.sahits.spring.aop.DialogAspect: In closing aspect:\r\nGenericDialog\r\nch.sahits.spring.DialogScope: Clear scope\r\nch.sahits.spring.DialogScope: Closed the scope<\/pre>\n<p>The annotation approach is largely inspired by <a href=\"http:\/\/eggsylife.co.uk\/2010\/02\/03\/spring-annotation-based-aop-and-intercepting-the-ball\/\">this article.<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>You can define your own Spring Scope. One thing to consider is that the beans that are defined in a scope must somehow be backed by a store. For the singlton scope this is the application context itself, for a session scope it is the session, for a request scope it is the request. With &hellip; <a href=\"http:\/\/sahits.ch\/blog\/blog\/2014\/01\/31\/custom-spring-scope-done-right\/\" class=\"more-link\"><span class=\"screen-reader-text\">\u201eCustom Spring scope done right\u201c <\/span>weiterlesen<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[291,290,269],"class_list":["post-2382","post","type-post","status-publish","format-standard","hentry","category-java","tag-aspect","tag-scope","tag-spring"],"_links":{"self":[{"href":"http:\/\/sahits.ch\/blog\/wp-json\/wp\/v2\/posts\/2382","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/sahits.ch\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/sahits.ch\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/sahits.ch\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/sahits.ch\/blog\/wp-json\/wp\/v2\/comments?post=2382"}],"version-history":[{"count":3,"href":"http:\/\/sahits.ch\/blog\/wp-json\/wp\/v2\/posts\/2382\/revisions"}],"predecessor-version":[{"id":2385,"href":"http:\/\/sahits.ch\/blog\/wp-json\/wp\/v2\/posts\/2382\/revisions\/2385"}],"wp:attachment":[{"href":"http:\/\/sahits.ch\/blog\/wp-json\/wp\/v2\/media?parent=2382"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/sahits.ch\/blog\/wp-json\/wp\/v2\/categories?post=2382"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/sahits.ch\/blog\/wp-json\/wp\/v2\/tags?post=2382"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}