Spring bean-объекты (контроллеры) и ссылки на сервисы в терминах сериализации - PullRequest
43 голосов
/ 05 июля 2010
  • стандартный случай - у вас есть контроллер (@Controller) с @Scope("session").
  • классами, помещенными в сеанс, обычно ожидается, что он реализует Serializable, чтобы они могли храниться физическив случае перезапуска сервера, например
  • Если контроллер реализует Serializable, это означает, что все службы (другие пружинные компоненты), на которые он ссылается, также будут сериализованы.Они часто являются прокси со ссылками на менеджеры транзакций, фабрики менеджера сущностей и т. Д.
  • Не исключено, что некоторые службы или даже контроллеры хранят ссылку на ApplicationContext, реализуя ApplicationContextAware,так что это может фактически означать, что весь контекст сериализован.И учитывая, что он содержит много соединений - то есть вещей, которые не сериализуются по идее, он будет восстановлен в поврежденном состоянии.

До сих пор я в основном игнорировал эти проблемы.Недавно я подумал о том, чтобы объявить все мои пружинные зависимости transient и вернуть их обратно в readResolve() статическими служебными классами WebApplicationContextUtils и такими, которые содержат запрос / ServletContext в ThreadLocal.Это утомительно, но гарантирует, что при десериализации объекта его зависимости будут «современными» с текущим контекстом приложения.

Есть ли принятая практика для этого,или любые рекомендации для сериализации частей контекста Spring.

Обратите внимание, что в JSF управляемые bean-компоненты (~ контроллеры) имеют состояние (в отличие от основанных на действии веб-фреймворков).Так что, возможно, мой вопрос стоит больше для JSF, чем для Spring-MVC.

Ответы [ 6 ]

19 голосов
/ 05 июля 2010

В этой презентации (около 1:14) докладчик говорит, что эта проблема решена весной 3.0 с помощью прокси-сервера не-сериализуемых bean-компонентов, который получает экземпляр из current контекст приложения (при десериализации)

7 голосов
/ 21 января 2012

Похоже, что щедрость не привлекла ни одного ответа, поэтому я задокументирую свое ограниченное понимание:

@Configuration
public class SpringConfig {

    @Bean 
    @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS) 
    MyService myService() {
        return new MyService();
    }

    @Bean
    @Scope("request")
    public IndexBean indexBean() {
        return new IndexBean();
    }

    @Bean
    @Scope("request")
    public DetailBean detailBean() {
        return new DetailBean();
    }
}

public class IndexBean implements Serializable {

    @Inject MyService myService;

    public void doSomething() {
        myService.sayHello();
    }
}

public class MyService {
    public void sayHello() {
        System.out.println("Hello World!");
    }
}

Тогда Spring не вставит голый MyService в IndexBean, а будет сериализуемым прокси-сервером. (Я проверил это, и это сработало).

Однако весенняя документация пишет :

Вы не должны использовать <aop:scoped-proxy/> в сочетании с бобами, которые определены как singletons или prototypes. Если вы попытаетесь создать прокси в области действия для одноэлементного компонента, BeanCreationException поднимется.

По крайней мере, при использовании конфигурации на основе Java, bean-компонент и его прокси могут быть созданы просто, то есть, исключение не выдается. Тем не менее, похоже, что использование прокси с заданной областью для достижения сериализуемости не является предназначением таких прокси. Поэтому я опасаюсь, что Spring мог бы исправить эту «ошибку» и предотвратить создание прокси-серверов с областью видимости через конфигурацию на основе Java.

Также существует ограничение: имя класса прокси-сервера меняется после перезапуска веб-приложения (поскольку имя класса прокси-сервера основано на хэш-коде совета, использованного для его создания, что, в свою очередь, зависит от hashCode объекта класса перехватчика. Class.hashCode не переопределяет Object.hashCode, который нестабилен при перезапусках). Поэтому сериализованные сеансы не могут использоваться другими виртуальными машинами или при перезапуске.

7 голосов
/ 05 июля 2010

Я бы ожидал, что контроллеры области видимости будут называться «синглтоном», то есть один раз для каждого приложения, а не для сеанса.

Session-scoping, как правило, больше используется для хранения информации для каждого пользователя или для функций пользователя.

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

Посмотрите на документы Spring для настройки некоторых пользовательских данных в области сеанса с использованием прокси-сервера aop:

http://static.springsource.org/spring/docs/2.5.x/reference/beans.html#beans-factory-scopes-other-injection

Надеюсь, это поможет

2 голосов
/ 26 января 2012

После того, как были испробованы все предложенные альтернативы, все, что мне нужно было сделать, это добавить aop: scoped-proxy к определению bean-компонента, и оно начало работать.

<bean id="securityService"
    class="xxx.customer.engagement.service.impl.SecurityContextServiceImpl">
    <aop:scoped-proxy/>
    <property name="identityService" ref="identityService" />
</bean>

securityService внедряется вмой управляемый, который является областью обзора.Кажется, это работает нормально.В соответствии с весенней документацией это должно вызывать исключение BeanCreationException, поскольку securityService является одноэлементным.Однако этого не происходит, и он работает нормально.Не уверен, что это ошибка или какие будут побочные эффекты.

2 голосов
/ 04 ноября 2011

Я недавно объединил JSF с Spring.Я использую RichFaces и функцию @KeepAlive, которая сериализует компонент JSF, поддерживающий страницу.Есть два способа заставить это работать:

1) Использовать @Component ("session") на базовом компоненте JSF

2) Получить компонент из ELContext, когда вам это нужнопримерно так:

@SuppressWarnings("unchecked")
public static <T> T  getBean(String beanName) {
    return (T) FacesContext.getCurrentInstance().getApplication().getELResolver().getValue(FacesContext.getCurrentInstance().getELContext(), null, beanName);
}
1 голос
/ 02 января 2015

Сериализация Динамические прокси хорошо работает, даже между различными JVM, например. как используется для сеанса-репликации.

@Configuration public class SpringConfig {
@Bean 
@Scope(proxyMode = ScopedProxyMode.INTERFACES) 
MyService myService() {
    return new MyService();
}
.....

Вам просто нужно установить идентификатор ApplicationContext до обновления контекста (см. Org.springframework.beans.factory.support.DefaultListableBeanFactory.setSerializationId (String))

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
// all other initialisation part ...
// before! refresh
ctx.setId("portal-lasg-appCtx-id");
// now refresh ..
ctx.refresh();
ctx.start();

Отлично работает на Spring-версии: 4.1.2.RELEASE

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...