Spring 控制反转

一、传统模式

依赖的创建,是通过new依赖对象,手动管理对象的生命周期(创建,初始化,销毁)。

IoC模式:容器根据配置创建并注入依赖,容器自动管理对象的完整生命周期。

控制反转的本质是将依赖的创建权生命周期管理权从代码转移到外部容器。

public class TestService {
    public void save() {
        MySqlDao mysqlDao = new MySqlDao();
        mysqlDao.save();
    }   
    //void update()...
}

Service中使用Dao中的方法操作数据库,这样写绝对线程安全,但难以扩展,比如要切换数据库类型为Progres,或者测试的时候传入一个模拟Dao(不会连接任何数据库),都需要修改Service类,违反了开闭原则

解决方案  依赖的对象的创建交给外部注入,这样就可以根据需要创建自己的DbDao。

public class TestService {
​
    private DbDao Dbdao;
​
    public TestService(DbDao Dbdao) {
        this.Dbdao = Dbdao;
    }
    public void save() {
        Dbdao.save();
    }
​
    //通过setter方法注入
    /*public void setDbdao(DbDao Dbdao) {
        this.Dbdao = Dbdao;
    }*/
}
public class MySqlDao implements DbDao {
    public void save()
    {
        System.out.println("MySqlDao save user");
    }
}
​
public class ProgresDao implements DbDao {
    public void save()
    {
        System.out.println("ProgresDao save user");
    }
}
​
public class Application {
    public static void main(String[] args) {
        TestService testService = new TestService(new MySqlDao());
        testService.save();
​
        TestService testService1 = new TestService(new ProgresDao());
        testService1.save();
    }
}

二、注入的方式

  1.  构造器注入(推荐)——如果构造函数有非常多参数,可使用Lombok简化
  2. Setter注入
  3. 字段注入

1. 构造器注入

构造函数里面赋初值。如果当前的类中只有一个构造函数,那么 @Autowired 也可以省略。构造方法和属性注入不同,构造方法注入可适用于任何环境,无论是 IoC 框架还是非 IoC 框架,构造方法注入的代码都是通用的,所以它的通用性更好。

优点:

  • 可注入不可变对象。final
  • 注入对象不会被修改。构造方法注入不会像 Setter 注入那样,构造方法在对象创建时只会执行一次,因此它不存在注入对象被随时(调用)修改的情况。
  • 注入对象会被完全初始化。因为依赖对象是在构造方法中执行的,而构造方法是在对象创建之初执行的,因此被注入的对象在使用之前,会被完全初始化,这也是构造方法注入的优点之一。
  • 通用性更好。构造方法和属性注入不同,构造方法注入可适用于任何环境,无论是 IoC 框架还是非 IoC 框架,构造方法注入的代码都是通用的,所以它的通用性更好。

image

@Component
public class UserService {
    private final UserDao userDao;
    private final EmailService emailService;
    
    // 当前类中如果只有一个构造方法,Spring 4.3+ 可省略 @Autowired
    public UserService(UserDao userDao, EmailService emailService) {
        this.userDao = userDao;
        this.emailService = emailService;
    }
}

 

 2、Setter注入

 普通成员函数里面为成员变量赋初值。优点是符合单一职责的设计原则,但是不能注入不可变对象(也就是final修饰的对象),注入的对象也可以被修改。

image

@Component
public class UserService {
    private UserDao userDao;
    private EmailService emailService;
    
    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    
    @Autowired
    public void setEmailService(EmailService emailService) {
        this.emailService = emailService;
    }
}

 

 3、字段注入Field Injection,也就是属性注入,但是不推荐。

特点是使用简单,只要在属性上面加了@Autowired,就可以不用new而直接可以获得注入的对象,但是官方不推荐,缺点在于

  • 无法注入一个不可变的对象(也就是final修饰的对象)。Java中,final一般直接赋值,或者在构造方法中赋值。当使用属性注入final对象时,不符合final的使用规范,无法注入成功,会报错。要注入一个不可变的对象,一般用构造器注入。
  • 不通用,只适用于IoC容器(框架),可移植性差,移到别的非IoC框架,就无效了。
  • 更容易违背单一设计原则
@Component
public class UserService {
    @Autowired
    private UserDao userDao;  // 或 @Resource, @Inject
    
    @Autowired
    private EmailService emailService;
}

 

备忘一下Java面试网站:www.javacn.site

 
posted on 2026-05-08 15:48  青柠锦鲤  阅读(6)  评论(0)    收藏  举报