programing

Spring의 ApplicationContext.getBean이 불량으로 간주되는 이유는 무엇입니까?

copyandpastes 2022. 7. 3. 18:38
반응형

Spring의 ApplicationContext.getBean이 불량으로 간주되는 이유는 무엇입니까?

나는 일반적인 스프링 질문을 했다.스프링 콩을 자동 주조하여 여러 사람이 스프링 콩을 불렀다고 응답하도록 했습니다.ApplicationContext.getBean()가능한 한 피해야 합니다.왜런그 일일?

그 외에 Spring에서 작성하도록 설정한 콩에 액세스하려면 어떻게 해야 합니까?

된 Spring에 이었습니다.ApplicationContextLiorH에 의해 기술된 오브젝트.

수정안

아래 답변은 받아들이겠습니다만, 여기 Martin Fowler의 대체 견해가 있습니다.Martin Fowler는 의존성 주입과 서비스 로케이터를 사용하는 것의 장점에 대해 설명합니다(이것은 기본적으로 랩을 호출하는 것과 같습니다).ApplicationContext.getBean()를 참조해 주세요.

부분적으로, Fowler는 다음과 같이 말합니다. "서비스 로케이터를 사용하면 애플리케이션 클래스는 로케이터에 대한 메시지로 명시적으로 [서비스를] 요구합니다. 주입을 사용하면 명시적 요청이 없는 경우 서비스가 응용 프로그램클래스에 표시되므로 제어가 반전됩니다. 통제의 반전은 프레임워크의 공통적인 특징이지만 그에 따른 대가가 따릅니다. 이는 이해하기 어렵고 디버깅을 시도할 때 문제가 발생하는 경향이 있습니다. 따라서 전반적으로 저는 필요하지 않는 한 [컨트롤의 반전된 통제는 필요 없습니다. 이것이 나쁜 것이라고 말하는 것은 아닙니다.그냥 좀 더 솔직한 대안보다 정당화할 필요가 있다고 생각합니다.

다른 질문에 대한 코멘트에서 언급했지만 컨트롤의 반전이라는 개념은 수업의 어느 누구도 자신이 의존하는 오브젝트를 어떻게 얻는지 알지 못하게 하는 것입니다.따라서 언제든지 사용하는 특정 종속성의 구현 유형을 쉽게 변경할 수 있습니다.또한 종속성의 모의 구현을 제공할 수 있기 때문에 클래스를 쉽게 테스트할 수 있습니다.마지막으로, 그것은 수업을 더 단순하게 만들고 그들의 핵심 책임에 더 집중할 수 있게 합니다.

" "ApplicationContext.getBean()트!롤!!!!!!!!!!!지정된 콩 이름에 대해 구성되는 내용을 변경하는 것은 여전히 쉽지만, 이제 클래스는 스프링에 직접 의존하여 종속성을 제공하므로 다른 방법으로는 얻을 수 없습니다.테스트 수업에서 직접 모의 실장을 만들어 직접 전달할 수는 없습니다.이 기본적으로 의존성 주입 용기로 봄의 목적을 좌절시키죠.이는기본적으로 Spring의 의존성 주입용기로서의 목적을 좌절시킵니다.

당신이 말하고 싶은 모든 곳:

MyClass myClass = applicationContext.getBean("myClass");

예를 들어 다음과 같이 메서드를 선언해야 합니다.

public void setMyClass(MyClass myClass) {
   this.myClass = myClass;
}

그 후, 설정:

<bean id="myClass" class="MyClass">...</bean>

<bean id="myOtherClass" class="MyOtherClass">
   <property name="myClass" ref="myClass"/>
</bean>

봄은 그때 자동으로그러면스프링이 자동으로 주입됩니다를 주입할 것이다.myClass안으로에myOtherClass..

모든 것을 이렇게 선언하면, 그 근원에는 다음과 같은 것이 있습니다.

<bean id="myApplication" class="MyApplication">
   <property name="myCentralClass" ref="myCentralClass"/>
   <property name="myOtherCentralClass" ref="myOtherCentralClass"/>
</bean>

MyApplication가장 중심 수업, 당신의 프로그램에 있는 모든 다른 서비스는 간접적으로나마에 달려 있나.는 가장 중심적인 클래스이며 프로그램 내의 다른 모든 서비스에 간접적으로 의존합니다.언제, 당신의 부트스트랩에 시 부츠 트래핑main방법, 메서드,호출할 수 있습니다 부를 수 있습니다.applicationContext.getBean("myApplication")하지만 하지만전화할 필요는 없습니다를 호출해야 하지 않아야 한다.getBean()다른어떤 데였어도!어디든지!

