2 Aralık 2019 Pazartesi

SpringContext BeanPostProcessor Arayüzü - Bean İçin Proxy Üretebilmeyi Sağlar

Giriş
Şu satırı dahil ederiz
import org.springframework.beans.factory.config.BeanPostProcessor;
postProcessBeforeInitialization() bean yaratılmadan önce çağrılır
postProcessAfterInitialization() bean yaratıldıktan sonra çağrılır. 

Lifecycle
Bean Lifecycle yazısına bakabilirsiniz.

Hata Logu
Bazen Spring uygulaması açılırken şuna benzer bir hata alırız. Bu hata BeanPostProcessor olan bir koda mybean isimli nesnenin inject edildiğini gösterir. Tabii bu da hatalı ve Spring bizi uyarıyor.
Bean 'mybean' of type [com.foo.Bar] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
Çözüm 1
Çözüm olarak BeanPostProcessor kodumuz içine bean inject etmek yerine ObjectFactory kullanılarak bean elde edilebilir. Şu kodu
@Autowired
public CustomerPermissionEvaluator(AuthorityService authorityService) {
  this.authorityService = authorityService;
}
Şöyle yaparız.
@Autowired public CustomerPermissionEvaluator(ObjectFactory<AuthorityService>
authorityServiceObjectFactory) { this.authorityServiceObjectFactory = authorityServiceObjectFactory; } @Override public boolean hasPermission(Authentication authentication, Object targetDomainObject,
Object permission) { AuthorityService service = this.authorityServiceObjectFactory.getObject(); // ... do stuff with service }
Çözüm 2
Kodumuzu BeanFactoryAware arayüzünden kalıtırız. Daha sonra bu nesneden bean'i elde ederiz.

Proxy Üretmek
Eğer proxy üretmek istiyorsak bunu postPrcessAfterInitialization() metodunda yapmak gerekir. Açıklaması şöyle
One, very important, fact/convention, Spring dictates, is that the beans returned from the “Before Initializaiton” phase should be actual/original beans passed to “Before Initialization” method. Proxies are wrapped around beans only in “After Initialization” phase, ...
postProcessBeforeInitialization metodu
Şöyle yaparız. Burada bean'in constructor'ı çağrılmadan önce bir zaman alınıyor. Daha sonra @PostContruct(varsa) çağrıldıktan sonra da bir zaman alınıyor ve bean'in ilklendirilmesinin ne kadar sürdüğü bulunuyor. Ancak bean içinde static{...} blok varsa bu hesaba katılmaz!
public class LoggerBeanPostProcessor implements BeanPostProcessor, Ordered {

  protected Log logger = ...;
  private Map<String, Long> start = ...;
  private Map<String, Long> end = ...;


  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName)
  throws BeansException {
    start.put(beanName, System.currentTimeMillis());
    return bean;
  }

  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName)
  throws BeansException {
    end.put(beanName, System.currentTimeMillis());
    logger.debug("Init time for " + beanName + ": " + initializationTime(beanName));
    return bean;
  }

  @Override
  public int getOrder() {
    return Integer.MAX_VALUE;
  }

  // this method returns initialization time of the bean.
  public long initializationTime(String beanName) {
    return end.get(beanName) - start.get(beanName);
  }
}
postProcessAfterInitialization metodu
Örnek
Şöyle yaparız.
public class EventBusRegisterBeanPostProcessor implements BeanPostProcessor,
        ApplicationContextAware {

  private ApplicationContext context;

  @Autowired
  private EventBus eventBus; // The only event bus i assume...

  public Object postProcessBeforeInitialization(Object bean, String beanName)
    throws BeansException {

    return bean;
  }

  public Object postProcessAfterInitialization(Object bean, String beanName)
    throws BeansException {

    if (bean instanceof ILanguageChangeListener) {
      registerToEventBus(bean);
    }

      return bean;
  }

  private void registerToEventBus(Object bean) {
    this.eventBus.register(bean);
  }

  public void setApplicationContext(ApplicationContext applicationContext)
    throws BeansException {
    this.context = applicationContext;
  }

}
Örnek
Elimizde şöyle bir anotasyon kodu olsun.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited // important when working with dynamically generated proxies i.e. CGLib
public @interface RegisterWithEventBus {}
Şöyle yaparız.
public class EventBusListenersRegistererBeanPostProcessor implements BeanPostProcessor{

  @Inject
  private EventBus bus;

  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName)
    throws BeansException {
    return bean;
  }

  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName)
    throws BeansException {
    if(bean.getClass().isAnnotationPresent(RegisterWithEventBus.class)){
      bus.register(bean);
    }

    return bean;
  }
}

Hiç yorum yok:

Yorum Gönder