@Import注解
@Import注解可以导入一些配置类,也就是创建一些指定对象。
使用@Import导入普通类
项目结构中,import-consumer和import-provider都是同层级的module,import-consumer的pom文件中有引用import-provider的依赖;
import-provider中创建接口的实现类:ImportFirstWayProviderConfig
@Slf4j
public class ImportFirstWayProviderConfig implements ImportAnnotationAble{
@PostConstruct
public void init(){
log.info("这是@Import注解第一种用法,直接导入普通类");
}
@Override
public String introduce() {
return "这是@Import注解第一种用法,直接导入普通类";
}
}
import-consumer中创建注解 @TestImportAnnotation
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(value = {ImportFirstWayProviderConfig.class})
public @interface TestImportAnnotation {
}
import-consumer中任意一个@service中使用刚才创建的@TestImportAnnotation注解:
@Service
@TestImportAnnotation
public class ThreeImportService {
}
然后启动 import-consumer module:
可以发现启动的是import-consumer,但是成功将import-provider中ImportFirstWayProviderConfig类给创建了。
实现ImportSelector接口
在import-provider种,添加一个类: ImportSecondWayProviderConfig
@Slf4j
public class ImportSecondWayProviderConfig implements ImportAnnotationAble{
@PostConstruct
public void init(){
log.info("这是@Import注解【第二种】用法,在消费module里实现ImportSelector接口");
}
@Override
public String introduce() {
return "这是@Import注解【第二种】用法,在消费module里实现ImportSelector接口";
}
}
在import-consumer种,实现ImportSelector接口:ImportSecondWayImpl
public class ImportSecondWayImpl implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.example.importProvider.config.importWay.ImportSecondWayProviderConfig"};
}
}
其中返回的字符串就是 ImportSecondWayProviderConfig 的全路径;在@TestImportAnnotation注解中添加:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(value = {ImportFirstWayProviderConfig.class, ImportSecondWayImpl.class})
public @interface TestImportAnnotation {
}
启动import-consumer项目:
可以看到也是成功加载了。
实现ImportBeanDefinitionRegistrar接口
在import-provider中添加 ImportThirdWayProvider 类:
@Slf4j
public class ImportThirdWayProvider implements ImportAnnotationAble{
@PostConstruct
public void init(){
log.info("这是@Import注解《第三种》用法,在消费module里实现ImportBeanDefinitionRegistrar接口");
}
@Override
public String introduce() {
return "这是@Import注解《第三种》用法,在消费module里实现ImportBeanDefinitionRegistrar接口";
}
}
在import-consumer中实现ImportBeanDefinitionRegistrar接口:
public class ImportThirdWayImpl implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition define = new RootBeanDefinition(ImportThirdWayProvider.class);
registry.registerBeanDefinition("importThirdWay",define);
}
}
在RootBeanDefinition的带参构造函数中,参数就是需要的ImportThirdWayProvider 对象,在@TestImportAnnotation注解中添加:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(value = {ImportFirstWayProviderConfig.class, ImportSecondWayImpl.class, ImportThirdWayImpl.class})
public @interface TestImportAnnotation {
}
启动项目:
@ConditionalOnProperty注解
新建一个注解 @ConditionToday:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ConditionalOnProperty(value = "today.isWorkDay", matchIfMissing = true)
public @interface ConditionToday {
}
新建一个注解修饰的类:
@Slf4j
@Configuration(proxyBeanMethods = false)
@ConditionToday
public class ConditionWorkBean {
@PostConstruct
public void init(){
log.info("这是 ConditionWorkBean 的 init方法");
}
}
在application.yml中添加配置:
today:
isWorkDay: true
在spring.factories文件中增加这个类:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.importConsuemer.bean.ConditionWorkBean
成功启动项目:
修改配置为false,再次启动项目,会发现并没有打印 日志,说明没有创建。
@Bean注解修饰方法
之前一直使用@Bean注解方法修饰无参的方法,在看NacosServiceRegistryAutoConfiguration源码的时候,发现修饰的是有参构造方法,好奇参数的值是怎么创建的。
创建两个普通对象 BeanAnnotationUse ,BeanAnnotationUse :
public class BeanHasCreated {
private String name;
public String getName() {
return name;
}
}
@Slf4j
public class BeanAnnotationUse {
private String name;
public String getName() {
return name;
}
public BeanAnnotationUse(String name) {
this.name = name;
}
public BeanAnnotationUse() {
}
public String test(){
log.info("这是使用@Bean注解创建的bean的测试方法,输出的name={}",getName());
return getName();
}
}
再新增一个中间类,TestBeanAnnotation,并将这个类配置再spring.factories中。
@Slf4j
public class TestBeanAnnotation {
@PostConstruct
public void init(){
log.info("这是测试 @Bean注解的初始化方法");
}
@Bean
public BeanAnnotationUse beanAnnotationUse2(BeanHasCreated bhc){
return new BeanAnnotationUse("@Bean修饰有参数的方法");
}
}
编译有报错,并且启动项目也会报错,提示的很清楚:找不到BeanHasCreated这个对象,
@Configuration
public class BeanHasCreated {
private String name;
public String getName() {
return name;
}
}
在BeanHasCreated类上增加@Configuration注解,这个时候编译没报错,启动项目也正常,那就说明@Bean修饰的方法参数必须是已经在spring中注入过的bean。
ApplicationListener接口和ApplicationEventPublisher接口
ApplicationEventPublisher接口时spring提供的一个用来发布事件的接口,ApplicationListener接口时spring提供的一个用来订阅事件发布的接口。
ApplicationListener接口demo
新建一个订阅事件类:
@Configuration
@Slf4j
public class TestWebServerEventListener implements ApplicationListener<WebServerInitializedEvent> {
@Override
public void onApplicationEvent(WebServerInitializedEvent event) {
log.info("接收的容器启动事件的");
}
}
然后直接启动项目,会发现打印结果符合预期。
使用@EventListener注解
自定义事件和自定义订阅
新建自定义事件对象:
public class TestSelfEvent extends ApplicationEvent {
private String eventName;
public TestSelfEvent(Object source) {
super(source);
this.eventName = "随便自定义的事件";
}
public TestSelfEvent(Object source, Clock clock) {
super(source, clock);
}
@Override
public String toString() {
return "TestSelfEvent{" +
"eventName='" + eventName + '\'' +
'}';
}
}
新建发布类:
@Slf4j
@Component
public class TestSelfPublisher implements ApplicationEventPublisher, ApplicationContextAware {
private ApplicationContext context;
@Override
public void publishEvent(Object event) {
log.info("准备发布一个事件:{}",event.toString());
context.publishEvent(event);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
}
新建订阅对象:
@Slf4j
@Configuration
public class SelfEventListener implements ApplicationListener<TestSelfEvent> {
@Override
public void onApplicationEvent(TestSelfEvent event) {
log.info("接收到事件,内容是,event.getSource() = {}",event.getSource());
}
}
再新建一个测试用的类:
@Configuration
@AutoConfigureAfter({TestSelfPublisher.class,SelfEventListener.class})
@Slf4j
public class TestSelfEventImport {
@Autowired
private TestSelfPublisher tsp;
@PostConstruct
public void init(){
TestSelfEvent tsf = new TestSelfEvent("这是一个自定义的随便的任意的一个事件");
tsp.publishEvent(tsf);
}
}
启动项目,打印如下: