IoC,Spring的核心理念之一,确实这是一个老生常谈的东西。但是今天呢!又重新温习之后,想再说说自己对IOC的一些想法。

IoC——Inversion of Control,控制反转。要想理解IoC还是要从其本身出发,首先就控制而言,控制是对谁的控制——是对象的控制。其次,反转是什么的反转或者说为什么要称做反转——是对象控制权反转。

对象控制,传统的方式就是程序员通过new关键字的方式来生成一个对象,然后由程序员根据程序逻辑人为地控制对象的使用。从这里出发,就可以很好地理解什么是控制反转了。

所谓控制反转,就是将原本在程序员手中的对象创建和管理的权限交给了Spring IoC容器。也就是说,控制反转就是要转移程序员对对象的控制权,而在Spring当中的实现就是Spring IoC容器通过Xml或注解的描述生成或者获取对象,再由IoC容器对这些Bean进行管理。

所以,理解IoC(控制反转),就只需要记住,控制权由谁反转给了谁。

IoC容器

顶级IoC容器接口—BeanFactory

对于BeanFactory,它的重要性源自于所有IoC容器都是直接或者间接派生自它。虽然,它的功能不是很强大,但是从其源码当中却可以看出很多端倪。

public interface BeanFactory {

/**

工厂Bean的前缀,

用于判断获取的是FactoryBean还是FactoryBean所产生的实例 下面会有详细解释

**/

String FACTORY_BEAN_PREFIX = "&";

/**通过name 获取Bean**/
Object getBean(String name) throws BeansException;
/**通过name和Class类型 获取Bean**/
<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
/**通过name和构造参数,也就是可以指定调用某个构造方法 获取Bean**/
Object getBean(String name, Object... args) throws BeansException;
/**通过Class类型 获取Bean**/
<T> T getBean(Class<T> requiredType) throws BeansException;
/**通过Class类型和构造参数,同样可以指定调用某个构造方法 获取Bean**/
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException; /**返回一个被ObjectProvider包装的Bean**/
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType); /**通过name判断是否在容器中有这个Bean**/
boolean containsBean(String name); /**是否为单例**/
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
/**是否为原型**/
boolean isPrototype(String name) throws NoSuchBeanDefinitionException; /**类型匹配否**/
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException; boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException; /**根据name找到Bean的Class类型**/
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException; /**获取此Bean之外的别名**/
String[] getAliases(String name);

}

FACTORY_BEAN_PREFIX 在Spring当中,有一个叫做FactoryBean的接口,这个类有一个T getObject() throws Exception;这样的方法,这个方法会返回一个对象实例。对于这个接口的实现类而言,通过BeanFactory的getBean()返回的Bean是实现类本身的实例,还是getObject()的返回实例就在于有没有前缀。有,返回FactoryBean;没有,返回getObject()的返回实例。

/**举个简单的例子

实现这样一个FactoryBean

用这样一个FactoryBean来创建一个我们需要的User

**/

@Component("user")

public class UserFactoryBean implements FactoryBean {

@Autowired
private User user; @Override
public User getObject() throws Exception {
return user;
} @Override
public Class<?> getObjectType() {
return user.getClass();
}

}

//测试方法

public static void test1(){

ApplicationContext ctx = new AnnotationConfigApplicationContext(UserConfig.class);

//没有前缀

//得到的是User getObject() throws Exception的返回值

User user = (User) ctx.getBean("user");

System.out.println(user);

//有前缀
//得到的是UserFactoryBean的实例
UserFactoryBean userFactoryBean =
(UserFactoryBean) ctx.getBean("&user");
System.out.println(userFactoryBean);

}

这里只是简单的例子,用来说明FACTORY_Bean_PREFIX的作用,FactoryBean更具体的用法,可以参考工厂模式当中工厂的作用。

ObjectProvider 这是在spring4.3之后才出现的一个接口,它主要作用是解决注入时Bean不存在或者Bean存在多个时出现的异常情况。

//getIfAvailable()可以解决容器中没有userDao时的异常

public class UserService{

private UserDao userDao;

public UserService(ObjectProvider dao){

userDao = dao.getIfAvailable();

}

}

//5.1之后可以通过流式处理来解决容器中存在多个userDao情况

