← Назад к вопросам

В чем разница между Session и Global Session?

2.3 Middle🔥 81 комментариев
#ORM и Hibernate#Spring Framework

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Session vs Global Session в Spring Beans

Контекст

Это вопрос про scope-ы бинов в Spring Framework. В веб-приложениях доступны несколько scope-ов для определения жизненного цикла бина.

Session Scope

Назначение: бин создаётся и существует в рамках одной HTTP Session.

@Component
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserSession {
    private String username;
    private LocalDateTime loginTime;
    
    public UserSession() {
        System.out.println("UserSession bean created");
    }
    
    public String getUsername() {
        return username;
    }
    
    public void setUsername(String username) {
        this.username = username;
    }
}

Характеристики:

  • Один бин на одну HTTP Session
  • Создаётся при первом обращении в сессии
  • Уничтожается при invalidate сессии
  • Может быть разным для разных пользователей
  • Данные не передаются между браузерами

Пример использования:

@Controller
public class LoginController {
    @Autowired
    private UserSession userSession;  // Бин из текущей сессии
    
    @PostMapping("/login")
    public String login(@RequestParam String username, HttpSession session) {
        userSession.setUsername(username);
        userSession.setLoginTime(LocalDateTime.now());
        return "redirect:/dashboard";
    }
    
    @GetMapping("/profile")
    public String profile(Model model) {
        // Получим ТОТ ЖЕ объект UserSession, что и при login
        model.addAttribute("username", userSession.getUsername());
        return "profile";
    }
}

Как работает:

  1. Пользователь A открывает сайт → создаётся Session A → создаётся UserSession bean (экземпляр A)
  2. Пользователь B открывает сайт → создаётся Session B → создаётся UserSession bean (экземпляр B)
  3. Пользователь A и B имеют разные экземпляры UserSession
  4. Данные A не видны для B

Global Session Scope

Назначение: бин существует в рамках Global Session (используется в Portlet приложениях, очень редко).