Inversion of Control(IoC)보다 Service Locator를 선호하는 이유는 다음과 같습니다.

  1. 서비스 로케이터는 다른 사람들이 당신의 코드를 따라하기 훨씬 쉽습니다.IoC는 '매직'이지만 유지보수 프로그래머는 복잡한 스프링 구성 및 모든 위치를 이해하고 오브젝트의 배선 방법을 파악해야 합니다.

  2. IoC는 구성 문제를 디버깅하는 데 매우 적합합니다.일부 클래스의 응용 프로그램에서는 잘못 구성되면 응용 프로그램이 시작되지 않으며 디버거에서 무슨 일이 일어나고 있는지 확인할 기회가 없을 수 있습니다.

  3. IoC는 주로 XML 기반입니다(주석은 상황을 개선하지만 여전히 많은 XML이 존재합니다).즉, 개발자는 Spring에 의해 정의된 모든 매직태그를 알지 못하면 당신의 프로그램에서 작업할 수 없습니다.이제 자바에 대해 아는 것만으로는 부족합니다.이로 인해 경험이 적은 프로그래머(즉,서비스 로케이터와 같은 단순한 솔루션이 동일한 요건을 충족할 때 보다 복잡한 솔루션을 사용하는 것은 실제로는 좋지 않은 설계입니다).또한 XML 문제의 진단 지원은 Java 문제에 대한 지원보다 훨씬 약합니다.

  4. 의존관계 주입은 대규모 프로그램에 더 적합합니다.대부분의 경우 추가적인 복잡성은 그럴 가치가 없습니다.

  5. 스프링은 "나중에 구현을 변경하고 싶은 경우"에 자주 사용됩니다.Spring IOC의 복잡함 없이 이를 달성할 수 있는 다른 방법이 있습니다.

  6. 웹 애플리케이션(Java EE WAR)의 경우 Spring 컨텍스트는 컴파일 시 효과적으로 바인딩됩니다(단, 사용자가 폭발적 전쟁에서 컨텍스트를 탐색하는 것을 원하지 않는 경우).Spring에서 속성 파일을 사용하도록 할 수 있지만 servlet 속성 파일은 미리 정해진 위치에 있어야 합니다. 즉, 같은 상자에 동시에 여러 개의 servlet을 배포할 수 없습니다.Spring with JNDI를 사용하여 서블릿 시작 시 속성을 변경할 수 있지만 관리자가 변경할 수 있는 파라미터에 JNDI를 사용하는 경우 스프링 자체의 필요성이 줄어듭니다(JNDI는 사실상 서비스 로케이터이기 때문에).

  7. Spring을 사용하면 Spring이 메서드로 디스패치되면 프로그램 제어가 손실될 수 있습니다.이것은 편리하고 많은 종류의 어플리케이션에서 동작하지만, 모든 어플리케이션에서 동작하는 것은 아닙니다.초기화 중에 태스크(스레드 등)를 작성해야 하는 경우 또는 콘텐츠가 WAR에 바인드된 시기를 Spring이 몰랐던 수정 가능한 리소스가 필요한 경우 프로그램 흐름을 제어해야 할 수 있습니다.

봄은 거래 관리에 매우 좋고 몇 가지 장점이 있습니다.다만, IoC는 많은 상황에서 과잉 엔지니어링되어 유지관리자에게 불필요한 복잡성을 초래할 수 있습니다.IoC를 사용하지 않는 방법을 생각하지 않고 IoC를 자동으로 사용하지 마십시오.

application-context.xml에 클래스를 포함하면 getBean을 사용할 필요가 없어집니다.하지만 그마저도 실제로는 불필요합니다.독립 실행형 응용 프로그램을 작성하고 있고 application-context.xml에 드라이버 클래스를 포함하지 않으려면 다음 코드를 사용하여 Spring에서 드라이버 종속성을 자동 연결할 수 있습니다.

public class AutowireThisDriver {

    private MySpringBean mySpringBean;    

    public static void main(String[] args) {
       AutowireThisDriver atd = new AutowireThisDriver(); //get instance

       ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
                  "/WEB-INF/applicationContext.xml"); //get Spring context 

       //the magic: auto-wire the instance with all its dependencies:
       ctx.getAutowireCapableBeanFactory().autowireBeanProperties(atd,
                  AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);        

       // code that uses mySpringBean ...
       mySpringBean.doStuff() // no need to instantiate - thanks to Spring
    }

    public void setMySpringBean(MySpringBean bean) {
       this.mySpringBean = bean;    
    }
}

앱의 일부 측면을 사용해야 하는 독립형 클래스(테스트용 등)가 있을 때 이 작업을 여러 번 수행해야 했지만 실제로 앱의 일부가 아니기 때문에 애플리케이션 컨텍스트에 포함시키고 싶지 않습니다.또한 String name을 사용하여 콩을 조회할 필요가 없습니다.이것은 항상 추하다고 생각되어 왔습니다.