public class UserService{

private UserDao userDao;

public UserService(ObjectProvider dao){

userDao = dao.orderedStream()

.findFirst()

.orElse(null)

}

}

核心容器—ApplicationContext

学习过Spring的人,对ApplicationContext都不会陌生。它是BeanFactory的子(准确的说应该是孙子)接口之一,而我们所使用到的大部分Spring IoC容器都是ApplicationContext的实现类。

​IoC Container.png

Spring的源码很庞大,也很复杂,所以建议学习的时候,从某几个重点类开始,分析其继承、扩展关系,以此横向展开对Spring的认识。

这里也就不再对ApplicationContext的各个继承接口一一解释了,API文档里面都有: ApplicationContext。对于ApplicationContext这个容器更多的是侧重于对它的应用介绍,就是如何通过这个容器来获取Bean。

通过一个简单的例子来了解一下:

//普通的JavaBean

public class User {

private Long id;

private String name;

private int age;

/getter,setter,toString/

}

//配置类,采用注解的形式来配置Bean

@Configuration

public class UserConfig {

@Bean(name="user")

public User getBeanUser(){

User user = new User();

user.setId(1L);

user.setName("klasdq1");

user.setAge(18);

return user;

}

}

//测试类

public class IocTest {

public static void main(String[] args) {

ApplicationContext ctx = new AnnotationConfigApplicationContext(UserConfig.class);

User user = ctx.getBean("user");//

System.out.println(user);

}

}

@Configuration @Configuration这个注解的作用就在于它标示的类拥有一个或多个@Bean修饰的方法,这些方法会被Spring容器处理,然后用于生成Bean或者服务请求。

//@Configuration的源码

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Component

public @interface Configuration {

@AliasFor(

annotation = Component.class

)

String value() default "";

boolean proxyBeanMethods() default true;

}

从注解的源码当中可以看出,它有两个;一是value,用于为配置类声明一个具体的Bean name。二是proxyBeanMethods,用于指定@Bean修饰的方法能否被代理。

@Bean 这个注解只用在方法上面,用于Spring容器管理生成Bean。

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface Bean {

@AliasFor("name")

String[] value() default {};

@AliasFor("value")
String[] name() default {}; /** @deprecated */
@Deprecated
Autowire autowire() default Autowire.NO; boolean autowireCandidate() default true; String initMethod() default ""; String destroyMethod() default "(inferred)";

}

@Bean的参数中重要的就是name(value),其含义在于为Bean声明具体的名称,一个Bean可以有多个名称,这也是为什么BeanFactory中有一个getAliases()方法。其他参数,看名字就知道什么意图,就不再多解释了。

AnnotationConfigApplicationContext 这是ApplicationContext类的具体实现类之一,用于注解形式的Bean的生成。与之相对应的还有ClassPathXmlApplicationContext从XML文件中获取Bean。

Bean的装配

在Spring当中对于Bean的装配允许我们通过XML或者配置文件装配Bean,但在Spring Boot中常用注解的形式,为了方便Spring Boot开发的需要,就不再使用XML的形式了。

直接看例子:

//配置JavaBean

@Component("klasdq2")

public class User {

@Value("2")

private Long id;

@Value("klasdq2")

private String name;

@Value("19")

private int age;

/getter,setter,toString/

}

//配置类扫描装配Bean

@Configuration

@ComponentScan

public class UserConfig {

}

//测试类

