Spring framework Day14:配置类的Lite模式和Full模式

前言

Lite模式和Full模式是指在软件或系统中的不同配置选项。一般来说,Lite模式是指较为简洁、轻量级的配置,而Full模式则是指更加完整、功能更丰富的配置。

Lite模式通常会去除一些不常用或占用资源较多的功能,以提高系统的运行效率和响应速度。这样可以在资源有限或对系统性能要求较高的情况下使用,比如在低配置的计算设备上或移动设备上。

Full模式则包含了系统或软件的所有功能和特性,同时可能会占用更多的内存和处理器资源。这样可以满足用户对更多功能和高级选项的需求,适合在高配置的计算设备上使用。

选择Lite模式还是Full模式取决于用户的需求和使用场景。如果用户需要更快的响应速度和轻量级的操作体验,或者使用的是资源受限的设备,那么Lite模式可能更适合。而如果用户需要更多功能和高级选项,并且使用的是高配置的设备,那么Full模式可能更适合。

需要注意的是,Lite模式和Full模式的具体配置内容可能因软件或系统而异,用户在选择时应根据具体情况进行判断和比较。

一、开始学习

1、新建项目,结构如下
2、添加 spring 依赖
XML 复制代码
 
    <!-- spring 的核心依赖 -->
    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.23</version>
        </dependency>
 
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.4.5</version>
        </dependency>
 
    </dependencies>
3、在 service 包下新建一个 UserService 接口,在 impl 包下新建一个 UserServiceImpl 实现类

UserService 接口

java 复制代码
public interface UserService {
 
    void add();
 
}

UserServiceImpl 实现类

java 复制代码
@Slf4j
public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        log.info("添加用户");
    }
}
4、在 controller 包下新建一个 USerController 类
java 复制代码
@RequiredArgsConstructor
public class UserController {

    private final UserService userService;

    public void addUser() {

        userService.add();
    }

}

这段代码使用了Lombok库中的@RequiredArgsConstructor注解,该注解会生成一个带有final修饰的构造方法,用于对类中的final属性进行初始化。在这段代码中,通过使用@RequiredArgsConstructor注解实现了UserController类的构造方法,在构造方法中初始化了userService属性,避免了手动编写构造方法的繁琐。

需要注意的是,使用@RequiredArgsConstructor注解时,必须保证被注解的属性为非空(final)属性,否则在编译时就会出现错误。因此,在使用该注解时,需要仔细检查每一个被注解的属性是否满足要求。

5、在config 包下新建一个 AppConfig 配置类
java 复制代码
@Slf4j
@Configuration
public class AppConfig {

    /**
     * 装配 Usersevice
     *
     * @return
     */
    @Bean
    public UserService userService() {
        return new UserServiceImpl();
    }

    /**
     * 装配 UserController 并注入 UserService
     *
     * @return
     */
    @Bean
    public UserController userController() {

        // 得到需要注入的 Bean
        UserService userService = userService();
        log.info("1:" + userService);
        UserService userService1 = userService();
        log.info("2:" + userService1);
        // 将 bean 通过构造方法注入
        return new UserController(userService);
    }

}

这段代码是一个使用Spring框架的Java配置类,其中包含了两个@Bean方法用于将UserService和UserController装配到Spring容器中。

在方法userService()中,使用@Bean注解声明一个UserService的实例,并返回该实例。该方法会在Spring容器启动时被调用,将UserService加入到Spring容器中。

在方法userController()中,同样使用@Bean注解声明一个UserController的实例,并注入一个UserService的实例。在这个方法内部,通过调用userService()方法来获取已经装配到Spring容器中的UserService实例。然后,将得到的UserService实例作为参数传递给UserController的构造方法,创建并返回UserController的实例。

需要注意的是,在这段代码中,还使用了Lombok库中的@Slf4j注解,用于自动生成日志记录器。通过在类上添加该注解,可以在Bean方法中使用log记录日志,避免手动编写日志记录器带来的繁琐。

@Configuration注解是Spring框架中的一个核心注解,用于标识一个类为配置类。配置类主要用于定义和组织Bean的创建和装配过程。

具体来说,@Configuration注解通常与@Bean注解一起使用。在一个带有@Configuration注解的类中,可以使用@Bean注解声明方法,并将该方法返回的对象注册为一个Bean。Spring容器在启动时会扫描这些类,并实例化这些Bean并将其添加到容器中。

6、测试
java 复制代码
public class Main {

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserController bean = context.getBean(UserController.class);
        bean.addUser();
    }

}

运行结果

