关于java:Spring-IOC-特性有哪些不会读不懂源码

39次阅读

共计 9969 个字符,预计需要花费 25 分钟才能阅读完成。

作者:小傅哥
博客:https://bugstack.cn

积淀、分享、成长,让本人和别人都能有所播种!😄

一、前言

多线程、锁、JVM 调优,都背出花啦,怎么一写代码还是乱哄哄?

为什么这些无论从书本、课堂、面试都显得十分重要的常识,然而在理论的编程中没有晋升你的编码能力呢?

首先这些这些常识在理论的互联网业务开发中,简直是不罕用的,简直有锁和多线程的场景,为了性能的晋升也根本都是采纳分布式设计和实现了。而这些看上去很有技术含量的常识少数都被包装在非业务逻辑性能的组件中,而程序员在做业务开发时候简直是关怀不到。所以会了这些也简直不太可能就把你的编码能晋升起来,少数晋升的是你在查简单 bug 时候有一臂之力。

就像 会汉字就能写出诗词歌赋吗?懂 RGB 就能绘出山河大川吗?能蹦跳就能够舞出摇曳生姿吗?那都是不可能的,不要想着屁股围噶布就说会武术!

如果真的想把代码写好,就要一点点从积攒数据结构和算法逻辑 ( 不只是机械式的刷几道题就算了。你不了解为什么,刷再多也只是徒劳),接下来要做的是对设计模式和架构设计的了解,最终是一直的使用和总结。在这个过程你会接触到业务、产品、经营,编码只是最初的具体实现,并不是全流程中最重要的一部分,与编码相比更重要的是逻辑设计。

二、面试题

谢飞机,小记!,这次放假一遍撸串一遍被 Spring,嘿嘿,测验成绩面试去!

面试官:飞机,明天筹备咋样,上次问你的都学会了吗?

谢飞机:@Resource 是 JDK javax.annotation.Resource 提供的注解,哈哈哈哈哈,另外也学习了 Bean 的注入。

面试官:挺好记住了一些,那你在做 Bean 注入学习的时候,有留神到 Spring IOC 的个性吗,你都用到了什么?

谢飞机:嗯,用到 Bean 的配置、BeanDefinitionRegistryPostProcessor 对 Bean 的定义、还有 FactoryBean

面试官:好,那明天再和你聊聊,alias、autowire、depends-on、factory-method、lookup-method 等,实际验证下看看它们是怎么利用的。

三、SpringIOC 个性

IOC(Inversion of Control),管制反转的核心思想在于,资源的应用不禁应用各自治理,而是交给不应用资源的第三方进行治理。这样的益处是资源是集中管理的,可配置、易保护,同时也升高了单方的依赖度做到了低耦合。

早在 1988 年,Ralph E. Johnson & Brian Foote 在论文《Designing Reusable Classes》

One important characteristic of a framework is that the methods defined by the user to tailor the framework will often be called from within the framework itself, rather than from the user’s application code.
The framework often plays the role of the main program in coordinating and sequencing application activity.
This inversion of control gives frameworks the power to serve as extensible skeletons. The methods supplied by the user tailor the generic algorithms defined in the framework for a particular application.

接下来就给大家介绍一下 IOC 的一些外围个性,因为这些内容不仅是面试考点,也是在开发中间件或者小组件时须要用到的性能类,概括如下:

1. xml 配置

1.1 alias

测试类

public class UserService {

    private UserDao userDao;

    public UserService() {System.out.println("我被初始化了,UserService");
    }

    // ...get/set

}

xml 配置

<bean id="userService" class="org.itstack.interview.UserService"/>
<!-- 起个别名 -->
<alias name="userService" alias="userService-alias01"/>
<!-- 别名的别名 -->
<alias name="userService-alias01" alias="userService-alias02"/>

单元测试

@Test
public void test_alias() {BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config-alias.xml");
    logger.info("获取 Bean:{}", beanFactory.getBean("userService"));
    logger.info("获取 Bean 通过别名:{}", beanFactory.getBean("userService-alias01"));
    logger.info("获取 Bean 通过别名的别名:{}", beanFactory.getBean("userService-alias02"));
}

测试后果

23:01:29.872 [main] INFO  org.itstack.interview.test.ApiTest - 获取 Bean:org.itstack.interview.UserService@2a40cd94
23:01:29.872 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'userService'
23:01:29.872 [main] INFO  org.itstack.interview.test.ApiTest - 获取 Bean 通过别名:org.itstack.interview.UserService@2a40cd94
23:01:29.872 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'userService'
23:01:29.872 [main] INFO  org.itstack.interview.test.ApiTest - 获取 Bean 通过别名的别名:org.itstack.interview.UserService@2a40cd94

  • 目标:用于给 Bean 起别名
  • 应用:在 xml 配置里咱们能够给一个 Bean 起个别名,还能够给别名起一个新的别名。

