caffeine-library / pro-spring-5

🌱 전문가를 위한 스프링5를 읽는 스터디
5 stars 0 forks source link

[question] JPA 사용시 EntityManager를 필드 주입받는 과정이 궁금합니다 #65

Closed binchoo closed 2 years ago

binchoo commented 2 years ago

질문

JPA 사용시 EntityManager를 필드 주입받는 과정이 궁금합니다

상세 내용

챕터8의 빈 구성을 살펴보면 ~EnityManagerFactoryBean를 팩토리 빈으로 등록합니다. 이들은 FactoryBean<EntityManagerFactory> 구현체이므로, 앱 컨텍스트에서 EntityManagerFactory를 획득하는 것에는 의문이 없습니다.

<bean id="transactionManager"
  class="org.springframework.orm.jpa.JpaTransactionManager" p:entityManagerFactory-ref="emf" />

<bean id="emf"
   class="org.springframework.orm.jpa.LocalContainerEntityManagerFatoryBean">
   ...
</bean>

DAO에서는 EntityManager 타입을 선언하고서 @PersistenceContext 어노테이션을 붙여 EntityManager 객체를 획득하여 사용합니다.

@Repository
public class SingerSerivceImpl implements SingerService {
    ...
    @PersistenceContext
    private EntityManager em; // 우리가 명시하지 않은 빈을 어떻게 획득했을까

    @Transactional(readOnly=true)
    @Override
    public List<Singer> findAll() {
        return em.createNamedQuery(Singer.FIND_ALL, Singer.class).getResultList();
    }

의문인 점은 앱 컨텍스트에서 EntityManager를 바로 얻는 것이 어찌 가능한가 입니다.

EntityManagerEntityManagerFactory.createEntityManager() 메서드로 얻어야 하는데 개발자는 이 메서드를 호출한 적이 없으며, 그저 어노테이션을 붙일 뿐입니다.

내부에서 어떤 처리가 있을 것으로 기대됩니다. 해당 내용이 궁금하네요.

연관 챕터

61, #63

참고

https://lng1982.tistory.com/276 https://stackoverflow.com/questions/31335211/autowired-vs-persistencecontext-for-entitymanager-bean

@caffeine-library/readers-pro-spring-5

binchoo commented 2 years ago

우리가 DAO에서 @PersistenceContext를 붙여 EntityManager 객체를 얻을 수 있는 이유는

  1. 어노테이션 후처리기가 이것을 감지
  2. 빈으로 등록된 EntityManagerFactory 객체를 통하여 'EntityManager`가 생성되고 주입되기 때문입니다.

챕터8> 예제 jpa-crud> service> SingerServiceImpl 에서 이런 동작을 확인해 볼 것입니다.

1. EntityManager 주입 지점에 브포 추가

image

원본 코드는 EntityManager의 필드 주입이 사용되고 있습니다. 빈 주입 추적을 용이하게 하려고, 이걸 수정자 주입으로 변경하고 브포를 걸어줍니다.

이후 SpringJPADemo어플리케이션을 디버그합니다.

2. 스택 트레이스 확인

image

InjectionMetadata.InjectedElement.inject 메서드

protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
        throws Throwable {

    if (this.isField) {
        Field field = (Field) this.member;
        ReflectionUtils.makeAccessible(field);
        field.set(target, getResourceToInject(target, requestingBeanName));
    }
    else {
        if (checkPropertySkipping(pvs)) {
            return;
        }
        try {
            Method method = (Method) this.member;
            ReflectionUtils.makeAccessible(method);
            method.invoke(target, getResourceToInject(target, requestingBeanName)); // step into
        }
        catch (InvocationTargetException ex) {
            throw ex.getTargetException();
        }
    }
}

InjectionMetadata.InjectedElement.getResourceToInject 메서드

PersistenceAnnotationBeanPostProcessor.PersistenceElement.getResourceToInject 메서드

@Override
protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
    // Resolves to EntityManagerFactory or EntityManager.
    if (this.type != null) {
        return (this.type == PersistenceContextType.EXTENDED ?
                resolveExtendedEntityManager(target, requestingBeanName) :
                resolveEntityManager(requestingBeanName)); // step into
    }
    else {
        // OK, so we need an EntityManagerFactory...
        return resolveEntityManagerFactory(requestingBeanName);
    }
}

PersistenceAnnotationBeanPostProcessor.PersistenceElement.resolveEntityManager 메서드

private EntityManager resolveEntityManager(@Nullable String requestingBeanName) {
    // Obtain EntityManager reference from JNDI?
    EntityManager em = getPersistenceContext(this.unitName, false);
    if (em == null) {
        // No pre-built EntityManager found -> build one based on factory.
        // Obtain EntityManagerFactory from JNDI?
        EntityManagerFactory emf = getPersistenceUnit(this.unitName);
        if (emf == null) {
            // Need to search for EntityManagerFactory beans.
            emf = findEntityManagerFactory(this.unitName, requestingBeanName);
        }
        // Inject a shared transactional EntityManager proxy.
        if (emf instanceof EntityManagerFactoryInfo &&
                ((EntityManagerFactoryInfo) emf).getEntityManagerInterface() != null) {
            // Create EntityManager based on the info's vendor-specific type
            // (which might be more specific than the field's type).
            em = SharedEntityManagerCreator.createSharedEntityManager(
                    emf, this.properties, this.synchronizedWithTransaction); // step into
        }
        else {
            // Create EntityManager based on the field's type.
            em = SharedEntityManagerCreator.createSharedEntityManager(
                    emf, this.properties, this.synchronizedWithTransaction, getResourceType());
        }
    }
    return em;
}

SharedEntityManagerCreator.createSharedEntityManager 메서드

public static EntityManager createSharedEntityManager(
        EntityManagerFactory emf, @Nullable Map<?, ?> properties, boolean synchronizedWithTransaction) {

    Class<?> emIfc = (emf instanceof EntityManagerFactoryInfo ?
            ((EntityManagerFactoryInfo) emf).getEntityManagerInterface() : EntityManager.class);
    return createSharedEntityManager(emf, properties, synchronizedWithTransaction,
            (emIfc == null ? NO_ENTITY_MANAGER_INTERFACES : new Class<?>[] {emIfc}));
}

public static EntityManager createSharedEntityManager(EntityManagerFactory emf, @Nullable Map<?, ?> properties,
        boolean synchronizedWithTransaction, Class<?>... entityManagerInterfaces) {

    ClassLoader cl = null;
    if (emf instanceof EntityManagerFactoryInfo) {
        cl = ((EntityManagerFactoryInfo) emf).getBeanClassLoader();
    }
    Class<?>[] ifcs = new Class<?>[entityManagerInterfaces.length + 1];
    System.arraycopy(entityManagerInterfaces, 0, ifcs, 0, entityManagerInterfaces.length);
    ifcs[entityManagerInterfaces.length] = EntityManagerProxy.class;
    return (EntityManager) Proxy.newProxyInstance(
            (cl != null ? cl : SharedEntityManagerCreator.class.getClassLoader()),
            ifcs, new SharedEntityManagerInvocationHandler(emf, properties, synchronizedWithTransaction)); // step-into
}

더 깊게 파고들지 않기 위해 이상 마칩니다.

결론