此时,可以看到我们输出的 userservice 和 userservice1 的地址是一样的,因为使用了代理模式,使用了 @Configuration 默认情况下,当proxyBeanMethods设置为true时,Spring容器会将@Bean注解的方法的返回值缓存起来作为单例对象。这样可以确保每次获取该Bean都是同一个实例,提高了性能和一致性。

二、禁用代理模式

1、修改 AppConfig 配置类
java 复制代码
@Slf4j
@Configuration(proxyBeanMethods = false)
public class AppConfig {

    /**
     * 装配 Usersevice
     *
     * @return
     */
    @Bean
    public UserService userService() {
        return new UserServiceImpl();
    }

    /**
     * 装配 UserController 并注入 UserService
     *
     * @return
     */
    @Bean
    public UserController userController() {

        // 得到需要注入的 Bean
        UserService userService = userService();
        log.info("1:" + userService);
        UserService userService1 = userService();
        log.info("2:" + userService1);
        // 将 bean 通过构造方法注入
        return new UserController(userService);
    }

}

@Configuration(proxyBeanMethods = false) 禁用了代理模式。在这种情况下,Spring容器将不会使用CGLIB动态代理来创建userService()userController()方法返回的Bean实例。

对于userService()方法,它没有任何依赖关系,每次调用都返回新的UserServiceImpl实例,无需代理。

对于userController()方法,它依赖于userService()方法返回的Bean实例。由于禁用了代理模式,每次调用userService()方法时都会返回一个新的实例,并且这个实例会被注入到UserController的构造方法中。

所以,在日志输出中看到的userServiceuserService1的值是不同的,它们引用的是不同的UserServiceImpl对象。

通过禁用代理模式,您可以确保每个Bean都是原始的实例,并且不存在代理对象的干扰。

2、测试
java 复制代码
public class Main {

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserController bean = context.getBean(UserController.class);
        bean.addUser();
    }

}

运行结果

3、编译异常

这个警告信息的意思是,您在一个@Configuration配置类中使用了@Bean注解的方法,并且将proxyBeanMethods设置为false,这可能会导致一些问题。

proxyBeanMethods@Configuration注解的一个属性,用于指定是否使用代理模式来管理@Bean注解的方法中创建的对象,默认值为true。当设置为true时,Spring容器将使用CGLIB动态代理来管理这些@Bean注解的方法和它们依赖的其他Bean。当设置为false时,Spring容器将直接调用该方法并返回该方法的返回值作为Bean实例。

如果您将proxyBeanMethods设置为false,则表示您不希望使用代理模式来管理Bean,但是同时使用@Bean注解的方法将直接调用,而没有使用Spring容器来管理这些Bean,这可能会导致一些问题。例如,有些Bean之间可能会产生循环依赖问题,导致应用程序无法启动。

为了避免这个问题,建议您将proxyBeanMethods设置为true,或者尽可能使用依赖注入,而不是直接调用@Bean注解的方法。

但是呢,并不会影响程序的正常运行。

三、使用代理模式和禁用代理模式的区别

代理模式和禁用代理模式的主要区别在于Spring容器处理@Bean注解的方法时所采用的方式。

proxyBeanMethods设置为true时,Spring容器会使用CGLIB动态代理来管理@Bean注解的方法和它们之间的依赖关系。

proxyBeanMethods设置为false时,Spring容器不使用代理,直接调用@Bean注解的方法并返回该方法的返回值作为Bean实例。

1、使用代理模式有以下几个优点:
  1. 循环依赖解决:如果存在循环依赖,代理模式可以通过提前暴露代理对象来解决循环依赖问题。代理对象可以在被依赖之前就可以被注入,从而解决了循环依赖的限制。

  2. 单例对象缓存:默认情况下,当proxyBeanMethods设置为true时,Spring容器会将@Bean注解的方法的返回值缓存起来作为单例对象。这样可以确保每次获取该Bean都是同一个实例,提高了性能和一致性。

2、而禁用代理模式则可能会导致以下问题:
  1. 循环依赖无法解决:如果存在循环依赖,则会导致应用程序无法启动。

  2. 缺少单例对象缓存:由于每次调用@Bean注解的方法都会返回一个新的实例,因此可能会导致性能问题或意外的行为。

因此,通常情况下建议使用代理模式,并将proxyBeanMethods设置为true,默认就是 true

四、通过代码了解代理模式

1、在 proxy 包下新建 A、B、BProxy 类

B类

java 复制代码
/**
 * @Date 2023-10-08
 * @Author qiu
 *
 * 目标对象(被代理的对象)
 */