public class IocTest {

public static void main(String[] args) {

ApplicationContext ctx = new AnnotationConfigApplicationContext(UserConfig.class);

User user = (User) ctx.getBean("klasdq2");

System.out.println(user);

}

@Component

@Component的源码很简单:

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Indexed

public @interface Component {

String value() default "";

}

参数当中只有一个value,用于声明Bean的名字(标识)。这里又出现一个新的注解@Indexed,顾名思义这个注解就是增加一个索引,这是因为Spring Boot当中大量采用扫描的形式来装配Bean之后,扫描的Bean越多,解析时间就越长,为了提高性能,在5.0版本的时候就引入了这样一个注解。

@Value

@Target({ElementType.FIELD,

ElementType.METHOD,

ElementType.PARAMETER,

ElementType.ANNOTATION_TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface Value {

String value();

}

这个注解可以用在字段、方法、方法参数、注解上,通过一个表达式或者具体字符串为其传入相应的值。@Value是一个功能非常强大的注解,建议对其多做了解。

其功能主要包括以下几种:

注入普通字符串

书写SpEL表达式,如:@Value("#{person.name}"),可以从配置文件、Bean属性、调用方法等等得到数据。

注入Resource,如:@Value("classpath:com/demo/config.txt") 使用Resource类型接收

注入URL资源,如:@Value("http://www.baidu.com") 使用Resource类型接收

@ComponentScan

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.TYPE})

@Documented

@Repeatable(ComponentScans.class)

public @interface ComponentScan {

/**

* 这个参数是ComponetScan注解最常用的,其作用就是声明扫描哪些包,

* 通过扫描,将含有@Componet注解的Bean装入Spring容器中。

* value和basePackages效果一样,其默认值为配置类所在包及其子包。

**/

@AliasFor("basePackages")

String[] value() default {};

@AliasFor("value")
String[] basePackages() default {}; /**扫描哪些类**/
Class<?>[] basePackageClasses() default {}; /**Bean Name生成器:自定义bean的命名生成规则**/
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class; /**作用域解析器**/
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class; /**作用域代理**/
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT; /**资源的匹配模式,默认就是.Class**/
String resourcePattern() default "**/*.class"; /**是否启用默认过滤器(源码下面自定义的过滤器)**/
boolean useDefaultFilters() default true; /**符合过滤器条件的组件 才会扫描**/
ComponentScan.Filter[] includeFilters() default {};
/**符合过滤器条件的组件 不会扫描**/
ComponentScan.Filter[] excludeFilters() default {}; /**是否启用懒加载**/
boolean lazyInit() default false; /**过滤器**/
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Filter {
/**可以按照注解类型或者正则式过滤**/
FilterType type() default FilterType.ANNOTATION; /**过滤哪些类**/
@AliasFor("classes")
Class<?>[] value() default {}; @AliasFor("value")
Class<?>[] classes() default {}; /**匹配方式**/
String[] pattern() default {};
}

}

例如:

@ComponetScan(basePackages="com.klasdq.sb.service.*"

,excludeFilters=(@Filter(classes="UtilService.Class")))

这样的一个例子中,basePcakages指定了扫描service包下所有具体@Component注解的Service Bean(@Service包含了@Component)。而excludeFilters定义使用@Filter过滤掉UtilService.Class。其他的参数使用,可以参数API文档中的介绍,大同小异。

@ComponetScans 这个注解也可以用于扫描组件,可以定义@ComponetScan,如:

@ComponentScans(value = { @ComponentScan(value = "com.klasdq.sb.service."),

@ComponentScan(value = "com.klasdq.sb.dao.
", excludeFilters=(@Filter(classes="UtilDao.Class")) })

通过这样一种方式来定义多个扫描组件,使得扫描更加精确。因为@ComponentScan(value="com.klasdq.sb.*")全包扫描的方式虽然写起来简单,但是耗费的时间代价却是极大的。

老生再谈 IoC的更多相关文章

  1. [转载]再谈百度:KPI、无人机,以及一个必须给父母看的案例

    [转载]再谈百度:KPI.无人机,以及一个必须给父母看的案例 发表于 2016-03-15   |   0 Comments   |   阅读次数 33 原文: 再谈百度:KPI.无人机,以及一个必须 ...

  2. Support Vector Machine (3) : 再谈泛化误差(Generalization Error)

    目录 Support Vector Machine (1) : 简单SVM原理 Support Vector Machine (2) : Sequential Minimal Optimization ...

  3. Unity教程之再谈Unity中的优化技术

    这是从 Unity教程之再谈Unity中的优化技术 这篇文章里提取出来的一部分,这篇文章让我学到了挺多可能我应该知道却还没知道的知识,写的挺好的 优化几何体   这一步主要是为了针对性能瓶颈中的”顶点 ...

  4. 浅谈HTTP中Get与Post的区别/HTTP协议与HTML表单(再谈GET与POST的区别)

    HTTP协议与HTML表单(再谈GET与POST的区别) GET方式在request-line中传送数据:POST方式在request-line及request-body中均可以传送数据. http: ...

  5. Another Look at Events(再谈Events)

    转载:http://www.qtcn.org/bbs/simple/?t31383.html Another Look at Events(再谈Events) 最近在学习Qt事件处理的时候发现一篇很不 ...

  6. C++ Primer 学习笔记_32_STL实践与分析(6) --再谈string类型(下)

    STL实践与分析 --再谈string类型(下) 四.string类型的查找操作 string类型提供了6种查找函数,每种函数以不同形式的find命名.这些操作所有返回string::size_typ ...

  7. 再谈JSON -json定义及数据类型

    再谈json 近期在项目中使用到了highcharts ,highstock做了一些统计分析.使用jQuery ajax那就不得不使用json, 可是在使用过程中也出现了非常多的疑惑,比方说,什么情况 ...

  8. C++ Primer 学习笔记_44_STL实践与分析(18)--再谈迭代器【下】

    STL实践与分析 --再谈迭代器[下] 三.反向迭代器[续:习题] //P355 习题11.19 int main() { vector<int> iVec; for (vector< ...

  9. C++ Primer 学习笔记_43_STL实践与分析(17)--再谈迭代器【中】

    STL实践与分析 --再谈迭代器[中] 二.iostream迭代[续] 3.ostream_iterator对象和ostream_iterator对象的使用 能够使用ostream_iterator对 ...

  10. 再谈IE的浏览器模式和文档模式

    原文:再谈IE的浏览器模式和文档模式 以前在 “IE8兼容视图(IE7 mode)与独立IE7的区别”一文中曾经涉及过浏览器模式和文档模式,但二者的区别却不甚了了,现在有了新的认识,再补充一下. 1. ...

随机推荐

  1. Linux基础命令3:通过名称指定文件、创建文件目录

    描述Linux文件系统层次结构概念 /root(root的home目录)/home/username(其它普通用户的home目录)/bin, /usr/bin, /usr/local/bin命令存放处 ...

  2. 《SpringBoot》史上最全SpringBoot相关注解介绍

    @SpringBootApplication @SpringBootApplication看作是 @Configuration.@EnableAutoConfiguration.@ComponentS ...

  3. flutter真机调试出现flutter Launching 'app' on No Devices.

    1. flutter真机调试出现flutter Launching 'app' on No Devices. flutter Launching 'app' on No Devices. 我的是华为手 ...

  4. 深入理解 Future, CompletableFuture, ListenableFuture,回调机制

    深入理解 Future, CompletableFuture, ListenableFuture,回调机制 本文禁止转载. 本文从设计思想.具体实现等角度分析了 Future.CompletableF ...

  5. 浅谈云主机在VPC中进行迁移的使用场景和操作方法

    本文分享自天翼云开发者社区<浅谈云主机在VPC中进行迁移的使用场景和操作方法>,作者:刘****雪 一.客户经常遇到的网络迁移问题 客户在天翼云购买一台云主机并且部署完成想要的应用后,发现 ...

  6. Sa-Token v1.40.0 发布 🚀,来看看有没有令你心动的功能!

    Sa-Token 是一个轻量级 Java 权限认证框架,主要解决:登录认证.权限认证.单点登录.OAuth2.0.微服务网关鉴权 等一系列权限相关问题. 目前最新版本 v1.40.0 已发布至 Mav ...

  7. Q:查看锁表进程,及杀死所有锁表进程sql

     查看锁表进程 SELECT DISTINCT decode(s.inst_id, 1, 'DB1', 2, 'DB2') 数据库服务器, decode(s.BLOCKING_SESSION, '', ...

  8. 7.1 闲话-Erdős–Gallai 定理和哈基米算法

    Erdős–Gallai 定理 前几天考试有一个建出最大流模型,转为最小割,然后模拟最小割的套路. 这一个套路并不是少见的.在 Gale-Ryser 定理和 Erdős–Gallai 定理的证明都体现 ...

  9. Flink CDC全量和增量同步数据如何保证数据的一致性

    Apache Flink 的 Change Data Capture (CDC) 功能主要用于实时捕获数据库中的变更记录,并将其转换为事件流以供下游处理.为了保证全量和增量数据同步时数据的一致性.不丢 ...

  10. IDEA Spring Boot项目,排查解决maven包冲突

    一.Idea安装插件 下载方式1:插件名称:maven helper 打开Idea设置,搜索安装该插件 下载方式2:https://plugins.jetbrains.com/plugin/7179- ...