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); } }
本文来自博客园,作者:LeeJuly,转载请注明原文链接:https://www.cnblogs.com/peterleee/p/10127943.html