preface

Recently shared some source code, framework design things. I found that people were not particularly enthusiastic, considering that most of them were actually writing code; This time I’m going to share a little down to earth: some tips for using SpringBoot.

Not much fancy stuff, but it all worked.

Masking external dependencies

The first one is shielding external dependencies. What does that mean?

For example, there is no such trouble in daily development:

Projects are based on distributed services such as SpringCloud or Dubbo, and you rely on many basic services.

For example, the generation of an order number, obtaining user information and so on.

Because of the service split, these functions are provided as interfaces in other applications, but luckily I can Mock them out.

But what if you want to launch your application and run through your own code?

There are several ways to do this:

  • Run all services locally.
  • Switch the registry to the development environment and rely on the services of the development environment.
  • Push the code directly to the development environment for self-testing.

Looks like you can do all three. I used to do that. But there are a few hiccups:

  • Local boot may be a lot of services, all up the computer can support also two said, in case there is a problem with the service can not go on.
  • Depending on the development environment is the premise of the network is open, and another problem is that the development environment code is very unstable and may affect your testing.
  • Push to the development environment should be a more reliable solution, but if you want to debug only log method, no localdebugEfficient and efficient.

So how do you solve the problem? You can debug locally without having to start other services.

You can also Mock out other external dependencies using a single test.

The general process is divided into the following steps:

  • SpringBootAfter startupSpringFind the one you need to blockAPIbean(Usually this interface is given toSpringManaged).
  • Manual frombeanDelete this from the containerbean.
  • Recreate one of theAPIThe object is just throughMockCome out.
  • Then manually register inbeanIn the container.

Take this code as an example:

    @Override
    public BaseResponse<OrderNoResVO> getUserByHystrix(@RequestBody UserReqVO userReqVO) {

        OrderNoReqVO vo = new OrderNoReqVO();
        vo.setAppId(123L);
        vo.setReqNo(userReqVO.getReqNo());
        BaseResponse<OrderNoResVO> orderNo = orderServiceClient.getOrderNo(vo);
        return orderNo;
    }
Copy the code

This is a SpringCloud application.

It relies on orderServiceClient to get an order number.

OrderServiceClient is an external API that is also managed by Spring.

Replace the original Bean

The next step is to replace the original Bean.

@Component
public class OrderMockServiceConfig implements CommandLineRunner {

    private final static Logger logger = LoggerFactory.getLogger(OrderMockServiceConfig.class);

    @Autowired
    private ApplicationContext applicationContext;

    @Value("${excute.env}")
    private String env;

    @Override
    public void run(String... strings) throws Exception {

        // Non-local environments are not processed
        if ("dev".equals(env) || "test".equals(env) || "pro".equals(env)) {
            return;
        }

        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();

        OrderServiceClient orderServiceClient = defaultListableBeanFactory.getBean(OrderServiceClient.class);
        logger.info("======orderServiceClient {}=====", orderServiceClient.getClass());

        defaultListableBeanFactory.removeBeanDefinition(OrderServiceClient.class.getCanonicalName());

        OrderServiceClient mockOrderApi = PowerMockito.mock(OrderServiceClient.class,
                invocationOnMock -> BaseResponse.createSuccess(DateUtil.getLongTime() + ""."mock orderNo success"));

        defaultListableBeanFactory.registerSingleton(OrderServiceClient.class.getCanonicalName(), mockOrderApi);

        logger.info("======mockOrderApi {}=====", mockOrderApi.getClass()); }}Copy the code

The CommandLineRunner interface is implemented to call the Run () method after the Spring container has been initialized.

The code is very simple, and it is simply a matter of deciding what the environment is, after all, except for the local environment, it is necessary to actually call the remote service.

The next step is to get the bean and manually delete it.

A key step:

OrderServiceClient mockOrderApi = PowerMockito.mock(OrderServiceClient.class,
                invocationOnMock -> BaseResponse.createSuccess(DateUtil.getLongTime() + ""."mock orderNo success"));

defaultListableBeanFactory.registerSingleton(OrderServiceClient.class.getCanonicalName(), mockOrderApi);
Copy the code

A new OrderServiceClient object is created and manually registered into the Spring container.

The first piece of code uses the POWERMockito. mock API, which creates a proxy object that returns the default return for all methods that call OrderServiceClient.

BaseResponse.createSuccess(DateUtil.getLongTime() + ""."mock orderNo success"))
Copy the code

Test if we call the interface without replacing it and start OrderService locally:

Fallback is not configured, so an error is reported indicating that the service cannot be found.

When replacing the bean:

The second request did not return an error, and we got our default return.

The log also shows that OrderServiceClient has been Mock proxy in the end and does not call the real method.

Configure the encryption

Next up is configuring encryption, which should be a basic feature.

For example, some accounts and passwords in our configuration files should be saved in ciphertext.

So this time an open source component is used for encryption and decryption, and springBoot-friendly only takes a few pieces of code to complete.

  • First, encrypt the configuration to be encrypted into ciphertext based on the encryption password.
  • Replace the configuration saved in plain text.
  • Decrypt when used again.

To use this package, you only need to import one dependency:

<dependency>
    <groupId>com.github.ulisesbocchio</groupId>
    <artifactId>jasypt-spring-boot-starter</artifactId>
    <version>1.14</version>
</dependency>
Copy the code

At the same time, write a single test to generate ciphertext based on the password, which can also be saved in the configuration file:

jasypt.encryptor.password=123456
Copy the code

Ciphertext is then generated in a single test.

    @Autowired
    private StringEncryptor encryptor;

    @Test
    public void getPass(a) {
        String name = encryptor.encrypt("userName");
        String password = encryptor.encrypt("password");
        System.out.println(name + "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -");
        System.out.println(password + "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -");

    }
Copy the code

Then you just need to use ciphertext.

Since I’m encrypting the database username and password, there’s also a decryption process.

Using an enhanced interface to Spring Beans:

@Component
public class DataSourceProcess implements BeanPostProcessor {


    @Autowired
    private StringEncryptor encryptor;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        if (bean instanceof DataSourceProperties){
            DataSourceProperties dataSourceProperties = (DataSourceProperties) bean;
            dataSourceProperties.setUsername(encryptor.decrypt(dataSourceProperties.getUsername())) ;
            dataSourceProperties.setPassword(encryptor.decrypt(dataSourceProperties.getPassword()));
            return dataSourceProperties ;
        }

        returnbean; }}Copy the code

This allows you to revert to plain text when you actually use it.

You can also configure the previous password in the startup command:

Java - Djasypt. The encryptor. Password = password - jar target/jasypt - spring - the boot - demo - 0.0.1 - the SNAPSHOT. The jarCopy the code

conclusion

So that’s two tips, you have more tips for SpringBoot, please leave a comment.

Some examples of the above code can be found here:

Github.com/crossoverJi…

Welcome to pay attention to the public account to communicate: