Spring IOC简析

文章目录

        • 前言
        • 理解IOC概念
          • Bean
          • IOC
          • DI
        • Bean的配置
          • xml配置
          • Java配置
          • 注解配置
        • 依赖注入的方式
          • setter方式
          • 构造函数
          • 注解注入
        • IOC源码解析
        • 参考文章:

前言

在编写java程序时,我们是自己编写不同的构造函数,控制对象的创建;但是当项目复杂后,一个对象的创建需要依赖很多对象,spring通过IOC功能将对象的创建和管理由业务代码转移到IOC容器中,从而简化编程。

理解IOC概念
Bean

Bean是一个java对象,在Spring中由IOC容器创建与管理。只要是通过注解@Component、@Service、@Repository、@Controller标记的类在Spring中都是bean对象,也可以在xml配置文件中的标签中定义bean对象。

IOC

IOC即Inverse of Control,控制反转,是一种设计思想。

要理解反转,就需要知道什么是正转。正转是指我们在对象内部通过new方式创建对象依赖的其它对象,是对象主动去创建依赖对象;而反转是由IOC容器来控制对象的创建与注入,即依赖对象的获取方式反转了,由IOC容器来统一创建与管理。

传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IOC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。

其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。

IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。

DI

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

Bean的配置
xml配置

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>
Java配置

将类的创建交给配置的JavcConfig类来完成,Spring只负责维护和管理,采用纯Java创建方式。其本质上就是把在XML上的配置声明转移到Java配置类中

  • 优点:适用于任何场景,配置方便,因为是纯Java代码,扩展性高,十分灵活;适用于无法直接用注解标记的对象
  • 缺点:由于是采用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的注解扫描器。

  • 优点:开发便捷,通俗易懂,方便维护。
  • 缺点:具有局限性,对于一些第三方资源,无法添加注解。只能采用XML或JavaConfig的方式配置
@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项目使用。

setter方式

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的例子前面已经给出,不再赘述。这里还需要补充上面三个注解的详解与区别。

IOC源码解析

待完成

参考文章:

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源码分析,非常详细

你可能感兴趣的:(Spring,spring,java,后端)