@Slf4j
public class B {

    public void say() {
        log.info("Hello world!");
    }

}

BProxy 类

java 复制代码
/**
 * @Date 2023-10-08
 * @Author qiu
 * <p>
 * 代理对象
 */
@Slf4j
public class BProxy {

    /**
     * 声明一个被代理的对象
     */
    private B b;

    public BProxy(B b) {
        this.b = b;
    }

    public void say() {
        // 目标方法前
        before();

        // 调用目标对象的 say 方法
        b.say();

        // 目标方法后
        after();
    }

    /**
     * 调用目标方法前要执行的逻辑
     */
    private void before() {
        log.info("调用目标之前执行的业务逻辑.....");
    }

    private void after() {
        log.info("调用目标之后执行的业务逻辑.....");
    }

}
2、新建 A 类 测试
java 复制代码
public class A {

    public static void main(String[] args) {
        // 创建代理对象
        BProxy bProxy = new BProxy(new B());
        bProxy.say();

    }

}

运行结果

在这个示例中,BProxy类是B类的代理类。代理类持有一个被代理对象 b 的引用,并且在调用 say() 方法时,会在目标方法前后执行额外的逻辑。

BProxy类中的 before() 方法和 after() 方法分别代表了目标方法调用前和调用后需要执行的逻辑。在 say() 方法中,首先会执行 before() 方法,然后调用被代理对象 bsay() 方法,最后再执行 after() 方法。

A 类的 main 方法中,创建了一个 BProxy 对象,并调用其 say() 方法。由于 BProxyB 类的代理类,因此在调用 say() 方法时,额外的逻辑会被执行。

通过静态代理模式,我们可以在不修改原始类 B 的情况下,对其进行功能扩展或增强。这种方式可以在一些场景下提供更灵活的控制和定制逻辑。

3、分析,下图

所谓的代理就是可以理解为是一个中间类(中介),当我们需要调用 B 类的方法时,我们通过一个代理类来调用 B 类的方法,为什么需要代理类呢,是因为当我们调用 B 类的方法时需要增加其他的业务逻辑,但是又不能对 B 类去修改,所以使用代理类,在代理类中调用 B 类的方法,并且在代理类中调用 say 方法时,在之前和之后都做了相应业务逻辑处理。而我们用户调用的还是 say 方法,用户不用关心代理类中做了什么事情,它只关注它调用的是它想要的方法即可。

代理类:就是在调用目标对象的方法时,对目标对象的方法进行了增强或者修改。

五、总结

配置类 Lite 模式(非代理模式)和 Full 模式(代理模式)

* 当配置类上标注了 @configuration 注解时,并且 proxyBeanMethods

* 属性设置为 true,此时就是 Full 模式,Full 模式就是 Spring 会为当前

* 配置类创建一个代理对象,从而创建配置类中所有的 @Bean 注解的方法

* 这样每当调用配置类只能的 Bean 方法之前,会从容器中进行检查 Bean 实例,

* 并返回容器中存在的 Bean 对象。

* 反之就是 Lite 模式,Lite 模式下配置类并不会被代理。每次调用 Bean

* 方法只是纯粹的调用,并不会经过代理。

Lite 模式(非代理模式)就是:@Configuration(proxyBeanMethods = false)

Full 模式(代理模式)就是:@Configuration

六、gitee 案例

案例完整地址:https://gitee.com/qiu-feng1/spring-framework.git

相关推荐
橙子家3 分钟前
关于列式存储(Column-base Storage)的几个要点解读
数据库
跃上青空5 分钟前
Java如何优雅的使用fastjson2进行枚举序列化/反序列化,欢迎探讨
java·开发语言
Mr.456715 分钟前
Spring Boot 集成 PostgreSQL 表级备份与恢复实战
java·spring boot·后端·postgresql
架构师沉默34 分钟前
为什么一个视频能让全国人民同时秒开?
java·后端·架构
٩( 'ω' )و26037 分钟前
MySQL基础
数据库·mysql
生命不息战斗不止(王子晗)42 分钟前
mysql基础语法面试题
java·数据库·mysql
umeelove351 小时前
Java进阶(ElasticSearch的安装与使用)
java·elasticsearch·jenkins
redaijufeng1 小时前
Node.js(v16.13.2版本)安装及环境配置教程
java
知识分享小能手1 小时前
MongoDB入门学习教程,从入门到精通,MongoDB应用程序设计知识点梳理(9)
数据库·学习·mongodb
齐齐大魔王1 小时前
linux-线程编程
java·linux·服务器