JAVASpring

Spring容器 注入管理对象有什么好处?

1.前言


在Spring框架中,将一个类通过注解的方式注入到Spring容器中,一般指在类上使用@Component或其衍生注解(如@Service、@Repository等),用于告诉Spring容器将该类实例化并管理它的生命周期。

通过注入,可以实现Spring框架的IOC(控制反转)和DI(依赖注入)功能,即将对象的创建、管理和使用分离开来,通过Spring容器自动管理对象的生命周期,降低了系统的耦合度,提高了系统的可扩展性、可维护性和可测试性。另外,注入还可以使代码更加简洁、优雅,提高开发效率。

如果不注入在Spring容器中,其实例化和管理需要我们手动完成,即需要通过new关键字手动创建对象,并且需要管理该对象的生命周期,比如手动销毁该对象等。这样会导致代码冗余度高、维护成本高,且不利于横向扩展和单元测试。

因此,将一个类通过注解的方式注入到spring容器中是一个很好的实践,它能够帮助我们简化代码,提高系统的可维护性、扩展性和可测试性。

当我们将一个类通过注解的方式注入到Spring容器中后,可以通过Autowired等注入形式来获取该对象的实例,并直接使用该实例,无需手动创建,例如:

@Component
public class UserService {
 
    @Autowired
    private UserDao userDao;
 
    public User getUserById(Long id) {
        return userDao.getUserById(id);
    }
}
 
@Repository
public class UserDaoImpl implements UserDao {
 
    @Override
    public User getUserById(Long id) {
        // ...查询数据库并返回用户信息
    }
}

在上面的代码中,UserService和UserDaoImpl都被注解为Spring Bean,在UserService中通过@Autowired注解注入了UserDao类型的实例,而UserDaoImpl实现了UserDao接口,同时也被注解为Spring Bean。这样一来,我们可以在其他地方直接使用UserService,而不用关心UserDaoImpl的实例化和管理,Spring容器会自动为我们管理该对象的创建和生命周期。

假设我们希望将UserDaoImpl替换为另一个实现方式,只需要实现UserDao接口并注解为Spring Bean即可,对于UserService及其他调用UserDao的代码来说,都无需修改。这样带来的好处就是可以轻松地进行组件替换,降低了耦合度,提高了代码的可维护性和扩展性。

下面是另一个关于@Component注解的例子,假设我们需要定义一个JDBC连接池,我们可以将其定义为Spring组件,并在其他地方使用它,例如:

@Component
public class ConnectionPool {
    
    private String url;
    private String username;
    private String password;
 
    public ConnectionPool(@Value("${jdbc.url}") String url, 
                          @Value("${jdbc.username}") String username, 
                          @Value("${jdbc.password}") String password) {
        this.url = url;
        this.username = username;
        this.password = password;
    }
 
    public Connection getConnection() {
        //...获取连接
    }
}

在上面的代码中,我们使用@Component注解将ConnectionPool类标记为Spring组件,并使用@Value注解将连接数据库的url、username、password从Spring容器中注入到ConnectionPool构造函数中,从而在ConnectionPool实例化时自动获取这些配置信息,实现了依赖注入的特性。

另外,通过将ConnectionPool组件注入到其他组件中,我们能够轻松地对数据库相关的操作进行管理:

@Service
public class UserService {
 
    @Autowired
    private UserDao userDao;
 
    @Autowired
    private ConnectionPool connectionPool;
 
    //...其他方法
}

在上述代码中,我们使用@Autowired注解将ConnectionPool组件注入到UserService组件中,并在需要连接数据库时使用它。这样通过注入的方式,我们可以避免在Service组件中手动创建ConnectionPool对象或硬编码数据库连接信息,提高了代码的可读性和灵活性。

2.那么很多人就有疑问了,那这样和自定义工具类不是一样的意思吗,只是少了节省了创建对象时new的过程?


其实不完全一样。虽然注入方式和自定义工具类都可以避免手动创建对象、重复构造等问题,但是它们的实现方式和意图不同。

自定义工具类通常是为了提供一些静态方法,它们不依赖于其他类或类的实例,并且无状态,一般是直接使用static关键字修饰方法,例如:

public class StringUtils {
    
    public static boolean isEmpty(String str) {
        return str == null || str.trim().length() == 0;
    }
 
    public static String capitalize(String str) {
        if (str == null || str.length() == 0) {
            return str;
        }
        return Character.toUpperCase(str.charAt(0)) + str.substring(1);
    }
}

在上述代码中,StringUtils并不依赖于其他类或类的实例,它提供静态方法isEmpty和capitalize,用于字符串的常见操作。我们可以直接使用StringUtils类的静态方法,而不用通过new关键字手动创建该类的实例。

相比之下,通过注入的方式可以更好地实现组件的管理和依赖注入。虽然在构造函数中使用@Autowired注解注入Bean对象有些类似于简单的工厂模式,但有区别。工厂模式是在需要对象的时候在工厂中生产一个对象,并直接返回它,而Spring容器会管理对象的生命周期,每次在需要对象时返回其已经管理的同一个实例。此外,Spring容器还提供了很多便捷的功能,例如提供给框架使用的AOP以及用于处理事务和安全性等的模块,从而简化我们的业务逻辑。

因此,虽然自定义工具类和通过注入方式实现功能有一些相似之处,但实现方式和使用场景略有不同,需要考虑具体情况来选择最合适的方式。

3.那么哪些场景用工具类,哪些场景适合用对象注入呢?


使用工具类的场景主要是提供一些通用的静态方法,这些方法通常与具体的业务场景无关,只是完成一些通用的操作,例如字符串的操作、数据类型转换等等。这些方法不需要维护对象的状态,可以直接使用静态方法进行调用,并且不需要在多个对象之间共享状态。

而对象注入则主要用于需要维护状态的功能模块。例如,需要调用数据库的操作,就需要维护数据库连接的状态,因此需要使用注入的方式,将一个数据库连接池对象绑定在需要使用它的组件上。因为 Spring 容器可以负责管理容器中的对象及其生命周期,因此我们无需显式地在代码中创建或销毁对象,只需要将需要依赖的对象注入即可。

因此,我们可以根据功能模块所需的状态是否需要维护来选择使用工具类还是对象注入。如果我们需要维护状态和共享对象,那么使用对象注入是比较合适的方式;如果只需要完成一些通用的操作,就可以使用工具类。当然,两种方式也可以同时使用,只需根据实际需要进行选择即可。