文章目录
IOC
明确定义组件的接口,独立开发不同的组件,然后按照依赖组装运行。将创建和管理的权利交给spring框架。其核心是Bean工厂(bean factory),能够让各个组件之间保持松散的耦合,降低了业务对象替代的复杂性。
IOC 控制反转与DI 依赖注入
控制的什么被反转了?就是:获得依赖对象的方式反转了
对象@Bean
spring boot 常用的管理Bean的注解
- name:指定Bean的名称,默认为方法名
生命周期
来源org.springframework.beans.factory.BeanFactory
的JavaDoc
创建Bean
- BeanNameAware的setBeanName
- BeanClassLoaderAware的setBeanClassLoader
- BeanFactoryAware的setBeanFactory
- EnvironmentAware的setEnvironment
- EmbeddedValueResolverAware的setEmbeddedValueResolver
- ResourceLoaderAware的setResourceLoader (仅在在应用程序上下文中运行时适用)
- ApplicationEventPublisherAware的setApplicationEventPublisher (仅适用于在应用程序上下文中运行的情况)
- MessageSourceAware的setMessageSource (仅适用于在应用程序上下文中运行的情况)
- ApplicationContextAware的setApplicationContext (仅适用于在应用程序上下文中运行的情况)
- ServletContextAware的setServletContext (仅适用于在Web应用程序上下文中运行的情况)
- BeanPostProcessors的postProcessBeforeInitialization方法
- InitializingBean的afterPropertiesSet
- 自定义的初始化方法定义
- BeanPostProcessors的postProcessAfterInitialization方法
销毁Bean
- DestructionAwareBeanPostProcessors的postProcessBeforeDestruction方法
- DisposableBean的destroy
- 自定义销毁方法定
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import com.example.demo.domain.MyBean;
@Configuration
public class BeanConfig {
@Bean
public MyBean myBean() {
return new MyBean();
}
}
作用域 @Scope
指定当前Bean的作用域和@Bean一起使用
作用域 | 描述 |
---|---|
singleton | 单例模式,默认模式 |
prototype | 每次都会返回新的Bean |
– | 以下几种作用域仅适用于web的Spring WebApplicationContext环境 |
request | 每个http request 会创建一个新的Bean |
session | 每个http session 会创建一个新的Bean |
application | 限定一个Bean的作用域为ServletContext的生命周期 |
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import com.example.demo.domain.MyBean;
@Configuration
public class BeanConfig {
@Bean
@Scope("prototype")
public MyBean myBean() {
return new MyBean();
}
}
延迟加载 @Lazy
该注解会让Bean延迟加载,即第一次使用时加载(默认是spring 容器启动时加载)
package com.example.demo.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.demo.domain.MyBean;
@RestController
public class BeanController {
public static final Logger LOGGER = LoggerFactory.getLogger(BeanController.class);
@Autowired
@Lazy
private MyBean myBean0;
@Autowired
@Lazy
private MyBean myBean1;
@GetMapping("printBean")
public void printBean() {
LOGGER.info(myBean0.toString());
LOGGER.info(myBean1.toString());
}
}
默认的 @Primary
在不使用@Qualifier
指定BeanName的时候,默认都会使用@Primary
的注解
package com.example.demo.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.example.demo.domain.MyBean;
@Configuration
public class BeanConfig {
public static final Logger LOGGER = LoggerFactory.getLogger(BeanConfig.class);
@Bean
@Primary
public MyBean myBean() {
final MyBean myBean = new MyBean();
LOGGER.info("Primary,{}", myBean);
return myBean;
}
@Bean
public MyBean myBean3() {
final MyBean myBean = new MyBean();
LOGGER.info(myBean.toString());
return myBean;
}
}
特定的 @Qualifier
通过指定BeanName,使用某一个Bean
package com.example.demo.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.demo.domain.MyBean;
@RestController
public class BeanController {
public static final Logger LOGGER = LoggerFactory.getLogger(BeanController.class);
@Autowired
private MyBean myBean0;
@Autowired
@Qualifier("myBean3")
private MyBean myBean1;
@GetMapping("printBean")
public void printBean() {
LOGGER.info(myBean0.toString());
LOGGER.info(myBean1.toString());
}
}
依赖 @DependsOn
改变Bean加载的顺序,myBean3 会在Mybean 之前加载
package com.example.demo.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import com.example.demo.domain.MyBean;
@Configuration
public class BeanConfig {
public static final Logger LOGGER = LoggerFactory.getLogger(BeanConfig.class);
@Bean
@DependsOn("myBean3")
public MyBean myBean() {
final MyBean myBean = new MyBean();
LOGGER.info("Primary,{}", myBean);
return myBean;
}
@Bean
public MyBean myBean3() {
final MyBean myBean = new MyBean();
LOGGER.info(myBean.toString());
return myBean;
}
}
ApplicationContext
BeanFactory
是Spring框架最核心的接口,它提供了高级IOC的配置机制。
ApplicationContext
建立在BeanFactory
的基础上,提供了更多面向应用的功能, 它提供了国际化支持和框架事件体系。
区别
描述
- BeanFactory=>IOC容器
- ApplicationContext=>应用上下文,或者spring容器
使用
BeanFactory
是Spring框架的基础设施,面向Spring本身ApplicationContext
面向使用Spring框架的开发者,几乎所有的应用场合都可以直接使用ApplicationContext
而非底层的BeanFactory
ApplicationContext使用
@Autowired
private ApplicationContext applicationContext;
获取Bean
// 从spring容器获取Bean
final BeanController bean = applicationContext.getBean(BeanController.class);
发布事件
主要部分
- 事件 继承
ApplicationEvent
拓展自己属性
package com.example.demo.event;
import org.springframework.context.ApplicationEvent;
/**
* @author lb
*/
public class LoginEvent extends ApplicationEvent {
private String userName;
/**
* Create a new {@code ApplicationEvent}.
*
* @param source the object on which the event initially occurred or with
* which the event is associated (never {@code null})
*/
public LoginEvent(Object source) {
super(source);
}
public LoginEvent(Object source, String userName) {
super(source);
this.userName = userName;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
- 发布事件 使用
ApplicationContext
发布一个事件
applicationContext.publishEvent(new LoginEvent(this, userName));
- 监听事件 使用
@EventListener
注解,或者实现ApplicationListener<T>
接口
@EventListener(LoginEvent.class)
public void onLoginEvent(LoginEvent event) {
LOGGER.info("onLoginEvent,{}", event.getUserName());
}
监听器
- 排序 使用
@Order
注解,越小优先度越高 - 继续发布事件,返回一个事件
- 异步,使用
@EnableAsync
和@Async
注解- 注意事项,不能用返回事件的方式继续发布事件,需要手动发布
- 异常不会传播到时间发布方,需要手动处理
package com.example.demo.event;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;
/**
* @author admin
*/
@Component
@EnableAsync
public class LoginListener {
public static final Logger LOGGER = LoggerFactory.getLogger(LoginListener.class);
/**
* 发布LogoutEvent事件
*
* @param event 事件
* @return LogoutEvent事件
*/
@EventListener(LoginEvent.class)
@Order(1)
public LogoutEvent onApplicationEvent(LoginEvent event) {
LOGGER.info("onApplicationEvent,{}", event.getUserName());
return new LogoutEvent(event.getSource(), event.getUserName());
}
/**
* 异步处理
* 优先于{@link this#onApplicationEvent(LoginEvent)}执行
*
* @param event 事件
* @throws InterruptedException 异常
*/
@EventListener(LoginEvent.class)
@Order(0)
@Async
public void onLoginEvent(LoginEvent event) throws InterruptedException {
LOGGER.info("onLoginEvent,{}", event.getUserName());
TimeUnit.SECONDS.sleep(50);
}
/**
* @param event 事件
*/
@EventListener
public void onLogoutEvent(LogoutEvent event) {
LOGGER.info("onLogoutEvent,{}", event.getUserName());
}
}
访问资源
Resource
本质上是JDKjava.net.URL
类的功能更丰富的版本。
可以以透明的方式从几乎任何位置获取低级资源,包括从类路径,文件系统位置,可使用标准URL描述的任何位置以及一些其他变体。
/**
* resources
*/
@GetMapping("resources")
public void login() throws IOException {
// 系统位置文件
final Resource resource = applicationContext.getResource(ResourceUtils.FILE_URL_PREFIX + "/data/uuid.txt");
LOGGER.info("resource:{}:{}", resource.getDescription(), resource.contentLength());
// 类路径文件,resource文件夹下等
final Resource classPathResource = applicationContext.getResource(ResourceUtils.CLASSPATH_URL_PREFIX + "resources.txt");
LOGGER.info("classPathResource:{}:{}", classPathResource.getDescription(), classPathResource.contentLength());
// 网络资源
final Resource urlResource = applicationContext.getResource("https://helpimage.paperol.cn//20210120161831.png");
LOGGER.info("urlResource:{}:{}", urlResource.getDescription(), urlResource.contentLength());
}
2021-01-25 11:51:01.997 INFO 8204 --- [nio-8080-exec-1] c.e.demo.controller.BeanController : resource:URL [file:/data/uuid.txt]:18278
2021-01-25 11:51:01.998 INFO 8204 --- [nio-8080-exec-1] c.e.demo.controller.BeanController : classPathResource:class path resource [resources.txt]:1845
2021-01-25 11:51:03.140 INFO 8204 --- [nio-8080-exec-1] c.e.demo.controller.BeanController : urlResource:URL [https://helpimage.paperol.cn//20210120161831.png]:219156