Use of Spring Foundation Components --@Scope and @Lazy and Their Verification

Keywords: Spring

@ Scope and @Lazy and Their Verification

@Scope

We all know that a Bean is registered in the Spring container. By default, it is a single instance. We can verify it and continue with our previous example. Here is our configuration class

@Configuration
@ComponentScan(value = "com.test")
public class TestConfig {
    @Scope
    @Bean
    public Dog dog(){
        return new Dog("Golden hair",3);
    }
}

Then we test in the test class.

public class TestMain {
    @Test
    public void test(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TestConfig.class);
        Object dog1 = applicationContext.getBean("dog");
        Object dog2 = applicationContext.getBean("dog");
        System.out.println(dog1 == dog2);
    }
}

The print result is: true

So what if we want our beans to be multi-instance? We need to use our @Scope annotation.

We can see that @Scope has four values, of which prototype is our multi-instance. Let's verify it.

@Configuration
@ComponentScan(value = "com.test")
public class TestConfig {
    @Scope("prototype")
    @Bean
    public Dog dog(){
        return new Dog("Golden hair",3);
    }
}

Run our test class again and print the result as follows:false

In addition, we need to note that our multi-instance beans are actually lazy to load, that is, IOC containers do not call methods to create objects when they start, but go back to create an object every time they get it. Let's go ahead and verify that, first of all, we print a sentence about the construction of the Dog class so that we can see the results easily.

public class Dog {
    private String name;
    private int age;
    public Dog() {
        System.out.println("Calling a parametric-free constructor...");
    }
    public Dog(String name, int age) {
        System.out.println("Call a parametric constructor...");
        this.name = name;
        this.age = age;
    }

    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 "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Next, change the Bean to a single instance in the configuration class

@Configuration
@ComponentScan(value = "com.test")
public class TestConfig {
    @Scope("singleton")
    @Bean
    public Dog dog(){
        return new Dog("Golden hair",3);
    }
}

Finally, we run the test class

public class TestMain {
    @Test
    public void test(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TestConfig.class);
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String beanName : beanDefinitionNames)
            System.out.println(beanName);
    }
}

The results are as follows:

Call a parametric constructor...
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
testConfig
userController
userDao
userService
dog

We see that the constructor is actually called, so when the IOC container starts up, the singleton Bean will call the method to create the object and put it in the IOC container. Then we can get it directly every time. Next, let's look at the multiple instances, just change the parameters of Scope in the configuration class to prototype.

@Configuration
@ComponentScan(value = "com.test")
public class TestConfig {
    @Scope("prototype")
    @Bean
    public Dog dog(){
        return new Dog("Golden hair",3);
    }
}

Running the above test classes, we can find the following results:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
testConfig
userController
userDao
userService
dog

It was found that the construction method of Dog was not actually invoked. Let's try calling multiple instances in the test class.

public class TestMain {
    @Test
    public void test(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TestConfig.class);
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String beanName : beanDefinitionNames)
            System.out.println(beanName);
        System.out.println("-------------------------------");
        applicationContext.getBean("dog");
        for (String beanName : beanDefinitionNames)
            System.out.println(beanName);
    }
}

The results are as follows:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
testConfig
userController
userDao
userService
dog
-------------------------------
//Call a parametric constructor...
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
testConfig
userController
userDao
userService
dog

We can clearly identify and validate our above features about @Scope.

@Lazy

Now what if we want to ask a single instance Bean to load lazily? Then we can use the @Lazy annotation, which is definitely for a single instance, because multiple instances are not needed at all.

@Configuration
@ComponentScan(value = "com.test")
public class TestConfig {
    @Scope("singleton")
    @Lazy
    @Bean
    public Dog dog(){
        return new Dog("Golden hair",3);
    }
}

We just need to add the @Lazy annotation to the method that needs lazy loading processing. The verification method is consistent with the above and can perform the verification.

Posted by 2kau on Sat, 17 Aug 2019 00:57:18 -0700