Tuesday, October 29, 2013

Spring Çatısında Özyinelemeli Metotlar İçin İlgiye Yönelik Programlama Çözümü

Kaliteli yazılım geliştirmek için Nesneye Dayalı Programlama, İlgiye Dayalı Programlama, Üretken Programlama, Fonksiyonel Programlama gibi birçok yaklaşımı beraber kullanmak gerekir. Bu yaklaşımlar genellikle biri birilerine rakip teknikler olmayıp diğerinin eksikliğini ya da yetersizliğini kapatırlar. Sınıflar arasındaki bağı zayıflatmak için istemci ve sunucu rollerindeki sınıflar arasında bir arayüz tanımlıyoruz:
public interface SequenceService {
      long elementAt(int n);
}  
Bu arayüzü gerçekleyen servis rolündeki bir sınıfı Spring çatısında aşağıdaki gibi gerçekleştiriyoruz:
@Service("fibonacci")
public class FibonacciService implements SequenceService {
      @Override
      public long elementAt(int n) {
            if (n == 0 || n == 1)
                  return n;
            return elementAt(n - 1) + elementAt(n - 2);
      }
}
FibonacciService sınıfı Fibonacci dizisinin n-yinci değerini özyinelemeli bir şekilde hesaplamaktadır. Bu sınıfın örnek bir uygulamasını aşağıda bulabilirsiniz:
public class TestFibonacciService {
   public static void main(String[] args) {
       ConfigurableApplicationContext context=
              new ClassPathXmlApplicationContext("spring-config.xml");
       int n=100;
       SequenceService sequenceService= 
                       (SequenceService) context.getBean("fibonacci");
       System.out.println("Fibonacci("+n+") is " 
                          sequenceService.elementAt(n));
       context.close();
   }
}
Bu kodu çalıştırdığınızda, uygulamanın kısa zamanda sonlanmadığını fark edeceksiniz. Fibonacci dizisinin yukarıdaki gibi hesaplanması durumunda, işlem sayısı O(2n) biçiminde artmaktadır. Ancak dikkatlice bakıldığında aynı parametreli çağrıların defalarca yapıldığı rahatlıkla görülebilinir. Çözüm için ön bellek kullanılır. Aynı parametreli çağrılardan ilkinin sonucu ön belleğe atılır ve ondan sonraki çağrılarda ise ön bellekteki sonuç kullanılır. Ön bellek çözümü Spring tarafından sağlanmaktadır. Ön bellekleme için sınıfın ilgili metoduna @Cacheable notunu düşmek yeterli olur:
@Service("fibonacci")
public class FibonacciService implements SequenceService {

      @Cacheable(value = "numbers", key = "#n")
      @Override
      public long elementAt(int n) {
            if (n == 0 || n == 1)
                return n;
            return elementAt(n - 1) + elementAt(n - 2);
      }

}
Yukarıdaki örnek uygulamayı tekrar çalıştırdığınızda sürenin değişmediğini göreceksiniz. İlgiye yönelik programlama çözümü Java platformunda dinamik vekil kalıbı kullanılarak sağlanır. Bu kalıpta çağrıyı yapan ile sağlayıcı sınıf arasına bir vekil girer. Her vekilin ilgilendiği bir sorumluluk vardır. Bu sorumluluk günlük oluşturma, yetkilendirme, başarım ölçümü ya da ön bellekleme olabilir. Bu kalıp, sınıfın bir metodunun sınıfın bir başka metodunu çağırdığı durumlarda ya da özyinelemeli çağrılarda işe yaramaz. Bu problem, sınıfa vekilin referansını ekleyerek ve metot çağrısını bu referans üzerinden yaparak aşılabilir:
import javax.annotation.PostConstruct;

import org.springframework.beans.BeansException;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;

@Service("fibonacci")
public class FibonacciService implements SequenceService, ApplicationContextAware {
  private ApplicationContext context;
  private SequenceService self;

