Поиск по блогу

Sunday, July 18, 2010

Spring hints #1. Оверайдим проперти не абстрактных контекстных бинов.

   Недавно возникла задача переопределения значений полей не абстрактных бинов в Spring контексте. В Гугле готового решения быстро найти не удалось, поэтому пришлось реализовывать самому.
Для решения задачи сначала нужно реализовать специальный Proxy класс. Spring framework предоставляет пользователям богатый набор интерфейсов для расширения. В нашем случае мы реализуем интерфейс org.springframework.beans.factory.FactoryBean.


Для начала создадим Maven проект со следующими зависимостями:

   
      org.springframework
      spring
      2.5.6
   

Теперь создадим класс SimpleBean с полями, которые мы хотим в дальнейшем переопределить:
public class SimpleBean {
   private String prop1;
   private String prop2;
   private SimpleBean sBean;

   public void setProp1(String prop1) { this.prop1 = prop1; }
   public void setProp2(String prop2) { this.prop2 = prop2; }
   public void setsBean(SimpleBean sBean) { this.sBean = sBean; }
}
Как вы можете видеть SimpleBean имеет ссылку на другой объект того же типа - sBean.

Далее создаем класс, реализующий интерфейс org.springframework.beans.factory.FactoryBean. Данный интерфейс определяет следующие методы:
public static interface FactoryBean  {    
    java.lang.Object getObject() throws java.lang.Exception;    
    java.lang.Class getObjectType();    
    boolean isSingleton();
}

Создаем класс PropertyOverrideFactoryBean, который реализует данный интерфейс. Данный класс будет содержать в себе логику переопределения пропертей исходного бина.
Реализация выглядит следующим образом:
public class PropertyOverrideFactoryBean 
       implements org.springframework.beans.factory.FactoryBean {

    private Object target;
    private boolean isSingleton = true;
    private java.util.Map propsToOverride;

    public Object getObject() throws Exception {
        for(Object eachKey: propsToOverride.keySet()) {
            if(!(eachKey instanceof String)) {
                continue;
            }
            String propName = (String) eachKey;
            java.lang.reflect.Field f = 
                    target.getClass().getDeclaredField(propName);
            f.setAccessible(true);
            f.set(target, propsToOverride.get(propName));
            f.setAccessible(false);
        }
        return target;
    }

    public Class getObjectType() {
        return target.getClass();
    }

    public boolean isSingleton() {
        return isSingleton;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    public void setPropsToOverride(java.util.Map propsToOverride) {
        this.propsToOverride = propsToOverride;
    }

    public void setSingleton(boolean singleton) {
        isSingleton = singleton;
    }
}

  • target поле будет содержать в себе ссылку на объект, проперти которого мы хотим переопределить. 
  • propsToOverride поле есть карта, в которой ключом является название проперти для переопределения, а значением - новое значение для переопределяемого проперти.

Теперь реализуем Spring контекст. Создадим в нем два бина. Один - бин класса SimpleBean,  второй - бин класса PropertyOverrideFactoryBean.


        
        
    

    
        
        
            
                
                    sBean
                    
                
                
                    prop1
                    new Prop1 value
                
                
                    prop2
                    new Prop2 value
                
            
        
      

Давайте посмотрим что получилось, запустив следующий код:
public class Main {

    public static void main(String[] args) {
        ApplicationContext ctx= 
              new ClassPathXmlApplicationContext("spring-context.xml");
        SimpleBean simpleBean= (SimpleBean) ctx.getBean("simpleBeanOverride");
        
        System.out.println("SimpleBean.prop1: " + simpleBean.getProp1());
        System.out.println("SimpleBean.prop2: " + simpleBean.getProp2());
        System.out.println("SimpleBean.sBean: " + simpleBean.getsBean());
    }
}

В этом коде мы вызываем из контекста бин с id="simpleBeanOverride", но на самом деле мы получаем экземпляр бин с id="simpleBean", но с переопределенными проперти. В консоль будет выведен следующий результат:

SimpleBean.prop1: new Prop1 value
SimpleBean.prop2: new Prop2 value
SimpleBean.sBean: SimpleBean@76ab2f

Видно, что на Proxy объект отлично справился с задачей. Для сравнения, если бы мы вызвали из контекста "simpleBean" напрямую, то результат был бы таким:

SimpleBean.prop1: prop1
SimpleBean.prop2: prop2
SimpleBean.sBean: null

No comments:

Post a Comment