【问题标题】:Injecting a SessionScope bean in Controller not working在控制器中注入 SessionScope bean 不起作用
【发布时间】:2021-11-14 06:51:41
【问题描述】:

我无法设置属性值,即。 shortname 到 Spring Boot 中的 SessionScope bean。

这是我的课:

    import java.util.Map;

    public class LdapUser {
        private String shortname = "";
        private Map<String,String> token = null;
        private String id = "";
    
    public LdapUser() {
           
        }
    
        public String getshortname() {
            return shortname;
        }
    
        public void setshortname(String shortname) {
            this.shortname = shortname;
        }
... remaining geters and setters

我的 Bean 定义在这里:

import xxx.controllers.SwitchController;
import xxx.isim.LdapUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.WebApplicationContext;

@Configuration
public class RestTemplateClient {

    Logger logger = LoggerFactory.getLogger(SwitchController.class);

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @Bean
    @Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
    public LdapUser sessionScopedLdapUser() {
        logger.info("LdapUser bean instance created");
        return new LdapUser();
    }
}

我在控制器中使用 Bean:

import xxx.errors.IsimConnectionException;
import xxx.isim.IsimConnection;
import xxx.isim.LdapUser;
import xxx.services.IsimRestApiService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.annotation.Resource;
import java.security.Principal;

@Controller
public class HomeController {

    private static final Logger log = LoggerFactory.getLogger(HomeController.class);

    @Autowired
    IsimRestApiService isimConn;

    @Resource(name = "sessionScopedLdapUser")
    LdapUser sessionScopedLdapUser;

    @RequestMapping("/")
    public String index(Principal principal) throws IsimConnectionException {
        Authentication authentication = (Authentication) principal;

        /

        if ((authentication.getPrincipal() != null) && (authentication.isAuthenticated())) {
            // set the shortname for the session
            String shortname = (String)authentication.getPrincipal();
            sessionScopedLdapUser.setshortname(shortname); //<-----

我的 Bean 的 shortname 值在带有箭头的行之后仍然为空,即使我正确获得了 shortname 字符串值并且该值不为空。您能否指出我在设置 bean 属性值时做错了什么。我遵循了 SessionScope Beans 的示例 here

更新: 我也尝试使用 autowired 而不是 @Resource(name = "sessionScopedLdapUser") 但执行 sessionScopedLdapUser.setshortname(shortname);

后该值仍然保持为空
 @Autowired
 LdapUser sessionScopedLdapUser

在日志中我还可以看到 LdapUser bean 实例被创建了 3 次。这怎么可能?

2021-09-21 10:55:55,469 INFO  [http-nio-8080-exec-4] xxx.config.RestTemplateClient: LdapUser bean instance created
2021-09-21 10:57:05,247 INFO  [http-nio-8080-exec-4] xxx.config.RestTemplateClient: LdapUser bean instance created
2021-09-21 10:58:08,401 INFO  [http-nio-8080-exec-4] xxx.config.RestTemplateClient: LdapUser bean instance created

想法是每个 HTTP 会话有一个 bean。我真的很困惑,希望能得到一些提示。我正在阅读这个article,这可能是因为我正在尝试将一个会话范围 bean 注入到一个 Singletone bean。

我的文件结构是:

xxx
---auth
---config
   --- RestRemplateClient
---controllers
   --- HomeController
---errors
---isim
   --- LdapUser
---services
Mainapp

【问题讨论】:

  • 您可以尝试在 HomeController 的控制器级别添加 @SessionAttributes(types = { LdapUser .class}) 吗?
  • @SessionAttributes 是为了完全不同的东西。请在您的代码中包含导入语句,因为我想知道您正在使用 which @SessionScope 注释。使用 @Resource@Autowired 不会改变任何事情。我想知道一件事是什么时候null?你怎么检查这个?是调试器中的null,还是其他地方的null
  • 嗨@M.Deinum 我很抱歉,我最初没有看到你的问题。我在原始问题中包含了导入语句。我在调试器中检查了 bean 字段 shortname 的值。我尝试了@SessionScope 和@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
  • 如果您签入调试器,该字段将出现null,因为您查看的是代理而不是会话中的真实对象。所以基本上你的大脑在欺骗你。如果您检查调试器,您将看到该类型实际上类似于LdapUser$EnchangedBySpring_Cgling$12343,这将有几个回调等相关联,如果您深入挖掘,您会发现实际的会话范围实例。但现在您看到的是代理而不是实际对象。
  • 非常感谢@M。 Deinum 以编程方式显示它工作正常。正如您所指出的,该字段的调试器值来自代理。我花了 2 天时间来接受这个:-)

标签: java spring-boot session-state spring-bean


【解决方案1】:

您可以指定如下配置:-

import org.springframework.context.annotation.Scope;
import java.time.LocalDateTime;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;

@Component
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class LdapUser {

    private String shortName = "LdapUser Session Scope";
// other properties
    
    public LdapUser() {
        System.out.println("LdapUser SessionScope Constructor Called at "+LocalDateTime.now());
    }

    public String getShortName() {
        return shortName;
    }

    public void setShortName(String shortName) {
        this.shortName = shortName;
    }
}

在配置中:

@Configuration
public class RestTemplateClient {

    Logger logger = LoggerFactory.getLogger(SwitchController.class);

    @Autowired
    private LdapUser ldapUser;

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    public void setLdapUser(LdapUser ldapUser) {
    this.ldapUser = ldapUser;
    }

    public LdapUser getLdapUser() {
    return ldapUser;
    }
}

进入你的控制器:-

@Controller
public class HomeController {
// Other Codes

@Autowired
private RestTemplateClient restTemplateClient;

private static final Logger log = LoggerFactory.getLogger(HomeController.class);

    @Autowired
    IsimRestApiService isimConn;

@RequestMapping("/")
    public String index(Principal principal) throws IsimConnectionException {
        Authentication authentication = (Authentication) principal;

        /

        if ((authentication.getPrincipal() != null) && (authentication.isAuthenticated())) {
            // set the shortname for the session
            String shortname = (String)authentication.getPrincipal();
restTemplateClient.getLdapUser().setShortName("LdapUser Session Scope Updated");
// .... Other codes
}
}
}

【讨论】:

  • 谢谢@N K Shukla 除了短名称之外,我还需要为每个会话存储另外两个值,即 private Map&lt;String,String&gt; tokenprivate String id,这就是我使用 SessionScope bean 的原因。
  • @Anuska 请尝试使用更新的解决方案方法。
  • 结果应该是一样的,如果这样可行,那么他的代码还有其他问题。添加额外的层不是解决方案。
  • 不是proxyMode = ScopedProxyMode.TARGET_CLASS在起作用吗?它定义了 Spring 如何代理你的 bean。在这种情况下,它将通过保留目标类来代理。 @M.Deinum
  • @SessionScope 是它的简写,在这种情况下会自动使用类代理,因为没有接口。
【解决方案2】:

感谢@M。 Deinum 我能够弄清楚。我在调试器中查看字段值,因为我查看的是代理而不是真实对象,所以该字段值始终为空。

这是在@Controller 类中注入会话范围的bean 的代码。它在@Service 类中也能以同样的方式正常工作。

public class LdapUser {
    private String shortname = "";
    private Map<String,String> token = new HashMap<>();
    private String id = "";

    public LdapUser() {
        this.shortname = shortname;
        this.token = token;
        this.id = id;
    }


    public String getshortname() {
        return shortname;
    }

    public void setshortname(String shortname) {
        this.shortname = shortname;
    }
 ... other getters and setters

我的bean配置类:

@Configuration
public class RestTemplateClient {

Logger logger = LoggerFactory.getLogger(SwitchController.class);

@Bean
public RestTemplate restTemplate() {
    return new RestTemplate();
}

@Bean
@SessionScope
public LdapUser sessionScopedLdapUser() {
    logger.info("LdapUser bean instance created at "+ LocalDateTime.now());
    return new LdapUser();
}

}

我的控制器类:

@Controller
public class HomeController {
private static final Logger log = LoggerFactory.getLogger(HomeController.class);

@Autowired
IsimRestApiService isimConn;

@Autowired
LdapUser sessionScopedLdapUser;

@RequestMapping("/")
public String index(Principal principal) throws IsimConnectionException {
    Authentication authentication = (Authentication) principal;

    //System.out.println("******* USER IS " + authentication.getPrincipal());

    if ((authentication.getPrincipal() != null) && (authentication.isAuthenticated())) {
        // set the shortname for the session
        String shortname = (String)authentication.getPrincipal();

        sessionScopedLdapUser.setshortname(shortname);

【讨论】:

    猜你喜欢
    • 2015-12-27
    • 2019-01-18
    • 1970-01-01
    • 1970-01-01
    • 2015-11-15
    • 2017-05-19
    • 2017-07-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多