  @Cacheable(value = "numbers", key = "#n")
  @Override
  public long elementAt(int n) {
    if (n == 0 || n == 1)
       return n;
    return self.elementAt(n - 1) + self.elementAt(n - 2);
  }

  @PostConstruct
  private void init() {
     self = (SequenceService) context.getBean("fibonacci");
  }

  @Override
  public void setApplicationContext(ApplicationContext context) throws BeansException {
     this.context = context;
  }
}
Spring yapılandırma dosyası, spring-config.xml, ise aşağıda verilmiştir:
<?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"
      xmlns:context="http://www.springframework.org/schema/context"
      xmlns:cache="http://www.springframework.org/schema/cache"
      xmlns:p="http://www.springframework.org/schema/p"
      xsi:schemaLocation="http://www.springframework.org/schema/aop 
         http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
         http://www.springframework.org/schema/beans 
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/cache 
         http://www.springframework.org/schema/cache/spring-cache-3.2.xsd
         http://www.springframework.org/schema/context 
   http://www.springframework.org/schema/context/spring-context-3.2.xsd">
      <aop:aspectj-autoproxy />
      <context:annotation-config />
      <cache:annotation-driven />
      <context:component-scan base-package="com.example.mod06.service" />
      <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
        <property name="caches">
           <set>      <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default" />
 <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="numbers" />
           </set>
        </property>
    </bean>