1.2 autowire

测试类

public class UserDao {public UserDao() {System.out.println("我被初始化了,UserDao");
    }
}

xml 配置

<bean id="userDao" class="org.itstack.interview.UserDao"/>

<!-- 手动配置依赖 -->
<bean id="userService-by-property" class="org.itstack.interview.UserService">
    <property name="userDao" ref="userDao"/>
</bean>

<!-- 主动配置依赖 -->
<bean id="userService-by-autowire" class="org.itstack.interview.UserService" autowire="byName"/>

单元测试

@Test
public void test_autowire() {BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config-autowire.xml");
    logger.info("获取 Bean by 手动配置依赖:{}", beanFactory.getBean("userService-by-property"));
    logger.info("获取 Bean by 主动配置依赖:{}", beanFactory.getBean("userService-by-autowire"));
}

测试后果

23:05:55.501 [main] INFO  org.itstack.interview.test.ApiTest - 获取 Bean by 手动配置依赖:org.itstack.interview.UserService@679b62af
23:05:55.501 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'userService-by-autowire'
23:05:55.501 [main] INFO  org.itstack.interview.test.ApiTest - 获取 Bean by 主动配置依赖:org.itstack.interview.UserService@5cdd8682

  • 目标:autowire 用于把类中的属性注入交给 Spring 治理
  • 应用:在 xml 配置中,有两种形式别离是:手动配置依赖、主动配置依赖,手动的大家根本很罕用,主动的配置个别可能更多的对于注解的应用。其实这里的 autowire 和注解有一样的作用,autowire 几个可选项,byName、byType、constructor 等。

1.3 factory-method

测试类

public class StaticFactoryBean {static public UserDao getUserDaoByStatic(){return new UserDao();
    }

}

xml 配置

<bean id="staticFactory-method" class="org.itstack.interview.StaticFactoryBean" factory-method="getUserDaoByStatic"/>

单元测试

@Test
public void test_factory_method() {BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config-factory-method.xml");
    logger.info("获取 Bean:{}", beanFactory.getBean("staticFactory-method"));
}

测试后果

23:15:28.950 [main] INFO  org.itstack.interview.test.ApiTest - 获取 Bean:org.itstack.interview.UserDao@588df31b
23:15:28.950 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'staticFactory-bean'

  • 目标:标识动态工厂的工厂办法(工厂办法是动态的)
  • 应用:外围在于 xml 配置中增加 factory-method="getUserDaoByStatic",这样就能够在初始化时候调用对应静态方法的实例化内容。

1.4 factory-bean

测试类

public class StaticFactoryBean {public UserDao getUserDao(){return new UserDao();
    }
}

xml 配置

<bean id="staticFactory" class="org.itstack.interview.StaticFactoryBean"/>
<bean id="staticFactory-bean" factory-bean="staticFactory" factory-method="getUserDao"/>

单元测试

@Test
public void test_factory_bean_method() {BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config-factory-method.xml");
    logger.info("获取 Bean:{}", beanFactory.getBean("staticFactory-bean"));
}

测试后果

23:15:28.950 [main] INFO  org.itstack.interview.test.ApiTest - 获取 Bean:org.itstack.interview.UserDao@33b37288

  • 目标:factory-bean,实例化工厂类
  • 应用:factory-bean、factory-method 须要配合应用,factory-method="getUserDao" 调用的是对应的费静态方法返回实例化后果。

1.5 depends-on

xml 配置

<bean id="userService" class="org.itstack.interview.UserService" depends-on="userDao"/>
<bean id="userDao" class="org.itstack.interview.UserDao"/>

单元测试

@Test
public void test_depends_on() {BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config-depends-on.xml");
    logger.info("获取 Bean:{}", beanFactory.getBean(UserService.class, "userService").getUserDao());
}

测试后果

我被初始化了,UserDao
我被初始化了,UserService
23:24:14.678 [main] INFO  org.itstack.interview.test.ApiTest - 获取 Bean:org.itstack.interview.UserDao@45afc369

  • 目标:解决依赖初始化程序问题
  • 应用:如果不应用 depends-on="userDao",那么依照 Spring 的配置最先初始化的是 UserService,当你有须要解决初始化依赖时则须要应用到这个配置。

1.6 lookup-method & ApplicationContextAware

测试类

public class UserDaoProvider implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public UserDao getUserDao() {return applicationContext.getBean("userDao", UserDao.class);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}

}

xml 配置

<bean id="userDao" class="org.itstack.interview.UserDao" scope="prototype"/>
<bean id="provider" class="org.itstack.interview.UserDaoProvider"/>

单元测试

