Spring MVC: как я могу повторно загрузить информацию о вошедшем в систему пользователе из базы данных на каждой странице Thymeleaf?

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

Эта информация может измениться в базе данных и должна быть повторно загружена при перезагрузке страницы пользователем.

Я бы хотел, чтобы был глобальный объект (например, loggedInUser), загружаемый из базы данных, когда страница Thymeleaf возвращается с сервера, чтобы я мог получить доступ к этому объекту из Thymeleaf без необходимости передавать его с каждого контроллера .

Возможно ли добиться чего-то подобного?

**** ОБНОВЛЕНИЕ ****

Например: я хочу отображать уведомления для пользователя в строке заголовка, и эти уведомления могут быть добавлены на стороне сервера базы данных с помощью различных действий. Когда пользователь загружает любую страницу веб-сайта (включая заголовок), уведомления должны загружаться на стороне сервера и отображаться в заголовке из Thymeleaf.

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


person Andrea    schedule 20.03.2015    source источник


Ответы (3)


Загрузите данные для всех или определенных контроллеров с помощью _ 1_ @ModelAttribute метод

@ControllerAdvice(assignableTypes={FooController.class, BarController.class})
public class LoadGlobalData {

  @Autowired
  private MyUserDataService userDataService;

  @ModelAttribute("userData")
  MyUserData getUserData(){
        return userDataService.findByUser(...);
  }  
}

(Или вы можете использовать перехватчик обработчика для вышеуказанного)

Используйте метод кеширования в своей службе.

@Service
public class MyUserDataService {

  @Cacheable(value="userData", key="#userId")
  public MyUserData findByUser(long userId) {
    ...
  }

  @CacheEvict(value="userData", key="#userId")
  public void bust(long userId) {
    ...
  }
}

Используйте базу данных, поддерживающую уведомления, такую ​​как Oracle или PostgreSQL, чтобы очистить кеш, когда изменения данных.

person Neil McGuigan    schedule 21.03.2015
comment
Спасибо, ваше решение кажется многообещающим для моих нужд .. Я не понимаю, что это за класс LoadGlobalData? .. Есть ли способ посоветовать только некоторым контроллерам загружать мои данные (чтобы я мог ограничить загрузку данных только контроллерами, возвращающими страница тимелеафа)? - person Andrea; 21.03.2015
comment
@Andrea в последней версии весны вы можете настроить controlleradvice, как вы спрашиваете. Или вы можете написать обработчик-перехватчик. - person Neil McGuigan; 21.03.2015
comment
Отлично! .. Я использовал @ControllerAdvice(assignableTypes = PagesController.class) , где PagesController - мой класс контроллера с методами, возвращающими страницу Thymeleaf. Если вы обновите ответ своим последним предложением, он идеально подойдет для моего вопроса. - person Andrea; 21.03.2015

Вы можете использовать перехватчик для добавления материала в экземпляр ModelAndView для каждой обслуживаемой страницы.

public class AuthenticationUtil {
    private AuthenticationUtil() {} // private c'tor for utility class

    private static final AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();

    /**
     * @return {@code true} if the user is authenticated non-anonymously
     */
    public static boolean isAuthenticated() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        return authentication != null &&
            authentication.isAuthenticated() &&
            !authenticationTrustResolver.isAnonymous(authentication);
    }
}

И перехватчик:

public class NotificationSettingInterceptor extends HandlerInterceptorAdapter {
    @Autowired
    private NotificationRepository notificationRepository;

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        if (modelAndView == null) { // can be null for static resources etc.
            return;
        }

        if (AuthenticationUtil.isAuthenticated()) {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            modelAndView.addObject("notifications", notificationRepository.findNotificationsFor(authentication.getName()));
        }
    }
}

Создайте bean-компонент из этого перехватчика и зарегистрируйте его, например путем переопределения метода addInterceptors вашего веб-класса @Configuration (расширение WebMvcConfigurerAdapter)

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
    ...
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(notificationSettingInterceptor());
    }
    ...
    @Bean
    public NotificationSettingInterceptor notificationSettingInterceptor() {
        return new NotificationSettingInterceptor();
    }
}

Или используйте тег <mvc:interceptors>, если вы используете конфигурацию пространства имен xml:

<mvc:interceptors>
    <bean class="com.example.NotificationSettingInterceptor"/>
</mvc:interceptors>
person James    schedule 20.03.2015
comment
Будет ли код перехватчика выполняться при каждом запросе / ответе или только тогда, когда ответ представляет собой страницу Thymeleaf? .. У меня есть некоторые контроллеры, возвращающие не страницу тимелеафа, а обслуживающие JSON, я бы хотел избежать загрузки пользовательских уведомлений также на эти контроллеры потому что мне это не нужно. - person Andrea; 21.03.2015
comment
Вы можете настроить перехватчик, как описано здесь, например, обрабатывать только /**/*.html: stackoverflow.com / questions / 8044277 / - person James; 21.03.2015
comment
Спасибо, Джеймс. Я выбрал ответ @NeilMcGuigan, потому что он проще и позволяет мне контролировать, где загружать информацию о пользователях по классам, а не по URL-адресам. - person Andrea; 21.03.2015

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

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

РЕДАКТИРОВАТЬ: если вы не хотите иметь состояние в своем приложении, обычное решение отправляет эти данные туда и обратно между клиентом и сервером. Но этот подход требует некоторой полосы пропускания.

РЕДАКТИРОВАТЬ1: Другое решение - сохранить эту информацию в файле cookie браузера. Это будет отправляться туда и обратно между сервером и клиентом.

person luboskrnac    schedule 20.03.2015
comment
что произойдет, если информация о пользователе изменится в базе данных после, когда пользователь вошел в систему (и сессионный компонент уже создан)? .. обновляется ли информация из базы данных при новых запросах? - person Andrea; 20.03.2015
comment
без вашего вмешательства нет. Это проблема состояния вашего приложения. Обычно вы хотите избежать этого. См. Мое предложение по редактированию в ответе. - person luboskrnac; 21.03.2015
comment
как получить то, о чем я просил, без указания государства? - person Andrea; 21.03.2015
comment
см. раздел ИЗМЕНИТЬ моего ответа - person luboskrnac; 21.03.2015
comment
спасибо @lkrnac, но, возможно, я плохо объяснил свою проблему ... см. мое обновление по вопросу, я добавил примерный сценарий, надеясь дать лучшее объяснение ... Я не знаю, как я могу добиться этого с помощью файлов cookie :) - person Andrea; 21.03.2015