在编写java程序时,我们是自己编写不同的构造函数,控制对象的创建;但是当项目复杂后,一个对象的创建需要依赖很多对象,spring通过IOC功能将对象的创建和管理由业务代码转移到IOC容器中,从而简化编程。
Bean是一个java对象,在Spring中由IOC容器创建与管理。只要是通过注解@Component、@Service、@Repository、@Controller标记的类在Spring中都是bean对象,也可以在xml配置文件中的标签中定义bean对象。
IOC即Inverse of Control,控制反转,是一种设计思想。
要理解反转,就需要知道什么是正转。正转是指我们在对象内部通过new方式创建对象依赖的其它对象,是对象主动去创建依赖对象;而反转是由IOC容器来控制对象的创建与注入,即依赖对象的获取方式反转了,由IOC容器来统一创建与管理。
传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IOC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。
IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。
DI-Dependency Injection,即依赖注入,指IOC容器在程序执行期间动态地将依赖关系注入到组件之中。 比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。
IOC是一种设计思想,而DI是Spring实现IOC的方式,它们是同一概念的不同描述。关于两者的区别,可以参考Martin Fowler大佬的文章Inversion of Control Containers and the Dependency Injection pattern
xml配置,即由xml配置文件存储bean的信息,由Spring加载xml文件来创建与管理bean。
最基本的配置是直接在xml里配置bean,如下所示。由于这种配置的bean在IOC容器中创建时使用的是无参构造函数,并没有注入对象所依赖的其它对象,所以有了通过setter方式和构造函数方式配置的bean对象依赖的对象(这两种方式是注入了依赖对象)。
方式1:单独配置bean,没有其它参数(通过无参构造函数注入)
<bean id="user" class="com.demo.User"/>
bean>
方式2:配置bean对象后,通过constructor-arg配置,由构造函数中的参数注入。
<bean id="user" class="com.demo.User"/>
<constructor-arg name="userDao" ref="userDao"/>
bean>
方式3:配置bean对象后,通过property配置,由对应的setter方法注入
<bean id="user" class="com.demo.User"/>
<property name="userDao" ref="userDao"/>
bean>
将类的创建交给配置的JavcConfig类来完成,Spring只负责维护和管理,采用纯Java创建方式。其本质上就是把在XML上的配置声明转移到Java配置类中
示例:
(1)在Spring中创建一个配置类(添加@Configuration注解,将该类声明为配置类,命名一般以Config结尾)
(2)在该配置类的方法中,加上@Bean注解,该方法用于创建实例并返回,该实例创建后会交给spring管理,方法名建议与实例名相同(首字母小写)。
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport
{
@Bean
@SuppressWarnings(value = { "unchecked", "rawtypes" })
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
{
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
serializer.setObjectMapper(mapper);
template.setValueSerializer(serializer);
// 使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.afterPropertiesSet();
return template;
}
}
通过在类上加注解的方式,来声明一个类交给Spring管理,Spring会自动扫描带有@Component,@Controller,@Service,@Repository这四个注解的类,然后帮我们创建并管理,前提是需要先配置Spring的注解扫描器。
@Service("studentService")
public class StudentServiceImp implements StudentService {
@Autowired
private StudentDao studentDao;
@Override
public Student queryById(Integer id) {
return studentDao.queryById(id);
}
@Override
public String queryName(Integer id) {
return studentDao.queryName(id);
}
@Override
public boolean addStudent(Student student) {
return false;
}
@Override
public void deleteStudent(int id) {
studentDao.deleteById(id);
}
}
如果Spring项目的配置文件为xml格式,那么还需要在xml中添加以下信息,让Spring扫描指定路径下的bean相关注解,不然读取不到bean。
<context:component-scan base-package='com.demo'>
常用的注入方式主要有三种:构造方法注入(Construct注入),setter注入,基于注解的注入(接口注入)。前两种主要是xml配置文件中使用,后面一种主要是不依赖xml配置文件的Spring项目使用。
xml文件配置的这种方式,见Bean的配置中xml配置的方式2。在SpringBoot项目中,也没见过这种配置的,因此略过。
xml文件配置的这种方式,见Bean的配置中xml配置的方式3。在SpringBoot项目中,也没见过这种配置的,因此略过。
注解注入的方式中,可以使用的注解有@Autowired、@Resource、@Inject。@Autowired是Spring自带的注解,通过AutowiredAnnotationBeanPostProcessor 类实现的依赖注入;@Resource是JSR250规范的实现,在javax.annotation包下;@Inject是JSR330 (Dependency Injection for Java)中的规范,需要导入javax.inject.Inject jar包 ,才能实现注入。
@Autowired的例子前面已经给出,不再赘述。这里还需要补充上面三个注解的详解与区别。
待完成
https://www.martinfowler.com/articles/injection.html Martin Fowler大佬讲解IOC与DI
https://www.pdai.tech/md/spring/spring-x-framework-ioc.html 系统讲解IOC
https://www.cnblogs.com/xdp-gacl/p/4249939.html IOC的基本概念
https://www.cnblogs.com/xdp-gacl/p/3707631.html IOC的基本概念
https://www.cnblogs.com/zrtqsk/p/3735273.html Bean的生命周期
https://javadoop.com/post/spring-ioc IOC源码分析,非常详细