스프링과 같은 것을 사용하는 것의 가장 멋진 이점 중 하나는 물건을 철사로 연결할 필요가 없다는 것이다.제우스의 머리가 갈라지고 당신의 클래스가 나타납니다.필요에 따라 모든 의존관계가 생성되고 연결되었습니다.신기하고 환상적이에요.

더 말하면 할수록 말한다ClassINeed classINeed = (ClassINeed)ApplicationContext.getBean("classINeed");를 마술이 끝나면 당신에겐.마법이 줄어들수록...적은 코드 거의 항상 낫다.을 사용법가 정말 이니 、 왜왜 、 왜??????

단, 첫 번째 오브젝트를 작성해야 하는 것은 분명합니다.getBean()을 통해 콩 한두 개를 얻는 주요 방법에는 문제가 없지만, 사용할 때마다 봄의 모든 마법을 실제로 사용하지 않기 때문에 피해야 합니다.

그 동기는 명확하게 봄에 의존하지 않는 코드를 쓰는 것이다.이렇게 하면 컨테이너를 바꾸도록 선택할 경우 코드를 다시 작성할 필요가 없습니다.

컨테이너를 코드에 보이지 않는 것으로 생각해 주십시오.요구를 요청하지 않고 마법처럼 제공합니다.

의존관계 주입은 "service locator" 패턴의 대척점입니다.이름으로 종속성을 조회하려면 DI 컨테이너를 제거하고 JNDI와 같은 기능을 사용하는 것이 좋습니다.

「」를 사용합니다.@Autowired ★★★★★★★★★★★★★★★★★」ApplicationContext.getBean()정말 똑같아요.어느 쪽이든 콘텍스트에 설정되어 있는 빈을 얻을 수 있습니다.또, 어느 쪽이든 코드는 봄에 의존합니다.Application Context(애플리케이션 컨텍스트).거거 in in ! 즉, ,즉, 인즉과 같은 입니다.

ApplicationContext context = new ClassPathXmlApplicationContext("AppContext.xml");

어플리케이션에서 한 번만 사용해야 합니다.

스프링 구내 중 하나는 커플링을 피하는 것입니다.인터페이스, DI, AOP를 정의하여 사용하고 ApplicationContext.getBean() :-)을 사용하지 않도록 합니다.

그 이유 중 하나는 테스트 가능성입니다.예를 들어 다음과 같은 수업이 있습니다.

interface HttpLoader {
    String load(String url);
}
interface StringOutput {
    void print(String txt);
}
@Component
class MyBean {
    @Autowired
    MyBean(HttpLoader loader, StringOutput out) {
        out.print(loader.load("http://stackoverflow.com"));
    }
}

이 콩은 어떻게 검사하나요?예:

class MyBeanTest {
    public void creatingMyBean_writesStackoverflowPageToOutput() {
        // setup
        String stackOverflowHtml = "dummy";
        StringBuilder result = new StringBuilder();

        // execution
        new MyBean(Collections.singletonMap("https://stackoverflow.com", stackOverflowHtml)::get, result::append);

        // evaluation
        assertEquals(result.toString(), stackOverflowHtml);
    }
}

쉽죠?

스프링(주석 때문에)에 의존하는 동안 코드를 변경하지 않고 스프링에 대한 의존성을 제거할 수 있습니다(주석 정의만). 테스트 개발자는 스프링의 작동 방식에 대해 아무것도 알 필요가 없습니다(어쨌든 스프링과는 별도로 코드를 검토하고 테스트할 수 있습니다).

Application Context(애플리케이션 컨텍스트). 그 는 조롱할 가 있어요.ApplicationContext이치노더미 구현이 필요하거나 Mockito와 같은 모의 프레임워크를 사용할 수 있습니다.

@Component
class MyBean {
    @Autowired
    MyBean(ApplicationContext context) {
        HttpLoader loader = context.getBean(HttpLoader.class);
        StringOutput out = context.getBean(StringOutput.class);

        out.print(loader.load("http://stackoverflow.com"));
    }
}
class MyBeanTest {
    public void creatingMyBean_writesStackoverflowPageToOutput() {
        // setup
        String stackOverflowHtml = "dummy";
        StringBuilder result = new StringBuilder();
        ApplicationContext context = Mockito.mock(ApplicationContext.class);
        Mockito.when(context.getBean(HttpLoader.class))
            .thenReturn(Collections.singletonMap("https://stackoverflow.com", stackOverflowHtml)::get);
        Mockito.when(context.getBean(StringOutput.class)).thenReturn(result::append);

        // execution
        new MyBean(context);

        // evaluation
        assertEquals(result.toString(), stackOverflowHtml);
    }
}

