Spring-MVC @ModelAttribute和@Autowired

时间:2013-09-14 10:48:10

标签: java spring spring-mvc

我试图在方法上使用@ModelAttribute Annontation初始化一个Object。调用URL" / p / PPP / scope"奇怪的事发生了。在调用@ModelAttribute方法时,似乎没有实例化ProjectService,但在调用show()方法时它就在那里。有没有人知道这有什么不对?

以下是日志声明:

12:32:19 [DEBUG] ScopeController - getProject() - loading project for 'PPP'
12:32:19 [DEBUG] ScopeController - getProject() - projectService initialized? null
12:32:21 [DEBUG] ScopeController - show() - projectService initialized? ...project.ProjectService@20f2442e

和来源:

@Controller
@RequestMapping("/p/{abbr}/scope")
@SessionAttributes("project")
public class ScopeController {

    public static final String SHOW_PROJECT_PAGE = "/projects/scope/show";

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

    @Autowired
    private ProjectService projectService;

    @ModelAttribute("project")
    private Project getProject(@PathVariable(value = "abbr") String abbr) {
        log.debug("getProject() - loading project for '{}'", abbr);
        log.debug("getProject() - projectService initialized? {}", projectService);
        // should call this method:
        // return projectService.find(abbr);
        return new Project();
    }

    @RequestMapping(method = RequestMethod.GET)
    @Transactional
    public String show() throws BindException {
        log.debug("show() - projectService initialized? {}", projectService);
        return SHOW_PROJECT_PAGE;
    }
}

4 个答案:

答案 0 :(得分:1)

所有使用ModelAttibute注释的方法都必须是公开的。

因此,当方法getProject是公共的时 - 它将正常工作:

 @ModelAttribute("project")
 public Project getProject( ...

答案 1 :(得分:0)

也许尝试一些事情。

  1. 更改签名:
    • 私人项目getProject to
    • public @ResponseBody 项目
  2. 从控制器中删除@Transactional并将其移动到需要它们的任何服务方法。 (可以说是更好的设计实践 - 怀疑它会导致你描述的问题)
  3. 将@ModelAttribute(“Project”)注释移动到Project类
    • 即。
      @ModelAttribute( “项目”) public Project get Project(){ 返回新项目(); }
  4. 所以它看起来像:

    @Controller
    @RequestMapping("/p/{abbr}/scope")
    @SessionAttributes("project")
    public class ScopeController {
    
    public static final String SHOW_PROJECT_PAGE = "/projects/scope/show";
    
    private static final Logger log = LoggerFactory.getLogger(ScopeController.class);
    
    @Autowired
    private ProjectService projectService;
    
    @RequestMapping(value = "/<yourUri for getProject>", method = RequestMethod.GET)
    public @ResponseBody Project get(@PathVariable(value = "abbr") String abbr) {
        return getProject(abbr);
    }
    
    private Project getProject(String abbr) {
        log.debug("getProject() - loading project for '{}'", abbr);
        log.debug("getProject() - projectService initialized? {}", projectService);
        // should call this method:
        // return projectService.find(abbr);
        return new Project();
    }
    
    @RequestMapping(method = RequestMethod.GET)
    @Transactional
    public String show() throws BindException {
        log.debug("show() - projectService initialized? {}", projectService);
        return SHOW_PROJECT_PAGE;
    }
    

    }

    //In your Project class 
    @ModelAttribute("project")
    public class Project {
    //your class stuff
    }
    

答案 2 :(得分:0)

首先,我会将@Transactional注释放在Repository / data访问层中,因为这是基于Spring MVC注释的良好分层应用程序的标准。此外,您的@PathVariable注释用于检索控制器基URI之后在URI中传递的值。因此,在不拦截URI模式的私有帮助器方法中使用此注释毫无意义。

答案 3 :(得分:0)

所以在玩完之后我找到了解决方案。问题是@ModelAttribute中的名称。删除“项目”后,该方法可以预先使用。由于关于“getProject()” - 方法的混淆,我做了一些重构,以使方法的意图更加清晰。以下是包含其他注释的完整课程:

@Controller
@RequestMapping("/p/{abbr}/scope")
public class ScopeController {

    private static final String SHOW_PROJECT_PAGE = "/projects/scope/show";

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

    @Autowired
    private ProjectService projectService;

    // method is called before show() and update()
    @ModelAttribute
    private void initProject(@PathVariable(value = "abbr") String abbr, Model model) {
        log.debug("loading project for '{}'", abbr);
        // load the project JPA entity from the database, will be merged with the  
        // updated form values in the POST request. By doing this, I can asure
        // that the primary key (the ID) and the related objects are present as 
        // needed for the em.saveOrUpdate() in the projectService.save() method.
        model.addAttribute("project", projectService.find(abbr));
    }

    @RequestMapping(method = RequestMethod.GET)
    public String show() throws BindException {
        // shows the project scope form with the project 
        // added in 'initProject()'
        return SHOW_PROJECT_PAGE;
    }

    @RequestMapping(method = RequestMethod.POST)
    public String update(
            // the project with the updated form values and the JPA ID and JPA 
            // relations as loaded in the initProject()
            @Valid @ModelAttribute Project project, BindingResult result, 
            RedirectAttributes redirectAttrs)
            throws MethodArgumentNotValidException {

        redirectAttrs.addFlashAttribute(project);

        try {
            if (!result.hasErrors()) {
                projectService.save(project);
            }
        }
        catch (Exception e) {
            log.error(e.toString());
            throw new MethodArgumentNotValidException(null, result);
        }

        log.debug("project '{}' updated", project.getAbbreviation());
        return SHOW_PROJECT_PAGE;
    }
}

感谢大家的回答。