DI, IoC


スプリングを使うと、「注入依存」や「注入制御」をたくさん聞いたことがあるかもしれません.
制御反転(IoC):プログラムの制御フローは外部で管理され、フレームワークはIoCである.フレームワークがプログラムの流れを制御しているから!
「依存注入」(Dependency Injection、依存/依存注入)はスプリングだけでは使用されません.
JAVAを例に!
JAVA
8989 v 1:DIアプリケーションX
Domain
public class Person {

    private Long id;
    private String name;
    private int age;

    public Person(Long id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
    
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
    
    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
Repository
public class PersonRepository {

    private static Map<Long, Person> store = new HashMap<>();

    public void save(Person person) {
        store.put(person.getId(), person);
    }
}
DIに集中するためDBには保存せず、HashMapを使用した.
TMI-HashMapは同時性の問題を引き起こす可能性があるので、実際の操作ではConcurrentHashMapを使用します.
  • ConcurrentHashMap:HashTableクラスの欠点を補いマルチスレッド環境で使えるクラス
  • Service
    public class PersonService {
    
        private final PersonRepository personRepository = new PersonRepoitory();
    
        public void join(Person person) {
            personRepository.save(person);
        }
    }
    Application
    public class PersonApp {
    
        public static void main(String[] args) {
            PersonService personService = new PersonService();
            Person person = new Person(1L, "KJ", 25);
            personService.join(person);
            
            System.out.println(person.toString()); // Person{id=1, name='KJ', age=25}
    
        }
    }
    PersonServiceクラスではPersonRepositoryクラスオブジェクトを使用する必要があります.PersonServiceクラスでPersonRepositoryオブジェクトを作成すると、この2つのオブジェクトの間に強い結合があります.結合度が強くなるのは良い方法ではありません.結合度が低く、凝集度が高いのは良い方法です.
    既存の強い結合をジェネレータを通過するDIに弱めましょう.
    8989 v 2:DIアプリケーション
    Domain
    省略
    Repository
    省略
    Service
    public class PersonService {
    
        private final PersonRepository personRepository;
    
        public PersonService(PersonRepository personRepository) {
            this.personRepository = personRepository;
        }
    
        public void join(Person person) {
            personRepository.save(person);
        }
    }
    PersonServiceクラスの作成者が作成されました.生成者だけになったらどうしますか?外に何か置くべきではないでしょうか.
    そうですね.AppConfig類はあなたに注入されます.
    💉 AppConfig 💉
    public class AppConfig {
    
        public PersonService personService() {
            return new PersonService(new PersonRepository());
        }
    
    }
    Application
    public class PersonApp {
    
        public static void main(String[] args) {
        
            AppConfig appConfig = new AppConfig();
            PersonService personService = appConfig.personService();
    
            Person person = new Person(1L, "KJ", 25);
    
            personService.join(person);
    
            System.out.println(person.toString()); // Person{id=1, name='KJ', age=25}
            
        }
    }
    このように、アプリケーション実行時(実行時)に外部(AppConfigクラス)から実際の実装オブジェクトを作成してクライアントに渡し、クライアントとサーバの実際の依存関係を接続することを依存関係注入(DI)と呼ぶ.
    これで以前は强い结合が弱まった!!
    Springに切り替え
    AppConfig
    @Configuration
    public class AppConfig {
    
        @Bean  // Bean은 스프링 컨테이너에 등록이 된다
        public PersonService personService() {
            return new PersonService(new PersonRepository());
        }
        
        /*
         * 이 주석 부분은 위 코드와 같다
        @Bean
        public PersonService personService() {
            return new PersonService(personRepository());
        }
    
        @Bean
        public PersonRepository personRepository() {
            return new PersonRepository();
        }
        */
    
    }
    AppConfigというクラスが作成されました.
    @Configuration Annotationを追加します.1つ以上の@Beanを提供するクラスには、@Configurationを追加します.
    スプリングでは、bean名はデフォルトでメソッド名(PersonService)として登録されています.
    @Bean(name="空の名前")から直接名前を付けることもできます.
    Application
    public class PersonApp {
    
        public static void main(String[] args) {
        
            ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
            // AppConfig 클래스의 빈을 스프링 컨테이너에 등록해 관리한다.
            PersonService personService = ac.getBean("personService", PersonService.class);
            // personService이라는 이름을 가진 빈을 가져와 변수에 할당
    
            Person person = new Person(1L, "KJ", 25);
    
            personService.join(person);
    
            System.out.println(person.toString()); // Person{id=1, name='KJ', age=25}
            
        }
    }
    ここでApplicationContextはIoC容器(DI容器、Spring容器)です.スプリングコンテナは、AnnotationベースのJava設定クラスであってもよいし、XMLベースであってもよい.
    上のAppConfigはAnnotationベースのJava設定クラスを使用して作成したSpringコンテナで、以下のようにXMLを使用して作成することもできます.
    AppConfig - XML
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="personRepository" class="com.kjkim.kj.PersonRepository" />
    
        <bean id="personService" class="com.kjkim.kj.PersonService">
            <constructor-arg name="personRepository" ref="personRepository" />
        </bean>
        
    </beans>
    XMLを使用してbean登録を行う場合は、アプリケーションコードも以下の変更を行います.
    Application - XML
    public class PersonApp {
    
        public static void main(String[] args) {
        
            ApplicationContext ac = new GenericXmlApplicationContext("appConfig.xml");
            PersonService personService = ac.getBean("personService", PersonService.class);
    
            Person person = new Person(1L, "KJ", 25);
    
            personService.join(person);
    
            System.out.println(person.toString()); // Person{id=1, name='KJ', age=25}
            
        }
    }
    上記の方法のようにbeanを直接登録することもできますが、spring機能を使って、@Component、@ComponentScanのAnnotationをこれよりも簡単に使うこともできます.
    @Controller、@Service、@RepositoryなどのAnnotationには@Component、@SpringBootアプリケーションには@ComponentScanが含まれています.
    したがって,@Beanを手動で追加しなくても,@Componentと@ComponentScanの2種類のAnnotationが自動的に提供される.
    また、依存関係注入には@Autowiredを使用できます.@Autowiredを使用すると、スプリングコンテナは自動的にスプリングギャップを検索して注入します.ジェネレータ、Setter、フィールド、および従来の方法で依存関係を注入することがあるが、ジェネレータでDIを使用することを推奨する.
    スプリングは難しいですが、スプリングに対する理解が多ければ多いほど、開発者を快適にすることができます!
    リファレンス
    基礎科目