@Test
public void test_lookup_method() {BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config-lookup-method.xml");
    logger.info("获取 Bean:{}", beanFactory.getBean(UserDaoProvider.class, "provider").getUserDao());
    logger.info("获取 Bean:{}", beanFactory.getBean(UserDaoProvider.class, "provider").getUserDao());
}

测试后果

我被初始化了,UserDao
16:29:25.813 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Finished creating instance of bean 'userDao'
16:29:25.813 [main] INFO  org.itstack.interview.test.ApiTest - 获取 Bean:org.itstack.interview.UserDao@1188e820
16:29:25.813 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'userDao'
我被初始化了,UserDao
16:29:25.814 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Finished creating instance of bean 'userDao'
16:29:25.814 [main] INFO  org.itstack.interview.test.ApiTest - 获取 Bean:org.itstack.interview.UserDao@2f490758

  • 目标:获取单例下的原型模式,每次获取都要有新的对象产生。
  • 应用:其实外围在于 ApplicationContextAware 的应用和 scope="prototype" 配置,Spring 外部实现为应用 Cglib 办法,从新生成子类,重写配置的办法和返回对象,达到动静扭转的成果。

2. 接口类

2.1 FactoryBean

测试类

public class MyFactoryBean implements FactoryBean<UserDao> {

    @Override
    public UserDao getObject() throws Exception {return new UserDao();
    }

    @Override
    public Class<?> getObjectType() {return UserDao.class;}

    @Override
    public boolean isSingleton() {return true;}
    
}

xml 配置

<bean id="userDao" class="org.itstack.interview.MyFactoryBean"/>

单元测试

@Test
public void test_factory_bean() {BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config-factory-bean.xml");
    logger.info("获取 Bean:{}", beanFactory.getBean("userDao"));
}

测试后果

23:36:19.339 [main] INFO  org.itstack.interview.test.ApiTest - 获取 Bean:org.itstack.interview.UserDao@3bd94634

  • 目标:用于生成 Bean 的 Bean,叫 FactoryBean
  • 应用:其实这个应用在上一章节对于 Bean 如何注入到 Spring 曾经提到过,在一些 ORM 框架、RPC-Starter 等都有所利用。

2.2 BeanPostProcessor

测试类

public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("初始化前:" + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("初始化后:" + beanName);
        return bean;
    }
    
}

xml 配置

<bean id="beanPostProcessor" class="org.itstack.interview.MyBeanPostProcessor"/>
<bean id="userDao" class="org.itstack.interview.UserDao"/>

单元测试

@Test
public void test_bean_post_processor() {BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config-bean-post-processor.xml");
}

测试后果

初始化前:userDao
初始化后:userDao
16:38:32.686 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Finished creating instance of bean 'userDao'

  • 目标:拿到 Bean 对象初始化前后的动作,做相应的解决
  • 应用:BeanPostProcessor 是 Spring 框架的扩大接口类,通过对这个接口的实现,就能够在 Bean 实例化的过程中做相干的动作,比方拦挡当前公布到注册核心等。AOP 的操作也是通过 BeanPostProcessor 和 IOC 容器建设起分割。

2.3 BeanFactoryAware

测试类

public class MyBeanFactoryAware implements BeanFactoryAware {
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {}}

  • 目标:用于获取运行时 Bean 的配置信息
  • 应用:BeanFactoryAware 的实现类能够拿到 beanFactory,也就获取到了 bean 的上下文信息,此时你想获取一些对象的属性就非常容易了。

四、总结

  • 以上咱们介绍了 Spring IOC 的罕用配置个性和接口,尽管当初大家可能曾经很少会应用 xml 配置对象,根本都是注解的形式。但在这些注解的背地仍然会有相应的通用外围原理实现,只有把这部分常识总结分明并学习源码,能力更好的了解注解的应用是如何解决这些配置的。
  • 对于接口的类应用,FactoryBean、BeanPostProcessor、BeanFactoryAware、ApplicationContextAware,在日常的业务流程开发中简直接触不到,但如果要做一些外围的组件设计或者是中间件的开发,就会应用的十分频繁。如果对这部分常识的使用不理解,能够参考:《SpringBoot 中间件设计和开发》
  • 后续会围绕这些知识点来给大家介绍一些源码的学习以及应用层的解决,Bean 的创立、循环依赖的三级缓存解决方案等。也心愿大家在学习的过程中要多总结、思考、记录,一点点的把常识栈建设残缺。

五、系列举荐

  • 小伙伴美团一面的分享和剖析
  • 你说,怎么把 Bean 塞到 Spring 容器?
  • 除了 JDK、CGLIB,还有 3 品种代理形式?面试又卡住!
  • 半年招聘筛选了 400+ 份简历,通知你怎么写容易被撩!
  • SpringBoot 中间件设计和开发

正文完
 0