springIOC容器的理解(控制反转和依赖注入,自动装配,循环依赖,实际注入等问题)

控制反转(Inversion of Control,IOC

概念:

IOC(DI):java程序中的每个业务逻辑至少需要两个或以上的对象来协作完成,通常,每个对象在使用他的合作对象时,自己均要使用像new object() 这样的语法来完成合作对象的申请工作。你会发现:对象间的耦合度高了。而IOC的思想是:Spring容器来实现这些相互依赖对象的创建、协调工作,包括销毁工作。自身对象只需要关系业务逻辑本身就可以了。从这方面来说,对象如何得到他的协作对象的责任被反转了(IOC、DI)。

 

IoC和DI是什么关系呢?

一种说法是是同一个概念的不同角度描述,另一种说法是,依赖注入可以看做是控制反转的一种实现方式,IoC是一种思想,DI是一种设计模式,实现IoC的模式

 

在java中的实现简单来说就是把对象创建、管理的控制权都交给Spring容器,这是一种控制权的反转。

自己控制:

@RestController
@RequestMapping("/student/api")
public class UserController {
    UserService userService=new UserServiceImp();
    @PostMapping(value="/all")
     public List<User> getAll(@RequestBody  Map<String, Object> params){
          List<User> userAll=userService.getUsers();
          return userAll;
      }
}

交给IOC容器控制,你只负责请求,它返回给你就好

IOC容器:(主要两个功能)

1.它负责了对象整个的生命周期的管理——创建、装配、销毁。

2.负责对象之间的依赖关系。

好处:

1.IoC容器自动完成对象的初始化,避免在开发过程中写一大段初始化代码。

2.创建实例的时候不需要了解细节。

@RestController
@RequestMapping("/student/api")
public class UserController {
    @Autowired
    UserService userService;
    @PostMapping(value="/all")
     public List<User> getAll(@RequestBody  Map<String, Object> params){
          List<User> userAll=userService.getUsers();
          return userAll;
      }
}

@Autowired就是从IOC容器中请求这个bean。

IOC容器具体工作流程:

IOC容器在创建一个实例的时候是从最上层往下层找依赖关系,到达最底层以后,再往上一步一步new,所谓依赖注入就是将底层类作为参数传入上层类。实现上层类对下层类的控制。

依赖注入(Dependency Injection

我们所需求的对象,需要依赖容器来获得,这个过程即是依赖注入。本质上IOC和DI是同一思想下不同维度的表现

IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。

自动装配的概念:

自动装配就是为了将依赖注入自动化的一个简化配置的操作,在spring中就是要装配哪个bean的引用,通常用注解来注入,在注解里面填写参数。

当一个对象的属性是另一个对象时,实例化时,需要为这个对象属性进行实例化。这就是装配。
如果一个对象只通过接口来表明依赖关系,那么这种依赖就能够在对象本身毫不知情的情况下,用不同的具体实现进行切换。但是这样会存在一个问题,在传统的依赖注入配置中,我们必须要明确要给属性装配哪一个bean的引用,一旦bean很多,就不好维护了。基于这样的场景,spring使用注解来进行自动装配,解决这个问题。自动装配就是开发人员不必知道具体要装配哪个bean的引用,这个识别的工作会由spring来完成。

比如当有多个同一类型的Bean时,可以用@Qualifier(“name”)来指定所要依赖的对象。

@Autowired默认按类的类型进行装配。

在依赖注入出现循环依赖的问题:

BeanA里面注入BeanB,BeanB里面又要注入BeanA(会报一个BeanCurrentlyInCreationException错误)而且会画出依赖圈

最根本是重新设计,尽量不要循环依赖。

 

1.用setter方法注入(推荐)

      因为setter是按需注入的,默认是允许依赖对象为null,在new的时候是不会报错的。

2.在构造方法中加@lazy延迟初始化,就会先构建外层实例,再构建依赖对象。

3.实现这个两个接口:

      ApplicationContextAware获取SpringContext,用于加载bean;InitializingBean定义了设置Bean的property之后的动作。

@Component
public class CircularDependencyA implements InitializingBean, ApplicationContextAware {
    private CircularDependencyB circB;
    private ApplicationContext context;
    
//主要是这一段,在外面的构造方法执行以后再去调用里面的 @Override
public void afterPropertiesSet() throws Exception { this.circB = context.getBean(CircularDependencyB.class); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; } public CircularDependencyB getCircularDependencyB() { return circB; }

https://blog.csdn.net/programmer_at/article/details/82389221

Spring IOC底层源码是怎样解决循环依赖的问题的?

https://segmentfault.com/a/1190000015221968(讲的很好,经常去看看)

依赖注入的几种方式有构造注入,setter注入(重新注入非默认值的依赖对象,对于非必需的依赖,建议用这个),接口注入,@value字段注入

构造注入:

public class UserMessage {
   UserDao userDao;
   public UserMessage(){
      userDao=new UserDao();  
   }      
   public void addUser(String username){
    userDao.addUser(username);  
   }  
}

由于在UserMessage内部创建了UseDao对象,这就造成了两个类之间的耦合度较高。

setter注入:

public interface UserDao{
  void addUser(String username);      
}
public class UserDaoImpl implements UserDao{ @Override public void addUser(String username) { System.out.println("添加用户:"+username); } } public class UserMessage{ private UserDao userDao; //使用设值方式赋值 public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void addUser(String userName, String password) { userDao.addUser(userName); } } public class test{ public static void main(String[] args) { UserDao userDao =new UserDaoImpl(); UserMessage userMessage=new UserMessage(); userMessage.setUserDao(userDao); } }

降低了User Message和UserDao的耦合度,需要换另一种UserDao的实现类的话,我们只需要修改test类下的第一行的代码就可以了,UserMessage内部并不需要修改。

注解注入

@Configuration
public class UserConfig {
    @Bean
    public UserDao getUserDao(){
        return new UserDao();
    }
    @Bean
    public UserMessage getUserMessage(){
        return new UserMesssgae(getUserDao);
    }
}

  

posted @ 2018-12-16 20:41  LeeJuly  阅读(323)  评论(0编辑  收藏  举报