1

メソッドの @ModelAttribute アノテーションを使用してオブジェクトを初期化しようとしています。URL "/p/PPP/scope" を呼び出すと、奇妙なことが起こります。ProjectService は、@ModelAttribute メソッドを呼び出したときにインスタンス化されていないように見えますが、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

4 に答える 4

1

ModelAttibute アノテーションを持つすべてのメソッドは public である必要があります。

したがって、メソッド getProject が公開されている場合、適切に機能します。

 @ModelAttribute("project")
 public Project getProject( ...
于 2014-04-24T15:17:02.340 に答える
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;
    }
}

回答ありがとうございます。

于 2013-09-15T12:50:22.717 に答える
0

1 つには、@Transactionalレイヤー化された Spring MVC アノテーション ベースの優れたアプリケーションの標準であるため、アノテーションをリポジトリ/データ アクセス レイヤーに配置します。さらに、@PathVariableアノテーションは、コントローラーのベース URI の後に URI で渡された値を取得するために使用されます。そのため、URI パターンをインターセプトしないプライベート ヘルパー メソッドにこのアノテーションを含めることはほとんど意味がありません。

于 2013-09-14T13:24:01.187 に答える
0

おそらく、いくつかのことを試してみてください。

  1. 次の署名を変更します。
    • プライベート プロジェクト getProject to
    • public @ResponseBodyプロジェクト
  2. コントローラーから @Transactional を削除し、必要なサービス メソッドに移動します。(間違いなくより良い設計手法 - あなたが説明した問題を引き起こしているのではないかと疑ってください)
  3. @ModelAttribute("Project") アノテーションを Project クラスに移動
    • すなわち
      @ModelAttribute("Project") public Project get Project(){ return new Project(); }

したがって、次のようになります。

@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
}
于 2013-09-14T12:37:45.590 に答える