【问题标题】:Spring non singleton controller弹簧非单例控制器
【发布时间】:2012-04-09 17:24:37
【问题描述】:

我正在构建一个基于 Spring 的应用程序,用于处理移动游戏应用程序的服务器端游戏管理。堆栈是 Spring/Hibernate/Jersey。我希望客户端(移动端)调用 REST API 来更新/检索游戏状态。

我创建了一个TournamentController 类,其职责是使用一些业务逻辑更新锦标赛状态。此类旨在在每次需要对锦标赛进行操作时实例化,然后丢弃。

public class TournamentController {

    @Autowired
    private TournamentDAO tournamentDAO;

    private final Tournament tournament;

    public TournamentController( Tournament tournament ) {
        this.tournament = tournament;
    }

    public void startTournament() {
        if ( tournament.getState() != TournamentState.SETUP ) {
            throw new TournamentAlreadyStartedException();
        }

        tournament.setState( TournamentState.IN_PROGRESS );

        //... some other logic and calls to other DAOs

        tournamentDAO.save( tournament );
    }

}

我还创建了一个TournamentResource 类,它是锦标赛的 REST 前端。它的职责是做一些基本的验证(用户安全,...)和异常翻译。

@Path( "/tournament" )
@Component
@Scope( "prototype" )
public class TournamentResource {

    private static final Log log = LogFactory.getLog( TournamentResource.class );

    @Autowired
    private TournamentDAO tournamentDAO;

    @GET
    @Path( "{tournamentId}/start" )
    @Produces( MediaType.APPLICATION_JSON )
    @Transactional
    public TournamentDTO startTournament( @PathParam( "tournamentId" ) long tournamentId ) {
        Tournament tournament = tournamentDAO.getTournament( tournamentId, LockMode.PESSIMISTIC_WRITE );
        if ( tournament == null ) {
            throw new WebApplicationException( Status.NOT_FOUND );
        }

        try {
            TournamentController controller = new TournamentController( tournament );
            controller.startTournament();

        } catch ( TournamentAlreadyStartedException e ) {
            log.warn( "Could not start tournament " + tournamentId + " since it is already started." );
            throw new RestException( Status.BAD_REQUEST, "Tournament already started" );
        }

        return DTOConverterUtil.getTournament( tournament );
    }

}

我正在使用 AspectJ 的加载时间编织。这是我的上下文:

   <context:annotation-config />

   <!-- Make spring aware ANY pojo with the @Configurable annotation -->
   <context:spring-configured />

   <!-- Scan all classes in com.mdarveau for annotations -->
   <context:component-scan base-package="so.question" />

   <!-- Load Time Weaver -->
   <context:load-time-weaver weaver-class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" aspectj-weaving="on" />

   <!-- DB config -->
   <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
      ...
   </bean>

   <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
      <property name="dataSource" ref="dataSource" />
      <property name="packagesToScan" value="com.mdarveau.fnp.model" />
      <property name="hibernateProperties">
         <value>
            hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
         </value>
      </property>
   </bean>

   <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
      <property name="sessionFactory" ref="sessionFactory" />
   </bean>

   <!-- enable @Transactional -->
   <tx:annotation-driven transaction-manager="txManager" mode="aspectj" />

我的TournamentResource 类在设计上是一个单例并且运行良好。

我的问题是,当它使用 new 而不是 spring 实例化 TournamentController 时,它的 @Autowired 属性似乎没有正确连接。我尝试在其上添加@Component 注释,但没有成功。

我应该创建TournamentResource ApplicationContextAware 并通过spring 创建TournamentController 吗?

这一定是一个相当普遍的问题。我看到很多后端是单例的示例,但我想避免将Tournament 传递给TournamentController 上的每个方法调用(然后传递给TournamentController 中的所有私有方法)。

【问题讨论】:

    标签: spring


    【解决方案1】:

    由于您已经拥有加载时编织和&lt;context:spring-configured /&gt;,您可以将您的TournamentController 声明为@Configurable

    它将为使用new 创建的TournamentController 实例启用自动装配。

    【讨论】:

      【解决方案2】:

      是的,您使用new 创建的任何内容都不受 Spring bean 工厂的控制。打电话给new 意味着你自己。

      我不确定为什么您的 TournamentController 需要对 Tournament 的私人引用。你说这是一个 REST 前端。如果我理解正确的话,REST 是基于 HTTP 的,它是一种无状态协议。

      我不确定您认为Tournament 实例的来源。如果 Spring 实例化了 TournamentController,那么您应该在注释或 XML 配置中连接对相关锦标赛的引用。

      我建议您将其设置为已连接的 Spring 资源,或者重新考虑您的设计以将其从 Controller 中删除。

      【讨论】:

      • “TournamentController”基本上只是一个特定“Tournament”的经理,所以它在任何时候必须在锦标赛上做某事时被实例化(在任何给定时间都会有多个锦标赛进行)。我希望它对“锦标赛”有一个私有引用,因此我不必将对“锦标赛”的引用传递给每个公共和私有方法。有点像中介模式。
      • 所以它不是无状态的。您需要为用户提供一个 REST 方法,以便他们能够指定他们想要的锦标赛,这将是控制器中的一个带注释的方法。它内部不需要引用。您应该不了解 REST 或调解器。
      • 是的,这是我的休息方法的“@PathParam("tournamentId")”参数:-)。该方法获取锦标赛 ID,从持久性中检索它,然后实例化 TournamentController(传递锦标赛)引用,以便它可以在请求范围内对其进行操作。
      • 您应该考虑将锦标赛保存在会话或缓存中,而不是控制器实例。您当前的设计要求每个用户都有自己的控制器,并且控制器要了解它的用户。这不是 RESTful。
      • 我可以,但我想减少应用会话。 TournamentController 只是 Tournament 周围的一个小包装器(调解器),因此我可以与之交互。
      猜你喜欢
      • 1970-01-01
      • 2014-10-28
      • 1970-01-01
      • 2014-12-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-23
      相关资源
      最近更新 更多