【问题标题】:Spring is injecting concrete class instead of proxySpring正在注入具体类而不是代理
【发布时间】:2013-01-13 06:33:11
【问题描述】:

我有一个问题,Spring 将 DAO 对象的代理注入到服务中,但是这个服务被注入到控制器中,它是具体的类。这不允许我使用服务范围的事务并分别为每个 DAO 调用启动事务。这是我所期望的。

配置:

Controller 是带有 @Controller 注解和构造函数 DI 的类。

服务:

@零件 @事务 公共类 UserServiceImpl 实现 UserService { ...}

道:

@零件 @事务 公共类 UserDaoImpl 实现 UserDao {

JPA 配置:

<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>

<bean id="entityManagerFactory"
      class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" >
    <property name="dataSource" ref="dataSource"/>
    <property name="persistenceUnitName" value="xxxPersistenceUnit"/>
    <property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml"/>
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        </bean>
    </property>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.dialect">${hibernate.dialect}</prop>
            <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
            <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
            <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
        </props>
    </property>
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

<tx:annotation-driven />

有人知道为什么会这样吗?

【问题讨论】:

  • 我会放弃服务上的“@Compoment”并放置“@Service”原型注释。至于 dao,我会放置“@Repository”注释并放弃 Component 注释。此外,“@Transactional”属性意味着服务的每个方法都将具有相同的事务传播。我会根据它们应该做什么分别注释我的每个方法。
  • 我添加了一个答案,所以请检查您的事务配置 xml 文件是否由声明用户服务的同一 app context 处理。随时问更多问题

标签: java spring jpa


【解决方案1】:

您的UserServiceImpl 很可能是错误创建的in the servlet context - 请检查context:component-scan 表达式以检查其中是否仅包含Controller 类。

@Service are constructed twice 组件扫描过滤器示例。

例如,如果在 root web app context 中声明了事务管理器 bean 和 &lt;tx:annotation-driven&gt;,则将仅为根应用上下文中的 bean 创建事务代理(来自 Spring Documentation):

BeanPostProcessor 接口的范围是每个容器。这只是 如果您使用容器层次结构,则相关。如果你定义一个 BeanPostProcessor 在一个容器中,它只会在 那个容器里的豆子。在一个容器中定义的 Bean 是 不由另一个容器中的 BeanPostProcessor 进行后处理,甚至 如果两个容器都属于同一层次结构。

不太可能是用户服务的事务配置被配置为使用另一个事务管理器(或另一个默认传播),但在这种情况下,TransactionInterceptor 调用将出现在 DAO 方法的堆栈跟踪中。

如果您了解自己在做什么,那么在 Spring 中的 DAO 类上使用 @Transactional 绝对没问题 - 存储库或 DAO 无法打开事务的想法来自于您必须创建样板代码才能打开的黑暗时期事务,并且很难管理事务实例(并且您无法确定如何管理事务)。但是当您使用声明式配置时,事情并没有那么糟糕。 Spring 提倡约定优于配置的风格,大多数方法使用Propagation.REQUIRED 事务模式。在 Spring 中,Propagation.REQUIRED 是您使用 @Transactional 装饰方法时的默认模式(此传播被硬编码为 @Transactional 注释声明),这意味着新的逻辑事务映射到相同的物理事务,因此装饰您的 DAO 类@Transactional 是无害的。

请参阅http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/transaction.html#tx-propagation 以获取有关 Spring 中事务传播的参考

在 Spring Data JPA 中(我很确定他们知道自己在做什么),例如 CRUD methods on repository instances are transactional by default。这在某些情况下可能很有用,该机制与 Hibernate 允许您从 Session 获取()一些任意对象以在不声明显式事务的情况下进行显示时相同(当然这并不意味着框架以某种方式设法运行)没有交易——在这种情况下是隐含的)。

【讨论】:

  • “永不言败”当然,但我认为在绝大多数情况下,您会希望为您的服务设置事务边界,因为它定义了您的业务工作单元的逻辑分组.在问题中使用传统的UserServiceUserDAO 肯定表明该成语在这种情况下是合适的。
  • @ach 是的,您对大多数情况都是正确的。但是恕我直言,这些模式(DAO/Repository)是在 2003 年发布的,在声明式事务传播框架流行之前。我认为在 Spring 出现之前,声明式事务仅适用于 Java EE 应用程序服务器(如果您更喜欢使用带有容器的独立 TM,则必须直接调用 TM 方法)。或者您必须开发自定义 AOP 解决方案(并告诉团队如何使用它),但在 Spring 中我们没有这个问题 - 一切都经过测试并有据可查。
  • 嘿。你是对的。在 servlet 上下文中创建服务 bean 是有问题的,而不是在根上下文中。将过滤器添加到 后,一切都像魅力一样。
  • 谢谢,这也帮助了我。我将 dispatcher-servlet 组件扫描设置为非常通用的东西,例如“com.mycompany”,它也包含来自应用程序上下文的 bean。
【解决方案2】:

我有点难以理解您在说什么,但您似乎很惊讶每次 DAO 调用都会获得新事务,而不仅仅是服务调用。不幸的是,这正是您通过在 DAO 类上放置“@Transactional”所指定的。你的 DAO 不应该是事务性的,至少如果你遵循通常的模式。如果我对您的理解正确,您应该删除 DAO 类上的 @Transactional 注释。

【讨论】:

  • -1 如果我在每个 DAO 调用中也得到一个新事务,当我的应用程序服务被注释为 @Transactional(Propagation.REQUIRED 被硬编码为默认值时,我也会感到非常惊讶@Transactional 注释中的传播)!
  • @BorisTreukhov -1 发​​表评论。 OP 说他的服务被注入为一个具体的类(不是代理),这意味着它不是事务性的。因此,对 DAO 方法的每次调用都应在新事务中执行(DAO 方法相互调用的情况除外)。
  • @ach A) OP 用事务性装饰了 DAO 和应用程序服务。 B)Re I have a problem, where Spring is injecting proxy to DAO object into service, but this service is injected into controller it is concrete class. => 问题是服务是一个具体的类(不是代理)。
  • @BorisTreukhov 是的,这就是我要说的。但是,您说“如果我在每个 DAO 调用中都获得一个新事务,您也会感到惊讶”......您不应该感到惊讶,因为如果服务不是事务性的,这正是预期的行为。
  • @ach 你注意到when my application service is annotated with @Transactional 部分了吗?因此,我因过度讽刺和错误信息而对这篇文章投了反对票。
【解决方案3】:

其他响应者是正确的,因为您不应该将您的 DAO 注释为 @Transactional,但要真正了解正在发生的事情,您应该参考 Transaction Propagation section in the Spring Reference Manual。使用@Transactional 时的默认传播是REQUIRES_PROPAGATION,因此请具体查看。

你的问题不是那么具体,所以我不确定你在寻找什么。

编辑:重新阅读您的问题后,您的组件扫描可能存在问题。检查以确保您的 &lt;tx:annotation-driven /&gt; 与您正在组件扫描服务类的应用程序上下文相同。

【讨论】:

    【解决方案4】:

    你不应该在你的 DAO 对象中使用那个“@Transactional”注解。您在您的服务中定义它,这将允许您在服务方法中调用的所有 DAO 方法在同一个事务中执行,这似乎正是您想要的,当您说“服务范围的事务”时,对?

    此外,按照建议,您可能希望将注释从“@Component”更改为 UserServiceImpl 中的“@Service”,并更改为 UserDaoImpl 中的“@Repository”。

    最好的问候。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-02-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-08
      • 1970-01-01
      • 1970-01-01
      • 2010-11-12
      相关资源
      最近更新 更多