Spring IoC: как насчет сериализации? - PullRequest
4 голосов
/ 15 сентября 2009

Я только нахожусь в процессе разработки нового программного компонента, включающего Spring Framework. Мне это нравится, но теперь у меня есть вопрос относительно IoC и сериализации.

Учитывая, что у меня есть этот класс (Пропущенный импорт и объявление пакета):

public class EMailNotificationEndpoint implements NotificationEndpoint {
    private List<String> notficationEmailAdresses = new ArrayList<String>();
    transient private MailSender mailSender;

    public EMailNotificationEndpoint(MailSender mailSender) {
        this.mailSender = mailSender;
    }

    @Override
    public void notify(Notification notification) {
        SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
        simpleMailMessage.setTo(notficationEmailAdresses.toArray(new String[notficationEmailAdresses.size()]));
        simpleMailMessage.setSubject(notification.getType().toString());
        simpleMailMessage.setText(notification.getMessage());

        mailSender.send(simpleMailMessage);
    }
}

NotificationEndpoint расширяет Serializable, чтобы обеспечить сериализацию всех реализаций. Дело в том, что я не могу сериализовать MailSender (который является org.springframework.mail.MailSender BTW) и хочу, чтобы он вводился во время десериализации. Теперь мой вопрос совершенно очевиден: могу ли я это сделать? И если ответ - да, то как?

Я свободен изменять интерфейс и реализации, так что должен быть способ сделать это, даже если это означает, что я должен реорганизовать соответствующие классы.

Любая идея или подсказка высоко ценится!

Ответы [ 2 ]

2 голосов
/ 15 сентября 2009

Вы правы в том, чтобы сделать его переходным.

Все, что вам нужно сделать, это восстановить его при необходимости при десериализации.

Вы могли бы реализовать адекватный метод readObject, чтобы выполнить эту инициализацию:

     /**
      * Always treat de-serialization as a full-blown constructor, by
      * initializing or validating the final state of the de-serialized object.
      */
      private void readObject(ObjectInputStream aInputStream) 
          throws ClassNotFoundException, IOException {
        //always perform the default de-serialization first
        aInputStream.defaultReadObject();
        //do your other stuff, like initialization
        this.mailSender = ...
      }

Примечание: здесь сам объект должен получить реализацию MailSender, которая не очень DI (внедрение зависимости). Либо вы согласны с этим, либо вы нашли другой путь (обычно после десериализации задействуется другой объект, который устанавливает правильную зависимость).


Если вам действительно нравится делать инъекцию зависимостей, я нашел ссылку для Spring и Entities, которая может содержать общие знания, применимые к этому случаю:
http://chris -richardson.blog-city.com / migrating_to_spring_2_part_3__injecting_dependencies_into_en.htm

1 голос
/ 15 сентября 2009

Вам потребуется реализовать пользовательские методы readObject и writeObject, которые сериализуют / десериализуют состояние и класс MailSender, что-то вроде этого:

private void writeObject(ObjectOutputStream out) throws ClassNotFoundException, IOException 
{
  try
  {
    out.defaultWriteObject();
    out.writeObject(mailSender.getClass().getName());

    for (Field field : mailSender.getClass().getDeclaredFields()) 
    {
      if (!(Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())))
      {  
        Object value = field.get(mailSender);
        if (value instanceof Serializable) 
        {
          out.writeObject(field.getName());
          out.writeObject(value);
        }
      }
    }  
  }
  catch (IllegalAccessException e) 
  {
    throw new IOException(e.getMessage(), e);
  }
}

private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException 
{
  try 
  {
    in.defaultReadObject();     
    mailSender = (MailSender)Class.forName((String)in.readObject()).newInstance();

    Map<String, Field> fields = new HashMap<String, Field>();
    for (Field field : mailSender.getClass().getDeclaredFields()) 
    {
      fields.put(field.getName(), field);
    }  

    int remainingFields = mailSender.getClass().getDeclaredFields().length;  
    while (remainingFields > 0) 
    {   
      String fieldName = (String)in.readObject();
      Object value = in.readObject();
    fields.get(fieldName).set(mailSender, value);
      remainingFields--;
    }
  }
  catch (IllegalAccessException e)
  {
    throw new IOException(e.getMessage(), e);
  }
  catch (InstantiationException e)
  {
    throw new IOException(e.getMessage(), e);
  }
}

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

...