IoC也称为依赖项注入(DI)。这是一个对象仅通过构造函数参数、工厂方法的参数或对象实例构造或从工厂方法返回后在对象实例上设置的属性来定义其依赖项(即使用的其他对象)的过程。然后容器在创建bean时注入这些依赖项。这个过程基本上是bean本身的逆过程(因此称为控制反转),通过使用类的直接构造或服务定位器模式等机制来控制其依赖项的实例化或位置。
通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦,降低程序间的耦合关系
参考链接:https://docs.spring.io/spring/docs/5.2.1.RELEASE/spring-framework-reference/core.html#beans
https://gitee.com/chenscript/spring_ioc_aop_mvc_learning.git
1.先看main方法
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(MyConfiguration.class);
context.refresh();
MyService bean = context.getBean(MyService.class);
String item = bean.getItem();
System.out.println(item);
使用AnnotationConfigApplicationContext()获取上下文,在注入配置,后再刷新配置,相当于重启IOC容器。
2.看看配置类是怎么实现的:
@Configuration表明这是个配置类,@Bean则表示该方法会返回一个bean实例,并会注入到spring容器中,通常方法名就是这个bean的名字。 但是如果new MyService(name)使用了构造函数,并且参数是name,则该bean的实例化id就是这个name参数。
3.maven依赖
spring依赖的引入(版本5.2.1.RELEASE),maven会帮我们下载好spring需要的几个包
以上编写好后,直接运行main()就能实现注解版的spring了。
@ComponentScan(basePackages = “com.spring.annotation_config_demo.pkg1”)
在basePackages 这个包下面的类都会被进行注解扫描
@Service、@Component和@Repository 的基类注解
一般配合@Autowired使用,区分同一个类不同的实例
如果你要自定义添加一个属性文件,那么可以使用@PropertySource注解。
@Configuration
@PropertySource("classpath:com.spring.annotation_demo/service.properties")
public class PropertiesConfig {
}
那么,如何获取这个属性文件的值呢? 可以配合@Value进行获取使用,也可以获取属性类型对象PropertySourcesPlaceholderConfigurer (请看下个注解)
public MyService01(@Value("${service.myname}") String name) {
this.name = name;
}
代码示例:码云目录(com.spring.annotation_demo)
<bean id="properties"
class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="properties">
<value>
datasource.url=www.baidu.com
datasource.username=chen3
datasource.password=123456
</value>
</property>
</bean>
代码获取属性:
PropertySourcesPlaceholderConfigurer myDataSource = (PropertySourcesPlaceholderConfigurer) context.getBean("myDataSource");
PropertySources sources = myDataSource.getAppliedPropertySources();
PropertySource<?> localProperties = sources.get("localProperties");
String propertySource = (String) localProperties.getProperty("jdbc.driver.className");
System.out.println(propertySource);
该注解的作用:
Indicates one or more component classes to import — typically @Configuration classes.
Provides functionality equivalent to the element in Spring XML
引入一个或多个@Configuration组件,等同于xml中的标签。
也就是能引入多个配置,从而形成隔离分类但还能生效的作用。(代码示例:com.spring.annotation_import_demo)
在每个配置类中加上注解
@Configuration
@Profile("dev")
public class DevConfig {
然后在启动时,显示的setActiveProfiles(“dev”)就可以把配置切换成开发环境了。
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("dev");
ctx.register(DevConfig.class, ProConfig.class);
ctx.refresh();
事件驱动,可以让我们进行更有效的解耦操作。也是为一些没有布上mq啥的做一些前期准备吧。
同步和异步的都在代码上,这里贴一下实现截图
@Configuration
@EnableAsync
public class Config {
被监听的事件:
public class BlackListEvent extends ApplicationEvent
@EventListener
public void processBlackListEvent(BlackListEvent event) {
异步这里我有点话~~
异步需要线程池的支持,所以这里需要注入一个线程池TaskExecutor()
@Bean(name="processExecutor")
public TaskExecutor workExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setThreadNamePrefix("Async-");
threadPoolTaskExecutor.setCorePoolSize(10);
threadPoolTaskExecutor.setMaxPoolSize(20);
threadPoolTaskExecutor.setQueueCapacity(600);
threadPoolTaskExecutor.afterPropertiesSet();
// 自定义拒绝策略
threadPoolTaskExecutor.setRejectedExecutionHandler((r, executor) -> {
// .....
});
// 使用预设的拒绝策略
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return threadPoolTaskExecutor;
}
然后再在监听的方法上加上就能实现异步了
@EventListener
@Async//异步
public void processSecondEvent(SecondEvent event) {
No qualifying bean of type ‘com.spring.primary_demo.IMyService’ available: expected single matching bean but found 2: myService001,myService002
示例代码:com.spring.primary_demo
@Component
@Primary
public class MyService001 implements IMyService{
该注解的作用可以让被修饰的beanA的初始化后于depends-on中的bean初始化,若beanA初始化时未发现depends-on中的bean初始化,则会强制初始化其中的bean,然后再初始化beanA。
示例:com.spring.depends_on_demo
<bean id="beanOne" class="com.spring.depends_on_demo.ExampleBean" depends-on="manager"/>
示例代码:com.spring.factorybean_demo
主要实现了FactoryBean方法,可以控制单例多例的实现
public class MyFactoryBean implements FactoryBean<CustomService>{
@Override
public CustomService getObject(){
return new CustomService(1);
}
@Override
public Class<?> getObjectType() {
return CustomService.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
这儿可以看tomcat源码上有个hook。
另外,如果自己手动实现一个web的话,可以设置一个按钮,关闭整个服务器,这时候就可以用这东西了(应该很少用,但是之前公司里使用的dubbo泛化调用就这么玩的)
public static void main(String[] args) throws InterruptedException {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
ctx.registerShutdownHook();
int n = 0;
while (n<5){
Thread.sleep(1000);
System.out.println("hehe");
n++;
}
}
@Inject相当于@Autowired ; @Name相当于@Component ,前提要引入依赖:
官网链接:
https://docs.spring.io/spring/docs/5.2.1.RELEASE/spring-framework-reference/core.html#beans-standard-annotations
<!--JSR330-->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
示例代码:com.spring.i18n_demo
玩这个,其实就是key-value形式的属性文件上的不同语言版本的内容,然后就在获取时传参数
1.spring.xml
<beans>
<!-- this MessageSource is being used in a web application -->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="i18n.exceptions"/>
<property name="defaultEncoding" value="UTF-8"/>
</bean>
</beans>
public class App {
public static void main(String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("spring.xml");
String message1 = resources.getMessage("argument.required",
new Object [] {"userDao"}, "Required", Locale.SIMPLIFIED_CHINESE);
System.out.println(message1);
}
}
没想好怎么写。
关于注解版的常用注解已经差不多解释完了,虽然也掺杂了一些xml形式,但不难转换成注解版的。(是我偷懒了~~)下面该讲原理了,就是IOC主要的构建流程。
不管是xml形式还是注解形式的spring,构建ioc容器核心都是refresh()方法,所以这是重点对象。坐标:AbstractApplicationContext#refresh(),整个方法分成了10个步骤。下面一一介绍:
Specify an id for serialization purposes, allowing this BeanFactory to be
deserialized from this id back into the BeanFactory object, if needed.
上一步只是创建DefaultListableBeanFactory 对象而已,这一步就相当于为它装配属性吧,装配标准BeanFactory该有的属性。
1.将DefaultListableBeanFactory 注入application属性中(新构建ApplicationContextAwareProcessor)
2.将ApplicationContextAwareProcessor作为处理器注入AbstractBeanFactory的beanPostProcessors 中,为第(5)步做准备
注册一个BootstrapContextAwareProcessor类,我理解是用来启动各种*Aware类的
目标对象:生成Bean的工厂—BeanFactory
这里做了两件事:
1.registryProcessor.postProcessBeanDefinitionRegistry(registry); —注册postProcessBean
----> 1.1 区分出实现PriorityOrdered,Ordered 和不实现Ordered的注册器,因为有些注册器存在依赖关系(就是依赖上一个注册器的结果),所有要实现Ordered接口。
-----> 1.2 先注册PriorityOrdered,后注册ordered接口的注册器,再注册没有实现ordered的接口的。2.invokeBeanFactoryPostProcessors —调用工厂的后置处理器
-----> 后置处理器处理的逻辑和注册器一样。
-----> 1.区分出实现PriorityOrdered,Ordered 和不实现Ordered的后置处理器
-----> 2.先调用PriorityOrdered,后调用ordered接口的注册器,再调用没有实现ordered的接口的。
这两步完成之后,算是BeanFactory创建完成了,接下来的步骤就是要注册我们应用程序中使用到的Bean了
多说一句,其实(5)中的beanFactory也算是JavaBean,所以Bean的创建步骤和(5)差不多。
差在哪呢?
1.这个internalPostProcessors这里,
AutowiredAnnotationBeanPostProcessor — 看类名可以推出是和@Autowired注解有关的后置处理器
CommonAnnotationBeanPostProcessor — 这也是跟注解有关的后置处理器。Common,那就是共同的注解吧。而AutowiredAnnotationBeanPostProcessor 则是注解后置处理器中的特例。。
2.最后会重新注册一个Bean后置处理器:ApplicationListenerDetector
也是为了在以后步骤中遍历处理完Bean的后置处理器,进行的一些监听器的操作。
初始化系统显示的信息类型,可以参考i18n国际化示例
广播ApplicationEvent 的操作,启动各种观察者对相对应的行为
这一步就主要是初始化ApplicationEventMulticaster实例而已,真正的操作也就是
setFactoryBean给这个实例。
这是初始化一些特殊的bean,据我所知,在springboot中的这个方法里,被重写了,用于热重启tomcat的。
1.为广播器装上监听器
获取所有监听器并添加到广播器中
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
发布早期的事件,并开启相关监听器
// Publish early application events now that we finally have a multicaster...
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (earlyEventsToProcess != null) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
Instantiate all remaining (non-lazy-init) singletons. – 初始化剩下的不是懒加载的单例
这代码片段就是证据
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
从定义好的bean中挑选上面这个条件的单例。
关键步骤:
beanFactory.preInstantiateSingletons();
1.RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); --获取bean信息1判断是否是工厂方法bean,如果是,在判断是否是热初始化
2获取bean实例 getBean(benaName)中的GetBeanName();1 若缓存中存在已经单例的,直接获取返回。bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
2 获取工厂中的bean,如果有,则直接返回
3 先实例化depend-on的类,再进行判断该生成的bean是单例还是多例实现1如果是单例实现,直接创建bean , createBean(beanName, mbd, args);
1 获取RootBeanDefinition mbdToUse = mbd;
2 覆盖重写的方法。mbdToUse.prepareMethodOverrides();
3 进行初始化前的解析 Object bean = resolveBeforeInstantiation(beanName, mbdToUse); 主要是处理bean后置处理器
4 然后就是创建bean Object beanInstance = doCreateBean(beanName, mbdToUse, args);1 instanceWrapper = createBeanInstance(beanName, mbd, args); --这一步很多地方使用上了反射api
2 final Object bean = instanceWrapper.getWrappedInstance(); 获取包装后的bean
3 执行 合并bean定义 applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
4 部署bean的属性 populateBean(beanName, mbd, instanceWrapper);
5 exposedObject = initializeBean(beanName, exposedObject, mbd);1 创建前处理 wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
2 如果bean 继承InitializingBean 实现它的重写方法 ((InitializingBean) bean).afterPropertiesSet();,如果存在init()自定义方法,则也在这个点执行。invokeCustomInitMethod(beanName, bean, mbd);
3 创建后处理wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);2 根据SmartInitializingSingleton 类,在初始化单例后,智能处理相关事件。
典型的synchronized锁的加法:
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
至此,(10)步骤已经结束。
主要工作还是推行事件发布
publishEvent(new ContextRefreshedEvent(this));
监听器是广播发布的。
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
ApplicationContext
BeanFactory:
FactoryBean
ApplicationListener
ApplicationAware
BeanPostProcessor
MessageSource:
Spring 提供了以下五种标准的事件:
上下文更新事件(ContextRefreshedEvent):该事件会在ApplicationContext 被初始化或者更新时发布。也可以在调用ConfigurableApplicationContext 接口中的 #refresh() 方法时被触发。
上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext 的 #start() 方法开始/重新开始容器时触发该事件。
上下文停止事件(ContextStoppedEvent):当容器调用 ConfigurableApplicationContext 的 #stop() 方法停止容器时触发该事件。
上下文关闭事件(ContextClosedEvent):当ApplicationContext 被关闭时触发该事件。容器被关闭时,其管理的所有单例 Bean 都被销毁。
请求处理事件(RequestHandledEvent):在 We b应用中,当一个HTTP 请求(request)结束触发该事件。