복붙노트

[SPRING] 프록시 때문에 봄베이 주입 실패

SPRING

프록시 때문에 봄베이 주입 실패

스프링 버전 : 3.2.4.RELEASE 및 3.2.9.RELEASE

모키 토 버전 : 1.8.5

나는 통합 테스트를 위해 오래된 프로젝트에 H2 테스트를 도입하려고 노력해 왔으며 몇 가지 문제를 겪어왔다. 트랜잭션이 전파되는 방식 때문에 autowired 클래스를 조롱해야했습니다. 나는 전에 이것을했지만, 지금 나는 심각한 문제에 부딪치고있다. 테스트를 초기화 할 때 다음 오류 메시지가 발생합니다.

조금 더 깊숙이 들어가면 빈은 실제로 프록시입니다. AbstractBeanFactory (라운드 라인 239)를 체크하면 우리는 프록시를 볼 수있다 :

유일한 문제는 이것이 어디에서 오는 것인지 전혀 모른다는 것입니다. 구성 및 종속성을 살펴 보았으므로 아무 곳이나 찾을 수 없습니다.

불행히도이를위한 샘플 프로젝트를 제공 할 수는 없지만 테스트 구성을 살펴 보겠습니다. 테스트를 위해 확장 한 루트 클래스가 있습니다.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:/spring/spring-test-context.xml"})
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
public abstract class AbstractIntegrationTest {
}

이것은 단순히 각 스프링 테스트에로드되고 각 테스트 후에 트랜잭션을 롤백합니다.

내 다른 모듈과이 모듈 사이에는 하나의 차이점이 있지만 봄 구성은 이상한 것도 아닙니다. 이것은 트랜잭션 관리자 및 세션 팩토리입니다.

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

<bean id="hibernateSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
...
</bean>

다른 모듈에서는 entityManagerFactory와 다른 트랜잭션 관리자를 사용하고 있습니다.

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

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
...
</bean>

실제 클래스에는 autowired 필드와 일반적인 @Service 주석이 있습니다.

@Service(value = "TheProcessor")
public final class XMLBatchFileProcessor extends BatchFileProcessor implements IXMLBatchProcessor {

마지막으로 실제 테스트는 다음과 같습니다.

public class XMLITCase extends AbstractIntegrationTest {

    @Resource(name = "TheProcessor")
    @InjectMocks
    private XMLBatchFileProcessor xmlProcessor;

    @Mock
    private ProcessHelper processHelper;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void test() throws Exception {
        Assert.assertNotNull(xmlProcessor);
    }

}

XMLBatchFileProcessor를 인터페이스로 바꾸고 필드를 autowire하면 컴파일하는 데 문제가 없습니다. 그러나 Mockito는 autowired 빈을 조롱 된 객체로 대체하지 않습니다. 만약 그렇다면 @Resource 주석을 사용하고 서비스의 이름을 지정하지 않아도되므로 Proxy 문제를 피할 수 있습니다.

이것에 대한 어떤 도움도 감사 할 것입니다. 나는 세션 팩토리와 그 차이점에 초점을 맞추 겠지만, 내가 완전히 다른 것을 놓치고있는 것이 가능할 수도있다.

Sotirios의 코멘트를 살펴보면, 오늘 아침에 또 다른 모습을 보였습니다. xmlProcessor에 @Transactional 주석이 있으므로, 클래스가 프록시되어야한다는 의미입니다. 최종 선언문을 제거하고 CGLib가 그것을 향상 시키면, initMocks (this)가 호출 될 때 Mockito가 빈을 대체합니다. 그러나 메소드가 호출 될 때 CGLib는 모든 빈을 스프링 확장 버전으로 대체하여 Mockito 버전을 덮어 쓰는 것으로 보입니다.

@Transactional 어노테이션이있는 클래스의 통합 테스트에서 Mockito와 Spring을 모두 사용하는 올바른 방법은 무엇입니까?

해결법

  1. ==============================

    1.좋아요, 일단 클래스가 @ Transactional 주석으로 인해 프록시되었다는 것을 알게되면 문제에 대한 해결책이 명확 해집니다. 필자가해야 할 일은 프록시의 포장을 풀고 조롱 된 객체를 직접 설정하는 것입니다.

    좋아요, 일단 클래스가 @ Transactional 주석으로 인해 프록시되었다는 것을 알게되면 문제에 대한 해결책이 명확 해집니다. 필자가해야 할 일은 프록시의 포장을 풀고 조롱 된 객체를 직접 설정하는 것입니다.

    그래서 내 AbstractIntegrationTest에서 :

    /**
     * Checks if the given object is a proxy, and unwraps it if it is.
     *
     * @param bean The object to check
     * @return The unwrapped object that was proxied, else the object
     * @throws Exception
     */
    public final Object unwrapProxy(Object bean) throws Exception {
        if (AopUtils.isAopProxy(bean) && bean instanceof Advised) {
            Advised advised = (Advised) bean;
            bean = advised.getTargetSource().getTarget();
        }
        return bean;
    }
    

    그럼 내 @ 전에 :

    @Mock
    private ProcessHelper processHelper;
    
    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
    
        IXMLBatchProcessor iXMLBatchProcessor = (IXMLBatchProcessor) unwrapProxy(xmlProcessor);
        ReflectionTestUtils.setField(iXMLBatchProcessor , "processHelper", processHelper);
    }
    

    이렇게하면 모든 @Autowired 클래스가 손상되지 않고 올바른 객체가 주입됩니다.

  2. from https://stackoverflow.com/questions/30648397/spring-bean-injection-failing-due-to-proxy by cc-by-sa and MIT license