spring @bean

spring @Bean

스프링에 자주 사용되는 어노테이션으로 @Bean에 대해 살짝 맛만 볼라고 한다.
저번에 한번 얘기를 했는데 ConfigurationClassParser클래스 doProcessConfigurationClass 메소드에 여러 메타 어노테이션을 파싱하는 부분이 있다.

...

// Process individual @Bean methods
Set<MethodMetadata> beanMethods = sourceClass.getMetadata().getAnnotatedMethods(Bean.class.getName());
for (MethodMetadata methodMetadata : beanMethods) {
    configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}

...

doProcessConfigurationClass 메소드의 Bean을 파싱하는 부분이다.
하지만 메타 정보만 갖고 있고 이때 인스턴스는 하지 않는다.(이 부분은 저번에도 얘기 한듯 하다)
그럼 우리가 흔히 쓰는 @Bean 은 언제 인스턴스 하는지 알아보자

@Bean
public ModelClass modelClass(){
    return new ModelClass();
}

예로 위와 같은 빈이 있다 가정하자
저번에 얘기 했듯이 Spring은 내부적으로 getBean을 호출하면서 그때 인스턴스도 같이 한다.(물론 안하는 Context도 있다. 안한다는 것보다 할 필요가 없는 듯 해서 그런거 같다.)
그 중에 @Bean 어노테이션들은 BeanMethodInterceptor aop 프록시를 통해 해당 빈을 호출한다.
그리고 나서 DefaultSingletonBeanRegistry클래스에 싱글톤 빈이라고 저장해둔다.(아마도 여기에 모든 싱글톤이 있는 듯하다)

private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

저기에는 실제 인스턴스화된 빈들이 저장 되어 있는 곳이다.
왜 이얘길 하냐면 우리들이 흔히 쓰는 getBean을 호출 할때 singletonObjects 멤버 변수에서 꺼내서 주는 거다.

Object sharedInstance = getSingleton(beanName);

getBean을 호출 할때 저 함수를 호출한다. getSingleton 함수를 보자

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

첫 줄만 봐도 알 것이다. Map으로 저장된 빈들을 꺼내서 사용한다.

그럼 Bean의 스코프가 prototype 일 경우는 어떠할까?
실질적으로 하는 행동은 같다.
AbstractBeanFactory 클래스의 일부분이다.

if (mbd.isSingleton()) {
    sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
        @Override
        public Object getObject() throws BeansException {
            try {
                return createBean(beanName, mbd, args);
            }
            catch (BeansException ex) {
                // Explicitly remove instance from singleton cache: It might have been put there
                // eagerly by the creation process, to allow for circular reference resolution.
                // Also remove any beans that received a temporary reference to the bean.
                destroySingleton(beanName);
                throw ex;
            }
        }
    });
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

else if (mbd.isPrototype()) {
    // It's a prototype -> create a new instance.
    Object prototypeInstance = null;
    try {
        beforePrototypeCreation(beanName);
        prototypeInstance = createBean(beanName, mbd, args);
    }
    finally {
        afterPrototypeCreation(beanName);
    }
    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}

코드만 봐도 비슷한 일을 하고 있다 대신 싱글톤일 경우엔 getSingleton 을 호출 하면서 싱글톤으로 등록 할 뿐이다.
저기 위에서 말했듯이 getBean을 호출 할 경우 getSingleton에서 싱글톤이 있는지 없는지 확인을 하는데 prototype일 경우엔 없으니 계속 Bean을 새로 호출 할 것이다.

스코프가 session이거나 request일 경우에도 비슷하게 동작 하지 않나 싶다. (확인을 안해서 믿지 마시길)

다 아는 내용 이겠지만 테스트를 해봤다.

  • singleton 일 경우
ModelClass hello = applicationContext.getBean("modelClass", ModelClass.class);
ModelClass hello2 = applicationContext.getBean("modelClass", ModelClass.class);
System.out.println(hello == hello2);
//true
  • prototype 일 경우
ModelClass hello = applicationContext.getBean("modelClass", ModelClass.class);
ModelClass hello2 = applicationContext.getBean("modelClass", ModelClass.class);
System.out.println(hello == hello2);
//false

이렇게 @Bean이 어떻게 동작하는지 조금 알아봤다.

spring Aware 순서

spring Aware order

이건 메모 추후에 다신 확인

AbstractApplicationContext finishBeanFactoryInitialization(beanFactory)

AbstractAutowireCapableBeanFactory 클래스에서 아래와 같이 셋팅

BeanNameAware
BeanClassLoaderAware
BeanFactoryAware
    private void invokeAwareMethods(final String beanName, final Object bean) {
        if (bean instanceof Aware) {
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
            if (bean instanceof BeanClassLoaderAware) {
                ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
            }
            if (bean instanceof BeanFactoryAware) {
                ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
            }
        }
    }

