Spring中的延迟加载

    Spring IOC在应用程序启动的时候创建并初始化所有单例bean。这种默认行为可以确保立即捕获任何可能的错误。
    此功能非常适合避免任何运行时的错误,但是在一些场景中,我们希望Spring IOC在启动时不创建bean,而是在应用程序请求时创建它。

    Spring 中@Lazy注解可用于防止单例bean在启动时预初始化。
    @Lazy的作用
@Lazy注解只对单例有用(懒加载是针对单实例来说的),它让bean在Spring启动时不会加载到容器中,只有在代码第一次被调用时才创建一次,
加载到容器中,以后再次调用时,不会再次创建实例,而是直接从容器中拿。

在用Spring开发时,我们常用修饰bean的注解都是单例的,比如@Component@Service@Bean。这些单例默认在Spring启动时加载到容器中,以后再调用时不会重新创建,而是直接从容器中拿。

例子1:利用@ configuration + @Bean创建单例
(1)先创建简单的实体类Use、Person

@Data
public class User {

    private String name;

    private int age;

    public User(){
        System.out.println("初始化:User()");
    }

    public User(String name, int age) {
        System.out.println("初始化:User(String name, int age)");
        this.name = name;
        this.age = age;
    }
}

@Data
public class Person {

    private String name;

    private int age;

    public Person(){
        System.out.println("初始化:Person()");
    }

    public Person(String name, int age) {
        System.out.println("初始化:Person(String name, int age)");
        this.name = name;
        this.age = age;
    }
}

(2)利用@Configuration+@Bean创建单例

/**
 * 默认@Configuration下@Bean定义的是单实例
 * 也就是Spring启动时,就已经加载到Spring容器中了
 * @Lazy是延迟加载,只有第一次调用Bean时才加载到容器中
 */
@Configuration
public class MyConfig {

    @Lazy
    @Bean(name = "myBeanPerson")
    public Person myBeanPerson(){
        return new Person("zoe", 20);
    }

    @Bean(name = "myBeanUser")
    public User myBeanUser(){
        return new User("top", 21);
    }
}

运行项目,只有User的构造器的输出,说明只创建了User的实例

初始化:User(String name, int age)

(3)我们可以选择在bean级别定义/声明@Lazy注解。@Lazy存在且在用@Lazy注解的@Configuration类中的@Bean方法上为false,这表示覆盖’默认懒加载’行为和bean预初始化。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

@Configuration
public class MyConfig {

    @Lazy
    @Bean(name = "myBeanPerson")
    public Person myBeanPerson(){
        return new Person("king", 21);
    }

    @Bean(name = "myBeanUser")
    @Lazy(false)
    public User myBeanUser(){
        return new User("king", 22);
    }
}

运行项目,只有User的构造器的输出,说明只创建了User的实例,也说明@Lazy通过配置false,使得延迟加载失效了

(4)@Autowired导致@Lazy失效的解决方案

@Service
public class BaseServiceImpl implements BaseService {

    public BaseServiceImpl(){
        System.out.println("--- baseServiceImpl ---");
    }

    @Override
    public void myBase() {
    }
}

上述代码在工程启动的时候,会打印:— baseServiceImpl —,说明启动的时候创建了实例,并放入容器中。
如果在类上添加@Lazy,如下图:

@Service
@Lazy
public class BaseServiceImpl implements BaseService {

    public BaseServiceImpl(){
        System.out.println("--- baseServiceImpl ---");
    }

    @Override
    public void myBase() {
    }
}

上图中代码,在工程启动的时候,不会执行构造函数,也就不会打印:— baseServiceImpl —,此时只是创建了代理对象,并没有真正的实例化。
原因就在于添加了@Lazy,使得实例化操作延迟了。
但是,如果此时其他地方引用了该,如下图:

@RestController
@RequestMapping("/api/{edition}/page")
public class MyPageHelperController {

    @Autowired
    private Map<String, IPrint> studentServiceMap;

    @Autowired
    private BaseService baseService;


    @GetMapping("/getStudentName")
    public void getStudentById(String studentId, String source) {
        try {
            //String key = ServiceMapConstants.STUDENT_SERVICE_PREFIX + source;
            String key = source;

            IPrint teacherService = (IPrint) ServiceBeanContext.getProvider(key);
            System.out.println(teacherService);
            if (teacherService != null) {
                teacherService.print(studentId);
            }
        } catch (Exception e) {
        }
    }
}

此时,启动程序的时候,依然会执行构造函数,并打印:— baseServiceImpl —,原因在于MyPageHelperController中引入了BaseService,导致BaseServiceImpl中的延迟加载失效,如果希望此场景下保持延迟加载,需要在MyPageHelperController中继续使用@Lazy,如下图:

@RestController
@RequestMapping("/api/{edition}/page")
public class MyPageHelperController {

    @Autowired
    private Map<String, IPrint> studentServiceMap;

    @Autowired
    @Lazy
    private BaseService baseService;


    @GetMapping("/getStudentName")
    public void getStudentById(String studentId, String source) {
        try {
            //String key = ServiceMapConstants.STUDENT_SERVICE_PREFIX + source;
            String key = source;

            IPrint teacherService = (IPrint) ServiceBeanContext.getProvider(key);
            System.out.println(teacherService);
            if (teacherService != null) {
                teacherService.print(studentId);
            }
        } catch (Exception e) {
        }
    }
}

再启动程序,由于MyPageHelperController中添加了@Lazy,BaseServiceImpl中也添加了@Lazy,此时延迟加载生效,不会执行BaseServiceImpl的构造方法,也就不再打印— baseServiceImpl —


常用注解功能介绍:

@Configuration:配置类注解,类似xml中的注解,生成的配置类bean 实例是代理,执行@bean 的方法之前 先判断单例池中是否已有该对象实例,确保@bean 注解的对象单例,也就是说是针对单例的。

@Bean:跟@Configuration配合使用,标注在方法上,实例化一个bean,默认以方法名称做bean的id,可以指定别名,初始化方法和销毁方法

@Scop:跟@bean配置使用,标注在方法上,调整作用域,指定bean创建的定义

             prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。每次获取的时候才会调用方法创建对象;

            singleton:单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中,以后每次获取就是直接从容器                                          (map.get())中拿

           request:同一次请求创建一个实例(web环境下)

           session:同一个session创建一个实例(web环境下)

@Lazy:懒加载,配和@Component及其子注解使用,被注解的bean 不是不实例化,而是先创建一个代理bean 注入容器启动不创建对象。第一次使用(获 取)Bean创建对象,并初始化;

你可能感兴趣的:(Java,初始化,spring,java)