用最直白的语言来说,依赖注入(DI)和自动装配的核心确实可以理解为"给对象的属性赋值"。但这种赋值不是普通的赋值,而是一种有特定规则和优点的赋值方式。
普通赋值:
// 普通赋值方式
public class OrderService {
private OrderRepository repository;
public OrderService() {
this.repository = new OrderRepositoryImpl(); // 自己new对象赋值
}
}
DI赋值:
// DI赋值方式
public class OrderService {
private OrderRepository repository;
// 由外部传入(注入)值
public OrderService(OrderRepository repository) {
this.repository = repository; // 别人传给我,我不自己new
}
}
自动装配更进一步,连手动传值都不需要了,容器会自动找到合适的值赋给属性:
public class OrderService {
@Autowired // 自动找到OrderRepository类型的bean赋值到这里
private OrderRepository repository;
}
虽然本质是赋值,但这种赋值方式有三大特殊之处:
就像:
可以赋不同的实现类而不改代码:
// 测试时赋Mock值
OrderService service = new OrderService(new MockOrderRepository());
// 生产时赋真实值
OrderService service = new OrderService(new JdbcOrderRepository());
Spring可以控制:
@Bean // 告诉Spring:这个对象需要你管理赋值
public DataSource dataSource() {
return new HikariDataSource();
}
public class UserService {
// 这两个属性需要被"赋值"
private UserRepository userRepository;
private PasswordEncoder passwordEncoder;
}
Spring容器的工作流程:
public class ProductService {
private ProductRepository repo;
// 创建ProductService时必须赋值
public ProductService(ProductRepository repo) {
this.repo = repo;
}
}
public class PaymentService {
private PaymentGateway gateway;
// 专门留个赋值入口
public void setGateway(PaymentGateway gateway) {
this.gateway = gateway;
}
}
public class ReportService {
@Autowired // 直接给这个字段赋值
private DataProvider provider;
}
@Autowired
private UserRepository repository;
// 找UserRepository类型的bean来赋值
@Autowired
private UserRepository userRepository;
// 找名为"userRepository"的bean来赋值
@Autowired
@Qualifier("jdbcUserRepo") // 明确指定赋哪个值
private UserRepository repository;
想换实现?只需换赋的值,不用改类代码:
// 配置A:赋MySQL实现
@Bean
public UserRepository userRepository() {
return new JdbcUserRepository();
}
// 配置B:赋MongoDB实现
@Bean
public UserRepository userRepository() {
return new MongoUserRepository();
}
测试时可以赋模拟值:
@Test
void testService() {
// 赋一个Mock值用于测试
UserService service = new UserService(new MockUserRepository());
}
所有赋值需求一目了然:
public class OrderService {
// 明确声明我需要这些值才能工作
private final OrderRepository repo;
private final PaymentService payment;
private final NotificationService notify;
public OrderService(OrderRepository repo,
PaymentService payment,
NotificationService notify) {
//...赋值
}
}
普通赋值编译时就能发现问题,DI赋值可能到运行时才报错:
@Autowired
private NonExistentBean bean; // 编译不报错,运行才失败
不知道值从哪里来:
@Service
public class MyService {
@Autowired // 魔法般的出现了值
private MysteryComponent component;
}
可能赋了不需要的值:
public class SimpleService {
@Autowired // 其实只需要A,但BC也被自动赋进来了
private DependencyA a;
@Autowired
private DependencyB b;
@Autowired
private DependencyC c;
}
public class GoodService {
private final Dependency dep;
// 明确所有必需的赋值
public GoodService(Dependency dep) {
this.dep = dep;
}
}
public class FlexibleService {
private OptionalDependency optDep;
// 可选赋值用setter
public void setOptDep(OptionalDependency optDep) {
this.optDep = optDep;
}
}
public class CarefulService {
@Autowired // 只在确实必要时用
private HardToTestComponent component;
}
确实可以把DI和自动装配理解为一种特殊的赋值方式,但这种赋值:
就像做菜:
理解了这个核心,就能更自然地掌握Spring的DI和自动装配机制。