이것은 꽤 가능성이 있지만, 나는 대부분의 사람들이 첫 번째 옵션이 더 우아하고 테스트를 더 쉽게 한다는 것에 동의할 것이라고 생각한다.

실제로 문제가 되는 유일한 옵션은 다음과 같습니다.

@Component
class MyBean {
    @Autowired
    MyBean(StringOutput out) {
        out.print(new HttpLoader().load("http://stackoverflow.com"));
    }
}

이를 테스트하려면 많은 노력이 필요합니다.그렇지 않으면 각 테스트에서 stackoverflow에 접속하려고 합니다.네트워크 장애(또는 stackoverflow 관리자가 과도한 액세스환율로 인해 사용자를 차단)가 발생하면 랜덤으로 테스트에 불합격하게 됩니다.

으로 말하면, 저는 '아까는 없다', '아까는 없다'라고.ApplicationContext직접적 오류는 자동으로 발생하므로 반드시 피해야 합니다.그러나 더 나은 옵션이 있는 경우(대부분의 경우), 더 나은 옵션을 사용합니다.

즉, 의존성 주입(제어 반전 또는 IOC)에 의존합니다.즉, 컴포넌트는 필요한 컴포넌트로 구성됩니다.이러한 의존관계는 (컨스트럭터 또는 설정자를 통해) 주입됩니다.그러면 직접 얻을 수 없습니다.

ApplicationContext.getBean()에서는 컴포넌트 내에서 빈 이름을 명시적으로 지정해야 합니다.대신 IoC를 사용함으로써 사용하는 컴포넌트를 구성에 따라 결정할 수 있습니다.

이를 통해 애플리케이션을 다른 컴포넌트 구현으로 쉽게 재접속하거나 시뮬레이트된 변종(테스트 중에 데이터베이스에 영향을 주지 않도록 시뮬레이트된 DAO 등)을 제공함으로써 테스트용 객체를 쉽게 구성할 수 있습니다.

다른 사람들은 일반적인 문제를 지적하고 있습니다(유효한 답변이기도 합니다). 하지만 저는 한 가지만 더 말씀드리겠습니다.그것은 절대 하지 않는 것이 아니라 가능한 한 적게 하는 것입니다.

보통 이것은 부트스트래핑 중에 딱 한 번 실행된다는 것을 의미합니다.다음으로 "root" bean에 액세스하면 다른 의존관계를 해결할 수 있습니다.이는 기본 서블릿과 같이 재사용 가능한 코드일 수 있습니다(웹 앱을 개발하는 경우).

getBean()이 필요한 상황은 다음 두 가지뿐입니다.

다른 사람들은 main()에서 getBean()을 사용하여 독립 실행형 프로그램의 "main" bean을 가져옵니다.

getBean()의 또 다른 용도는 인터랙티브 사용자 설정에 따라 특정 상황의 빈 메이크업이 결정되는 경우입니다.예를 들어 부팅 시스템의 일부가 scope='bean' 정의의 getBean()을 사용하여 데이터베이스 테이블을 루프한 다음 추가 속성을 설정합니다.아마도 애플리케이션 컨텍스트 XML을 다시 쓰는 것보다 데이터베이스 테이블을 더 쉽게 조정하는 UI가 있을 것입니다.

getBean을 사용하는 것이 타당할 때가 또 있습니다.이미 존재하는 시스템을 재구성하는 경우 스프링 컨텍스트 파일에서 종속성이 명시적으로 호출되지 않습니다.getBean에 콜을 보내는 것으로 프로세스를 시작할 수 있기 때문에 한번에 모든 것을 연결할 필요가 없습니다.이렇게 하면 스프링 구성을 서서히 구축할 수 있습니다.각 부품은 시간이 지남에 따라 제자리에 배치되고 비트는 적절히 정렬됩니다.getBean에 대한 콜은 최종적으로 대체되지만 코드 구조를 이해하거나 이 구조가 없기 때문에 getBean에 대한 콜의 수를 줄이고 더 많은 빈을 배선하는 프로세스를 시작할 수 있습니다.

단, 서비스 로케이터 패턴이 필요한 경우도 있습니다.예를 들어 컨트롤러 bean이 있는 경우 이 컨트롤러에 기본 서비스 bean이 있을 수 있습니다.이것은 구성에 의해 삽입된 의존관계일 수 있습니다.또한 이 컨트롤러는 현재 또는 나중에 호출할 수 있는 추가 서비스 또는 새로운 서비스가 많이 있을 수 있습니다.이러한 서비스를 취득하려면 서비스 로케이터가 필요합니다.

Application Context 대신 Configurable Application Context를 사용해야 합니다.

언급URL : https://stackoverflow.com/questions/812415/why-is-springs-applicationcontext-getbean-considered-bad

반응형