잊지 않겠습니다.

Spring에서 xml이나 annotation을 이용한 component-scan에 의해서 등록되는 Bean중에서 @PersistenceContext의 동작원리는 다음과 같습니다.

ComponentScanBeanDefinitionParser

ComponentScanBeanDefinitionParser.registerComponents를 통해 Bean을 등록시켜준다. annotationConfig가 enable된 상태에서 context의 Bean들을 scan시켜줍니다.

protected void registerComponents(
        XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) {

    Object source = readerContext.extractSource(element);
    CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);

    for (BeanDefinitionHolder beanDefHolder : beanDefinitions) {
        compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder));
    }

    // Register annotation config processors, if necessary.
    boolean annotationConfig = true;
    if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {
        annotationConfig = Boolean.valueOf(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));
    }
    if (annotationConfig) {
        Set<BeanDefinitionHolder> processorDefinitions =
                AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
        for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
            compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));
        }
    }

    readerContext.fireComponentRegistered(compositeDef);
}

AnnotationConfigUtil

AnnotationConfigUtil을 통해 Annotation이 설정된 Bean들을 얻어오기 시작합니다. registerAnnotationConfigProcessors에 의해서 각각의 Type에 따라 다른 등록을 Process 처리하게 됩니다.

if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
    RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
    def.setSource(source);
    beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}

if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
    RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
    def.setSource(source);
    beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}

if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
    RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
    def.setSource(source);
    beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}

// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
    RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
    def.setSource(source);
    beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}

// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
    RootBeanDefinition def = new RootBeanDefinition();
    try {
        ClassLoader cl = AnnotationConfigUtils.class.getClassLoader();
        def.setBeanClass(cl.loadClass(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME));
    }
    catch (ClassNotFoundException ex) {
        throw new IllegalStateException(
                "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
    }
    def.setSource(source);
    beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
}

처리되는 Bean들은 다음과 같습니다.

  • CONFIGURATIONANNOTATIONPROCESSORBEANNAME : @Configuration이 적용된 객체들을 처리하는 Process 입니다.
  • AUTOWIREDANNOTATIONPROCESSORBEANNAME : Bean 객체들의 @Autowired가 적용된 객체들을 처리하는 Process 입니다.
  • REQUIREDANNOTATIONPROCESSORBEANNAME : @Required Annotation이 처리됩니다.
  • COMMONANNOTATIONPROCESSORBEANNAME : EJB, JNDI 지원 객체들의 Property들을 처리하는 Process 입니다.
  • PERSISTENCEANNOTATIONPROCESSORBEANNAME : JPA를 처리합니다. 실질적으로 JPA의 EntityManagerFactory 객체의 경우에는 PersistenceAnnotationBeanPostProcess 에서 처리하게 됩니다.
PersistenceAnnotationBeanPostProcess

이제 마지막 코드입니다. PersistenceAnnotationBeanPostProcess는 @PersistenceContext로 지정된 Property에 EntityManagerFactory에서 새로운 EntityManager를 생성하거나, Transaction에 의해 기존ㅇ에 생성된 EntityManager를 반환시켜서 사용하게 해줍니다. 이에 대한 코드는 다음과 같습니다.

먼저, EntityManagerFactory를 생성하고 주입하고 있습니다.

public PersistenceElement(Member member, PropertyDescriptor pd) {
    super(member, pd);
    AnnotatedElement ae = (AnnotatedElement) member;
    PersistenceContext pc = ae.getAnnotation(PersistenceContext.class);
    PersistenceUnit pu = ae.getAnnotation(PersistenceUnit.class);
    Class<?> resourceType = EntityManager.class;
    if (pc != null) {
        if (pu != null) {
            throw new IllegalStateException("Member may only be annotated with either " +
                    "@PersistenceContext or @PersistenceUnit, not both: " + member);
        }
        Properties properties = null;
        PersistenceProperty[] pps = pc.properties();
        if (!ObjectUtils.isEmpty(pps)) {
            properties = new Properties();
            for (PersistenceProperty pp : pps) {
                properties.setProperty(pp.name(), pp.value());
            }
        }
        this.unitName = pc.unitName();
        this.type = pc.type();
        this.synchronizedWithTransaction = (synchronizationTypeAttribute == null ||
                "SYNCHRONIZED".equals(ReflectionUtils.invokeMethod(synchronizationTypeAttribute, pc).toString()));
        this.properties = properties;
    }
    else {
        resourceType = EntityManagerFactory.class;
        this.unitName = pu.unitName();
    }
    checkResourceType(resourceType);
}

다음에, 각 Resource에 EntityManager를 주입합니다.

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

최종적으로 얻어지는 EntityManager는 resolveEntityManager method를 통해서 주입되게 됩니다. EntityManager의 코드는 다음과 같습니다.

private EntityManager resolveEntityManager(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);
        }
        else {
            // Create EntityManager based on the field's type.
            em = SharedEntityManagerCreator.createSharedEntityManager(
                    emf, this.properties, this.synchronizedWithTransaction, getResourceType());
        }
    }
    return em;
}

항시 SharedEntityManager를 이용해서 @Transactional annotation과 같이 사용할 수 있도록 동작하고 있는 것을 볼 수 있습니다. 코드를 보시면 아시겠지만, JPA를 사용하실 때는 반드시 @PersistenceContext annotation을 이용해야지 됩니다.

Posted by Y2K
,