ApplicationContextAwareProcessor 클래스에서 아래와 같이 셋팅

EnvironmentAware
EmbeddedValueResolverAware
ResourceLoaderAware
ApplicationEventPublisherAware
MessageSourceAware
ApplicationContextAware
    private void invokeAwareInterfaces(Object bean) {
        if (bean instanceof Aware) {
            if (bean instanceof EnvironmentAware) {
                ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
            }
            if (bean instanceof EmbeddedValueResolverAware) {
                ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(
                        new EmbeddedValueResolver(this.applicationContext.getBeanFactory()));
            }
            if (bean instanceof ResourceLoaderAware) {
                ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
            }
            if (bean instanceof ApplicationEventPublisherAware) {
                ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
            }
            if (bean instanceof MessageSourceAware) {
                ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
            }
            if (bean instanceof ApplicationContextAware) {
                ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
            }
        }
    }

XXXXXAwareProcessor를 참고 하면 됨 BeanPostProcessor를 구현한걸로 보임. 모두다 그렇지는 않음

Spring 4.2.5의 전체 목록. 일단 기본적인것만 확인 했다.

ApplicationContextAware, ApplicationEventPublisherAware, BeanClassLoaderAware, BeanFactoryAware, BeanNameAware, BootstrapContextAware, EmbeddedValueResolverAware, EnvironmentAware, ImportAware, LoadTimeWeaverAware, MessageSourceAware, NotificationPublisherAware, PortletConfigAware, PortletContextAware, ResourceLoaderAware, SchedulerContextAware, ServletConfigAware, ServletContextAware

JAVA8에 대해 알아보자(번외)

java8 번외

갑자기 생각나서 쓴다.
java8 의 추가된 기능 중 interface에 관한 내용이다.
java8 interface는 static 메소드도 구현 가능하다.
이로써 util interface를 구현 할 수 있다는 거다.

또 다른 하나는 default 키워드가 추가 되어 메소드 바디를 갖는 인터페이스가 추가 되었다.
그러면서 다중 상속의 개념이 다시 등장했다.
그래서 한번 테스트를 해봤다.

interface Man {
    default void print(String name) {
        System.out.println("Man :"  + name);
    }
}

interface WoMan {
    default void print(String name) {
        System.out.println("WoMan :"  + name);
    }
}

이런 두개의 인터페이스가 있다고 가정하자.

그리고 두개의 인터페이스를 상속해보자

class Hermaphrodite implements Man,WoMan {
}

그럼 intellij에선 컴파일 에러가 난다.
같은 동일한 메소드가 다 구현 되어 있어서 에러를 내는거 같다.
만약 메소드가 동일 하지 않다면 에러를 내뱉지 않는다.
이로써 다아이몬드 상속은 해결된건가?

다음 코드도 한번 보자.

interface Human{
    default void print(String name) {
        System.out.println("Human "  + name);
    }
}

interface Man extends Human{
    default void print(String name) {
        System.out.println("Man :"  + name);
    }
}

interface WoMan extends Human{
    default void print(String name) {
        System.out.println("WoMan :"  + name);
    }
}

class Hermaphrodite implements Man,WoMan {

    @Override
    public void print(String name) {
        System.out.println("Hermaphrodite : " + name);
    }
}

네개의 클래스와 인터페이스를 만들고 해보자.

public static void main(String[] args) {
    Human man = new Hermaphrodite();
    man.print("wonwoo");
}

실행 결과는 Hermaphrodite : wonwoo
무조건 구현해야 되기 때문에 나올 수 밖에 없다.
그럼 Woman의 default 메소드를 제거하고 Hermaphrodite 구현체를 제거해보자.

interface WoMan extends Human{
}

class Hermaphrodite implements Man,WoMan {
}

다시 실행해보자.

public static void main(String[] args) {
    Human man = new Hermaphrodite();
    man.print("wonwoo");
}

기대했던 결과는

Man wonwoo

일 것이다.
그럼 다음 코드를 보자.

interface Human {
    default void print(String name) {
        System.out.println("Human " + name);
    }
}

interface Man extends Human {
    default void print(String name) {
        System.out.println("Man " + name);
    }
}

interface WoMan extends Human {
    default void print(String name) {
        System.out.println("Man " + name);
    }
}

class Hermaphrodite implements Man, WoMan {
    @Override
    public void print(String name) {
        Man.super.print("wonwoo");
        WoMan.super.print("no!");
    }
}
Human man = new Hermaphrodite();
man.print("wonwoo");
wonwoo
no!

이렇게 하면 다중상속이 된다.