@Component
@Scope(value = "globalSession", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class PortalGlobalData {
    private String sharedData;
    
    public PortalGlobalData() {
        System.out.println("PortalGlobalData bean created");
    }
}

Характеристики:

  • Один бин на одного пользователя портала
  • Используется ТОЛЬКО в Portlet приложениях (очень редко в production)
  • В обычных Servlet приложениях ведёт себя как session scope
  • PortletSession имеет два уровня видимости: APPLICATION_SCOPE и PORTLET_SCOPE
  • Global Session — это APPLICATION_SCOPE в portlet терминологии

Пример использования (Portlet):

// В Portlet приложении (portal.liferay.com или подобное)
@Component
@Scope(value = "globalSession", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class PortalUserPreferences {
    private String theme;  // Выбранная тема на уровне портала
    private String language;  // Язык портала
    
    // Доступно всем портлетам в приложении
    public String getTheme() {
        return theme;
    }
}

Детальное сравнение

АспектSessionGlobal Session
Область видимостиОдна HTTP SessionОдна Global Session (Portlet)
СозданиеПри первом доступеПри первом доступе
УничтожениеПри invalidate сессииПри выхода из портала
ИспользованиеОбычные веб-приложенияPortlet приложения (редко)
Количество экземпляровОдин на пользователяОдин на пользователя портала
Совместное использованиеМежду контроллерами одной сессииМежду всеми портлетами
Реальное применение~80% session-based приложений< 5% (legacy portals)
В servlet приложенииРаботает как задуманоВедёт себя как session

Практические сценарии

Session Scope — реальный пример

Шоппинг-карзина

@Component
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ShoppingCart {
    private List<CartItem> items = new ArrayList<>();
    
    public void addItem(CartItem item) {
        items.add(item);
    }
    
    public List<CartItem> getItems() {
        return items;
    }
    
    public void clear() {
        items.clear();
    }
}

@RestController
@RequestMapping("/api/cart")
public class CartController {
    @Autowired
    private ShoppingCart cart;  // Разный для каждого пользователя
    
    @PostMapping("/add")
    public void addItem(@RequestBody CartItem item) {
        cart.addItem(item);  // Добавит в корзину текущего пользователя
    }
    
    @GetMapping("/items")
    public List<CartItem> getItems() {
        return cart.getItems();  // Вернёт только товары текущего пользователя
    }
    
    @PostMapping("/checkout")
    public Order checkout() {
        Order order = new Order(cart.getItems());
        cart.clear();  // Очистить корзину текущего пользователя
        return order;
    }
}

Сценарий:

1. Пользователь A открывает сайт (Session A создана)
2. Пользователь A добавляет товары в корзину
3. Пользователь B одновременно открывает сайт (Session B создана)
4. Пользователь B добавляет товары в корзину
5. Корзина A != Корзина B (разные экземпляры ShoppingCart)
6. Пользователь A не видит товары B

Global Session — портал (редко)

// Portlet контекст (IBM Portal, Liferay)
@Component
@Scope(value = "globalSession", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class CompanySettings {
    private String companyName;  // Доступна всем портлетам компании
    private String logo;  // Общее лого
    private String theme;  // Общая тема
}

// Портлет 1
@Controller
public class SalesPortlet {
    @Autowired
    private CompanySettings settings;
    
    @GetMapping("/sales")
    public String view() {
        String logo = settings.getLogo();  // Общее для всех портлетов
        return "sales";
    }
}

// Портлет 2
@Controller
public class HRPortlet {
    @Autowired
    private CompanySettings settings;
    
    @GetMapping("/hr")
    public String view() {
        String logo = settings.getLogo();  // ТОТ ЖЕ объект
        return "hr";
    }
}

Важное примечание про Proxy Mode

Зачем proxyMode = ScopedProxyMode.TARGET_CLASS?

Session-scoped бины нельзя внедрить прямо в singleton бин.

// ❌ Ошибка! Singleton пытается внедрить session-scoped бин
@Service
public class OrderService {
    @Autowired
    private ShoppingCart cart;  // BeanCreationException
}

// ✅ Правильно! Используем proxy
@Component
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ShoppingCart {
    // Proxy будет выбирать правильный экземпляр в зависимости от текущей сессии
}

Как работает proxy:

  1. Spring создаёт CGLIB proxy объект
  2. Каждый раз, когда вызывается метод, proxy определяет текущую сессию
  3. Находит (или создаёт) правильный экземпляр бина для этой сессии
  4. Делегирует вызов реальному объекту

Другие scope-ы для сравнения

// Singleton (по умолчанию)
@Component
public class ApplicationConfig {
    // Один экземпляр на всё приложение
}

// Prototype
@Component
@Scope("prototype")
public class NewObject {
    // Новый экземпляр при каждом внедрении
}

// Request
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestData {
    // Один экземпляр на один HTTP запрос
}

// Session
@Component
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserSession {
    // Один экземпляр на одну HTTP сессию
}

// Application (глобальный для приложения)
@Component
@Scope(value = "application", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ApplicationData {
    // Один экземпляр на всё веб-приложение
}

Когда использовать Session

Используй Session scope:

  • Для данных пользователя (корзина покупок)
  • Для user-specific настроек
  • Для кэширования пользовательских данных
  • Для временных данных в рамках сессии

Не используй Session scope:

  • Для shared данных (используй application scope или кэш)
  • Для тяжёлых объектов (используй lazy loading)
  • Если data нужна в async потоках (может быть undefined)

Когда использовать Global Session

⚠️ Global Session используется редко:

  • Только в Portlet приложениях (IBM Portal, Liferay)
  • Для data общей между портлетами
  • В обычных Servlet приложениях лучше использовать application scope

Заключение

  • Session — один бин на одного пользователя в рамках одной HTTP сессии, основной scope для веб-приложений
  • Global Session — один бин на одного пользователя портала, используется редко в Portlet приложениях
  • В обычных веб-приложениях Global Session ведёт себя как Session
  • Используй proxyMode = ScopedProxyMode.TARGET_CLASS при внедрении session-scoped бинов в singleton