Spring每个程序员都使用Spring——IOC

Posted 你个佬六

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring每个程序员都使用Spring——IOC相关的知识,希望对你有一定的参考价值。

一、前言

这一段时间,想重新学习一下Spring,想通过博客+思维导图的形式来记录一下Spring。在上一篇博客中,小编向大家介绍了Spring是什么、spring是怎么发展的。用过spring的程序员都知道spring的核心是IOC和AOP。在这篇博客中,小编就向大家分享一下小编理解的IOC。

二、什么是IOC?

IOC ,Inversion or control , 控制反转,是指创建对象的控制权的转移。

在没有spring的时候,我们需要自己创建对象,自己要管理对象的生命周期,管理不好就会发现对象无法回收,发生内存泄漏等问题。

接入spring后,可以把对象的生命周期交给spring容器管理,而IOC容器就是最好选择。IOC容器会根据配置文件或者扫描注解,把对象加载到容器中。并且对象和对象之间的依赖关系也会被管理。也就是DI。

如何把bean注入到ioc容器中?

  • xml 声明bean,Spring 容器启动的时候会解析xml,把bean装载到ioc

这种方式现在基本不用,尤其是我们使用了springboot后,通过相关注解更简单。

<bean id=”……” class=”……”>  
    <property name=”属性1” value=”……”/>  
    <property name=”属性2” value=”……”/>  
    ……  
</bean> 
  • 使用@CompontScan扫描 @Controller,@Service,@Repository,@Component

这个注解可以说在SpringBoot之前的springmvc还是很常见的, 需要配置扫描包的路径。当SpringBoot出来后,启动类上做了一个组合注解@SpringBootApplication,这个注解就包含了@CompontScan。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AliasFor;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = @Filter(
    type = FilterType.CUSTOM,
    classes = TypeExcludeFilter.class
), @Filter(
    type = FilterType.CUSTOM,
    classes = AutoConfigurationExcludeFilter.class
)
)
public @interface SpringBootApplication 
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    Class<?>[] exclude() default ;

    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    String[] excludeName() default ;

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
    String[] scanBasePackages() default ;

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default ;

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "nameGenerator"
    )
    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;

从上面代码@SpringBootApplication并没有定义新的属性而是复用其他注解已有的注解属性并对其进行组合
形成新的注解从而到达到便捷的目的。这样的注解我们可以称之为复合注解。
所以在使用SpringBoot 时我们只需要@SpringBootApplication一个注解就能开启
自动配置,自动扫描的功能。
而不再需要使下面三个注解来达到同样的目的。
@Configuration
@ComponentSan
@EnnableAutoConfiguration

  • 使用@Configuration声明配置类,并用@Bean实现对象定义
    这是xml的一种进步,完全代替了xml。比如下面,通过@Bean声明一个RestHighLevelClient对象,并起名字为esClient,使用的时候可以通过@Autowire注入即可。
package com.xxxx.central.daas.adapter;

import com.ctrip.framework.apollo.ConfigService;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.HttpAsyncResponseConsumerFactory;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ElasticsearchConfig 

    public static final RequestOptions COMMON_OPTIONS;

    String key = "ELASTICSEARCH";

    private String username = ConfigService.getConfig(key).getProperty("es.username", "");
    private String passwrod = ConfigService.getConfig(key).getProperty("password", "");
    private String hostName = ConfigService.getConfig(key).getProperty("hostname", "");
    private Integer hostport = Integer.valueOf(ConfigService.getConfig(key).getProperty("hostport", "9200"));

    static 
        RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();

        // 默认缓存限制为100MB,此处修改为30MB。
        builder.setHttpAsyncResponseConsumerFactory(
                new HttpAsyncResponseConsumerFactory
                        .HeapBufferedResponseConsumerFactory(30 * 1024 * 1024));
        COMMON_OPTIONS = builder.build();
    

    @Bean("esClient")
    public RestHighLevelClient createRestHighLevelClient() 
        final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, passwrod));
        // 通过builder创建rest client,配置http client的HttpClientConfigCallback。
        // 单击所创建的Elasticsearch实例ID,在基本信息页面获取公网地址,即为ES集群地址。
        RestClientBuilder builder = RestClient.builder(new HttpHost(hostName, hostport))
                .setHttpClientConfigCallback(httpClientBuilder ->
                        httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider));
        //防止 java.net.SocketTimeoutException: 30,000 milliseconds timeout on connection http-outgoing-3
        RestClientBuilder.RequestConfigCallback configCallback = new RestClientBuilder.RequestConfigCallback() 
            @Override
            public org.apache.http.client.config.RequestConfig.Builder customizeRequestConfig(org.apache.http.client.config.RequestConfig.Builder requestConfigBuilder) 
                return requestConfigBuilder
                        .setConnectTimeout(5000 * 1000)
                        .setSocketTimeout(6000 * 1000);
            
        ;
        builder.setRequestConfigCallback(configCallback);
        // RestHighLevelClient实例通过REST low-level client builder进行构造。
        RestHighLevelClient highClient = new RestHighLevelClient(builder);
        return highClient;
    


    @Autowired
    private RestHighLevelClient esClient;
  • 使用@Import导入配置类或普通bean

  • 用factoryBean工厂,动态构建Bean实例
    Spring Cloud OpenFeign 的动态代理实例就这么建立的。

  • 实现ImportSelector接口,动态批量注入配置类或者对象
    springboot 自动装配机制有用到。

DI ,Dependency Injection ,依赖注入

DI和控制反转是同一个概念的不同角度的描述,即应用程序在运行时依赖IoC容器来动态注入对象需要的外部资源。

最直观的表达就是,IOC让对象的创建不用去new了,可以由spring自动生产,使用java的反 射机制,根据配置文件在运行时动态的去创建对象以及管理对象,并调用对象的方法的。

依赖注入的方式 :

  • 构造器注入

  • setter方法注入

  • 接口注解注入

IoC让相互协作的组件保持松散的耦合,而AOP编程允许你把遍布于应用各层的功能分离出来,形成可重用的功能组件。

三、IOC容器中的对象是线程安全的吗?

不安全

spring容器可以创建多例Bean 和单例Bean。
多例Bean 每次调用都会创建一个新的对象,所以是安全的。
单例Bean 所有线程公用一个对象,所以是不安全的

四、小结

spring是最伟大的发明,没有spring,会让开发难度上升一个等级。把对象管理的明明白白的。相关依赖也注入了。很方便。

以上是关于Spring每个程序员都使用Spring——IOC的主要内容,如果未能解决你的问题,请参考以下文章

spring ioc原理

java框架篇---spring IOC 实现原理

[spring学习1] IoC容器

Spring IOC分析

Spring框架-IOC和AOP

简述你对Spring框架IOC和AOP的理解。