Статьи

Как заключить в капсулу бобовые

Насколько я знаю, Spring Framework не предоставляет никакого механизма для инкапсуляции компонентов Spring, кроме как в отдельных контекстах. Поэтому, если у вас есть открытый класс, зарегистрированный в контейнере Spring Inversion of Control, он может быть автоматически подключен к любому бину Spring из той же конфигурации контекста. Это очень сильно, но это также очень опасно. Разработчики могут легко соединить бобы вместе. При отсутствии дисциплины команда может легко выстрелить себе в ногу. К сожалению, я работал над одним монолитным проектом, где команда стреляла в себя из пистолета-пулемета. Проводка часто нарушала правила наслоения. Никто не может легко понять, что зависит от чего. График зависимости бина был просто сумасшедшим. Это серьезная проблема в больших приложениях.

К счастью, есть один простой способ инкапсуляции Spring bean. Spring прекрасно работает с модификатором доступа по умолчанию на уровне класса. Таким образом, вы можете создать пакет bean-компонента, который может использоваться только в текущем пакете. Просто и мощно. Давайте посмотрим на пример:

01
02
03
04
05
06
07
08
09
10
package net.lkrnac.blog.spring.encapsulatebean.service;
 
import org.springframework.stereotype.Service;
 
@Service
class AddressService {
    public String getAddress(String userName){
        return "3 Dark Corner";
    }
}

Этот простой bean-компонент подключен к другому пакету:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
package net.lkrnac.blog.spring.encapsulatebean.service;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
@Service
public class UserService {
    private AddressService addressService;
 
    @Autowired
    public UserService(AddressService addressService) {
        this.addressService = addressService;
    }
     
    public String getUserDetails(String userName){
        String address = addressService.getAddress(userName);
        return String.format("User: %s, %s", userName, address);
    }
}

Основной контекст просто сканирует оба бина:

01
02
03
04
05
06
07
08
09
10
11
12
package net.lkrnac.blog.spring.encapsulatebean;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
 
@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Application {
}

Вот тест, чтобы доказать, что он работает нормально:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package net.lkrnac.blog.spring.encapsulatebean;
 
import net.lkrnac.blog.spring.encapsulatebean.service.UserService;
 
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
public class ApplicationTests {
    @Autowired
    private UserService userService;
     
    @Test
    public void isPackagePrivateBeanCalled(){
        //GIVEN - spring context defined by Application class
         
        //WHEN
        String actualUserDetails = userService.getUserDetails("john");
         
        //THEN
        Assert.assertEquals("User: john, 3 Dark Corner", actualUserDetails);
    }
}

Я считаю, что все должны рассмотреть возможность использования модификатора доступа по умолчанию для каждого нового компонента. Очевидно, что в каждом пакете должен быть какой-то общедоступный компонент. Но не на каждом бобе. Исходный код есть на GitHub .

Ссылка: Как инкапсулировать Spring bean от нашего партнера JCG Любоса Крнака в