</beans>
Yukarıdaki örnek uygulamayı tekrar çalıştırdığınızda uygulamanın hemen sonuca hızlıca ulaştığını göreceksiniz:
Oct 29, 2013 11:31:14 AM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@561ba49d: startup date [Tue Oct 29 11:31:14 EET 2013]; root of context hierarchy
Oct 29, 2013 11:31:14 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-config.xml]
Oct 29, 2013 11:31:14 AM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@7b70a0d3: defining beans [org.springframework.aop.config.internalAutoProxyCreator,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.cache.annotation.AnnotationCacheOperationSource#0,org.springframework.cache.interceptor.CacheInterceptor#0,org.springframework.cache.config.internalCacheAdvisor,auditAspect,cache,accountService,fibonacci,memoryAccountDao,cacheManager,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy
fibonacci(100) is 3736710778780434371
Oct 29, 2013 11:31:15 AM org.springframework.context.support.AbstractApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@561ba49d: startup date [Tue Oct 29 11:31:14 EET 2013]; root of context hierarchy
Oct 29, 2013 11:31:15 AM org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletons

INFO: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@7b70a0d3: defining beans [org.springframework.aop.config.internalAutoProxyCreator,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.cache.annotation.AnnotationCacheOperationSource#0,org.springframework.cache.interceptor.CacheInterceptor#0,org.springframework.cache.config.internalCacheAdvisor,auditAspect,cache,accountService,fibonacci,memoryAccountDao,cacheManager,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy

Çözümün kaynak koduna Maven projesi olarak bu bağlantıdan erişebilirsiniz. Yukarıdaki çözüm bizi amacımıza ulaştırmış olsa da çok temel bir tasarım problemi barındırmaktadır. FibonacciService sınıfı Spring çatısı ve özellikle AspectJ ile ilgili bilgi içermektedir. Spring, ilgiye yönelik programlamada her zaman vekil tabanlı bir çözüm sunar. Bu yüzden de öz yinelemeli çağrılar için yukarıda verilen yaklaşım dışında bir çözüm bulunmamaktadır. AspectJ ise daha farklı bir yaklaşım kullanır. Yazdığımız sınıflara, derleme aşamasında ya da çalıştırma zamanında sınıfları yüklerken, ilgileri sınıfa dokuyarak değişiklikler yapar. Eğer bu değişiklik sınıf yükleme aşamasında yapılıyorsa, buna Yükleme Zamanında Dokuma (=Load-Time Weaving) adını veriyoruz. Spring çatısında bunun nasıl uygulandığına bir bakalım. Önce Spring yapılandırma dosyasında (spring-config.xml) bununla ilgili bir tanımlama yapmamız gerekecek:

<?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:context="http://www.springframework.org/schema/context"
     xmlns:aop="http://www.springframework.org/schema/aop"
     xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  <context:annotation-config/>
  <context:component-scan base-package="com.example.service"/>
  <bean id="cache" class="com.example.aop.CachingAspect"/>
  <context:load-time-weaver/>
  <aop:aspectj-autoproxy proxy-target-class="true">
    <aop:include name="cache"/>
  </aop:aspectj-autoproxy>

</beans>

İkinci olarak AspectJ için bir yapılandırma dosyası ekliyoruz, META-INF/aop.xml:

<?xml version="1.0" encoding="UTF-8"?>
<aspectj >
     <weaver options="-Xreweavable -verbose">
           <include within="com.example.service.*" />
           <include within="com.example.aop.*" />
     </weaver>
     <aspects>
           <aspect name="com.example.aop.CachingAspect" />
     </aspects>
</aspectj>

Cep bellek ilgisini ise basitçe AspectJ damgalarını kullanarak aşağıdaki gibi kodluyoruz:


package com.example.aop;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class CachingAspect {
     private Map<Integer,Object> cache= new ConcurrentHashMap<>();
     @Around("methodsToBeCached()")
     public Object cache(ProceedingJoinPoint pjp) throws Throwable {
        Object[] params= pjp.getArgs();
        int key= 0;
        if (params!=null && params.length>0){
             key= params[0].hashCode();
        }
        Object value= cache.get(key);
        if (value==null){
           value=  pjp.proceed();
           cache.put(key, value);
        }
        return value;
     }
     @Pointcut("execution(public * com.example..*Service.*(..))")
     public void methodsToBeCached() {
     }
}

Son olarak, bu uygulamayı çalıştırırken, sınıf yüklenirken bu dokuma işini yapacak temsilciyi Java Sanal Makinesine tanıtmak gerekecektir:

java -javaagent:aspectjweaver-1.7.2.jar -javaagent:spring-instrument-3.2.4.RELEASE.jar com.example.App

Uygulama çalıştırıldığında aşağıdaki ekran çıktısını üretmektedir:

[AppClassLoader@65fe28a7] info AspectJ Weaver Version 1.7.2 built on Friday Feb 15, 2013 at 18:40:45 GMT
[AppClassLoader@65fe28a7] info register classloader sun.misc.Launcher$AppClassLoader@65fe28a7
[AppClassLoader@65fe28a7] info using configuration /C:/workspace/workspace-spring-core-oct-2013-thy/aop-aspectj-ltw/target/classes/META-INF/aop.xml
[AppClassLoader@65fe28a7] info using configuration file:/C:/Users/bkurt/.m2/repository/org/springframework/spring-aspects/3.2.4.RELEASE/spring-aspects-3.2.4.RELEASE.jar!/META-INF/aop.xml
[AppClassLoader@65fe28a7] info register aspect com.example.aop.CachingAspect
[AppClassLoader@65fe28a7] info register aspect org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect
[AppClassLoader@65fe28a7] info register aspect org.springframework.scheduling.aspectj.AnnotationAsyncExecutionAspect
[AppClassLoader@65fe28a7] info register aspect org.springframework.transaction.aspectj.AnnotationTransactionAspect
[AppClassLoader@65fe28a7] info register aspect org.springframework.cache.aspectj.AnnotationCacheAspect
[AppClassLoader@65fe28a7] info weaver operating in reweavable mode.  Need to verify any required types exist.
.
.
.
Nov 01, 2013 9:20:02 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@51b30472: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,fibonacci,cache,org.springframework.context.weaving.AspectJWeavingEnabler#0,org.springframework.context.config.internalBeanConfigurerAspect,loadTimeWeaver,org.springframework.aop.config.internalAutoProxyCreator,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy
[AppClassLoader@65fe28a7] info processing reweavable type com.example.aop.CachingAspect: com\example\aop\CachingAspect.java
[AppClassLoader@65fe28a7] info successfully verified type com.example.aop.CachingAspect exists.  Originates from com\example\aop\CachingAspect.java
Nov 01, 2013 9:20:02 PM org.springframework.context.support.AbstractApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@74eb011d: startup date [Fri Nov 01 21:20:01 EET 2013]; root of context hierarchy
fibonacci(100)=3736710778780434371
Nov 01, 2013 9:20:02 PM org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletons
INFO: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@51b30472: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,fibonacci,cache,org.springframework.context.weaving.AspectJWeavingEnabler#0,org.springframework.context.config.internalBeanConfigurerAspect,loadTimeWeaver,org.springframework.aop.config.internalAutoProxyCreator,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy
Nov 01, 2013 9:20:02 PM org.springframework.context.weaving.DefaultContextLoadTimeWeaver destroy
INFO: Removing all registered transformers for class loader: sun.misc.Launcher$AppClassLoader

Bu uygulamayı Maven projesi olarak bu bağlantıdan indirebilirsiniz. Spring konusunda daha fazla bilgi edinmek isterseniz bu bağlantıdaki Spring sınıf eğitimlerini tavsiye ederim.

Saturday, October 19, 2013

Spring Çatısına Kendi Injector Sınıfımızı Eklemek

Nesneye dayalı yaklaşım ile tasarım yapıldığında uyulması tavsiye edilen bir dizi ilke vardır. Bu ilkelerden biri yüksek uyum ve zayıf bağdır (=High Cohesion-Low Coupling). Uyumluluk bir sınıfın metotları için tanımlanır. Eğer sınıfın metotları aynı problemin çözümüne odaklanmışlar ise sınıfın metotları arasında bir uyumdan bahsedilir. Yazdığımız her sınıfa tek bir sorumluluk verirsek ya da tek bir konuda uzmanlaşmasını sağlarsak sınıf metotları arasında yüksek uyum sağlamış oluruz. Böylelikle sınıfı değiştirmek için tek bir nedenimiz olur. Ancak yüksek uyumluluk sağlamaya çalışırken, ortaya başka bir problem çıkar. Bir sınıf metodu işini yaparken kendi uzmanlığı dışında bir iş yapması gerektiğinde, bu işi uzmanına ihale edecektir. Bu durumda sınıflar arasında hizmet alma ve verme ilişkisi ortaya çıkar. Bir sınıf hem istemci hem de sunucu rolüne sahip olabilir. İstemci sınıf hizmet alacağı sınıfın referansını içerir. Böylelikle sınıflar arasında bağımlılıklar ortaya çıkacaktır. Bu bağımlılığı yönetebilmek gerekir. Bu bağımlılıktan dolayı oluşan bağın zayıf olmasını sağlamak üzere istemci sınıf hizmet alacağı sınıfın referansını doğrudan içermez. Bunun yerine istemci ile sunucu rollerindeki sınıflar arasında bir arayüz/sözleşme tanımlanır. Sınıflar bir birlerini doğrudan görmezler. İstemci sınıf bu sözleşmeye göre hizmet alır, servis sınıfı da bu sözleşmeye bağlı kalarak hizmet verir. İstemci sınıf ileride kodda değişiklik yapmadan hizmet alacağı sınıfı değiştirebilir. Nasıl cep telefon numaranızı değiştirmeden GSM operatörünüzü değiştirebiliyorsanız ya da internet servis sağlayıcınızı değiştirdiğinizde ADSL modeminizi değiştirmeniz gerekmiyorsa, istemci sınıf da hizmet aldığı sınıfı değiştirdiğinde kodda değişiklik yapması gerekmemelidir. Sözleşmeyi soyut sınıf ya da genellikle arayüz ile tanımlıyoruz. Bu şekilde istemci ve sunucu sınıflar arasındaki bağı gevşek tutmuş oluyoruz. Bu bağı daha da gevşetmenin yolu Java ya da C# programlama dillerindeki damgalama/not düşme yeteneğini kullanmaktır. Bu yaklaşımı genellikle bileşen tabanlı yazılım geliştirmede kullanıyoruz. Bileşenler aynı sınıflardan yarattığımız nesneler gibidir. Ancak onlardan farklı olarak yaşayabilmeleri, var olabilmeleri için bir barınağa ya da kaba (=container) ihtiyaç duyarlar. Barınak bileşenlerin yaşam döngülerini yönetir ve bazı sık kullanılan hizmetler sunar. Kurumsal Java uygulamalarındaki uygulama sunucuları Web kabı ve EJB kabı olmak üzere iki kap ve bileşen modeli içerir. Web kabında Servlet ve JSP bileşen modelleri yer alır. EJB kabında ise Session Bean, Message Driven Bean bileşen modelleri bulunur. Bu bileşenler, uygulama sunucusunun sağladığı hizmetlerden standart bir dizi damga yardımıyla yararlanır.
Arayüz kullanarak bağı zayıflatmış olsak da yürütme zamanında istemci sınıfa hangi sınıftan hizmet alacağını söylememiz gerekir. Yani bağımlılıkların çözülmesi gerekir. Karmaşık sınıf ilişkilerinin olduğu bir yazılımda bağımlılıkları görmek ve düzgün sırada bağımlıkları çözmek zordur. Bunun için genellikle Fabrika Kalıbı kullanılır. Karmaşık nesneleri yaratma sorumluluğu bu fabrika sınıflarına verilir. Ancak bu durumda çok sayıda fabrika tasarlamak gerekir. Üstelik nesnelerin  bağlantıları değiştirildiğinde fabrika sınıfını değiştirmek ya da yeni bir fabrika sınıfı yazmak gerekecektir. Spring çatısı bu bağları otomatik olarak çözebilen genelleştirilmiş bir fabrika sunar. Bu fabrika her türlü sınıftan nesneyi tüm bağımlılıkları ile birlikte yaratabilme yeteneğine sahiptir. Bunun dışında istersek biz de kendi Injector sınıfımızı yazmamıza imkan verir. Bu yazının konusu bu tür bir Injector sınıfının nasıl yazılacağı ve Spring IoC kabına tanıtılması ile ilgilidir.
Bunun için org.springframework.beans.factory.config.BeanPostProcessor arayüzünü gerçekleyen bir sınıf, com.example.mod04.processors.RandomPostProcessor, tasarlıyoruz:

1:  package com.example.mod04.processors;  
2:    
3:  import com.example.mod04.annotation.RandomNumberGenerator;  
4:  import org.springframework.beans.BeansException;  
5:  import org.springframework.beans.factory.config.BeanPostProcessor;  
6:    
7:  public class RandomPostProcessor implements BeanPostProcessor {  
8:    
9:       @Override  
10:       public Object postProcessAfterInitialization(Object bean, String beanname) throws BeansException {  
11:            RandomNumberGenerator.inject(bean);  
12:            return bean;  
13:       }  
14:    
15:       @Override  
16:       public Object postProcessBeforeInitialization(Object bean, String beanname) throws BeansException {  
17:            return bean;  
18:       }  
19:    
20:  }  

Buradaki RandomRandomGenerator sınıfının kodunu aşağıda bulabilirsiniz:
1:  package com.example.mod04.annotation;  
2:    
3:  import java.lang.reflect.*;  
4:  import java.util.Collections;  
5:  import java.util.List;  
6:    
7:  public class RandomNumberGenerator {  
8:       public static int random(int min, int max) {  
9:            int len = max - min + 1;  
10:            return (int) (Math.random() * len + min);  
11:       }  
12:    
13:       public static void inject(Object obj) {  
14:            Class<? extends Object> cls = obj.getClass();  
15:            for (Field field : cls.getDeclaredFields()) {  
16:                 if (field.isAnnotationPresent(Random.class)) {  
17:                      Class<? extends Object> type = field.getType();  
18:                      Random random = field.getAnnotation(Random.class);  
19:                      int minValue = random.minValue();  
20:                      int maxValue = random.maxValue();  
21:                      int size = random.size();  
22:                      boolean sorted = random.sorted();  
23:                      boolean unique = random.unique();  
24:                      field.setAccessible(true);  
25:                      if (type.isPrimitive()) {  
26:                           try {  
27:                                field.set(obj, random(minValue, maxValue));  
28:                           } catch (IllegalArgumentException | IllegalAccessException e) {  
29:                                e.printStackTrace();  
30:                                return;  
31:                           }  
32:                      }   
33:                      else if (type.equals(List.class)) {  
34:                           List<Integer> list;  
35:                           try {  
36:                                list = (List<Integer>) field.get(obj);  
37:                           } catch (IllegalArgumentException | IllegalAccessException e) {  
38:                                e.printStackTrace();  
39:                                return;  
40:                           }  
41:                           for (int i = 0; i < size; ++i) {  
42:                                int candidate;  
43:                                do {  
44:                                     candidate = random(minValue, maxValue);  
45:                                     if (!unique)  
46:                                          break;  
47:                                } while (list.contains(candidate));  
48:                                list.add(candidate);  
49:                           }  
50:                           if (sorted)  
51:                                Collections.sort(list);  
52:                      }  
53:                 }  
54:            }  
55:       }  
56:  }  

Random damgasının tanımı aşağıda verilmiştir. Random damgası sadece sınıf özniteliklerine uygulanabilir, yürütme zamanında tip bilgisine, Class sınıfı üzerinden erişilebilinir. Random damgasının minValue, maxValue, unique, sorted, size öznitelikleri ile rastgele sayı üretilmesi sağlanmaktadır. Bu damgayı RandomNumberGenerator hizmet sınıfı kullanmaktadır. Bu sınıfın tek bir metodu bulumaktadır: inject. Bu metot parametre olarak Object sınıfından bir referans alır. java.lang.reflect paketini kullanarak nesnenin tip bilgisine erişerek, Random damgası ile damgalanmış bir özniteliği olup olmadığını sorgular. Bu damga ile damgalanmış bir özniteliği varsa damganın minValuemaxValueuniquesortedsize değerlerine uygun olarak bir rastgele değer belirler ve  nesnenin özniteliğinin değerini bu değer ile değiştirir. Random damgasının minValuemaxValueuniquesortedsize değerlerinin hepsi için bir varsayılan değer tanımlanmıştır. Dolayısı ile sadece @Random() ile damgalama yapılabilinir.
1:  package com.example.mod04.annotation;  
2:    
3:  import java.lang.annotation.Documented;  
4:  import java.lang.annotation.ElementType;  
5:  import java.lang.annotation.Retention;  
6:  import java.lang.annotation.RetentionPolicy;  
7:  import java.lang.annotation.Target;  
8:  @Documented  
9:  @Target(ElementType.FIELD)  
10:  @Retention(RetentionPolicy.RUNTIME)  
11:  public @interface Random {  
12:       int minValue() default 1;  
13:       int maxValue() default 100;  
14:       boolean unique() default false;  
15:       boolean sorted() default false;  
16:       int size() default 1;   
17:  }  
Şimdi bir kaç Random damgası ile damgalanmış sınıf örneğine bakalım:
1:  package com.example.mod04.domain;  
2:  import com.example.mod04.annotation.Random;  
3:    
4:    
5:  public class Circle {  
6:       @Random() private int x;  
7:       @Random() private int y;  
8:       @Random(minValue=20,maxValue=40) private int radius;  
9:         
10:       public Circle() {            
11:       }  
12:         
13:       public Circle(int x, int y, int radius) {  
14:            this.x = x;  
15:            this.y = y;  
16:            this.radius = radius;  
17:       }  
18:    
19:       public int getX() {  
20:            return x;  
21:       }  
22:       public void setX(int x) {  
23:            this.x = x;  
24:       }  
25:       public int getY() {  
26:            return y;  
27:       }  
28:       public void setY(int y) {  
29:            this.y = y;  
30:       }  
31:       public int getRadius() {  
32:            return radius;  
33:       }  
34:       public void setRadius(int radius) {  
35:            this.radius = radius;  
36:       }  
37:    
38:       @Override  
39:       public String toString() {  
40:            return "Circle [x=" + x + ", y=" + y + ", radius=" + radius + "]";  
41:       }  
42:  }      

1:  package com.example.mod04.domain;  
2:    
3:  import java.util.ArrayList;  
4:  import java.util.List;  
5:    
6:  import com.example.mod04.annotation.Random;  
7:    
8:  public class Lottery extends Object {  
9:       @Random(minValue=1,maxValue=49,size=6,unique=true,sorted=true)   
10:       private List<Integer> numbers;  
11:    
12:       public Lottery() {  
13:            numbers= new ArrayList<Integer>();  
14:       }  
15:    
16:       @Override  
17:       public String toString() {  
18:            return "Lottery "+numbers;  
19:       }  
20:    
21:       public List<Integer> getNumbers() {  
22:            return numbers;  
23:       }  
24:  }  
Spring IoC kabının yapılandırma dosyası, spring-config.xml, ise aşağıdaki gibi oluşturulmalıdır:
1:  <?xml version="1.0" encoding="UTF-8"?>  
2:  <beans xmlns="http://www.springframework.org/schema/beans"  
3:       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
4:       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">  
5:     <bean class="com.example.mod04.processors.RandomPostProcessor"/>  
6:     <bean id="cember" class="com.example.mod04.domain.Circle"/>  
7:     <bean id="sayisal" class="com.example.mod04.domain.Lottery"/>   
8:  </beans>  
Örnek uygulama ve çıktısı aşağıda verilmiştir:
1:  package com.example.mod04.console;  
2:    
3:  import com.example.mod04.domain.Circle;  
4:  import com.example.mod04.domain.Lottery;  
5:    
6:  import org.springframework.context.ConfigurableApplicationContext;  
7:  import org.springframework.context.support.ClassPathXmlApplicationContext;  
8:    
9:  public class TestRandomPostProcessor {  
10:    
11:       /**  
12:        * @param args  
13:        */  
14:       public static void main(String[] args) {  
15:            ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");  
16:            Circle circle= context.getBean("cember",Circle.class);  
17:            System.out.println(circle);  
18:            Lottery sayisal= context.getBean("sayisal",Lottery.class);  
19:            System.out.println(sayisal);  
20:            context.close();  
21:       }  
22:  }  

 Oct 19, 2013 12:02:21 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh  
 INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@17ac9cff: startup date [Sat Oct 19 12:02:21 EEST 2013]; root of context hierarchy  
 Oct 19, 2013 12:02:21 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions  
 INFO: Loading XML bean definitions from class path resource [beans.xml]  
 Oct 19, 2013 12:02:21 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons  
 INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@7393ccd5: defining beans [com.example.mod04.processors.RandomPostProcessor#0,cember,sayisal]; root of factory hierarchy  
 postProcessAfterInitialization()  
 postProcessAfterInitialization()  
 Circle [x=50, y=17, radius=33]  
 Lottery [7, 14, 23, 34, 35, 43]  
 Oct 19, 2013 12:02:21 PM org.springframework.context.support.AbstractApplicationContext doClose  
 INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@17ac9cff: startup date [Sat Oct 19 12:02:21 EEST 2013]; root of context hierarchy  
 Oct 19, 2013 12:02:21 PM org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletons  
 INFO: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@7393ccd5: defining beans [com.example.mod04.processors.RandomPostProcessor#0,cember,sayisal]; root of factory hierarchy  
Projenin tamamını Maven projesi olarak bu adresten indirebilirsiniz. Spring konusunda daha fazla bilgi edinmek isterseniz bu bağlantıdaki Spring sınıf eğitimlerini tavsiye ederim.