spring boot之高级话题--@Enable*注解的工作原理(5)

@Enable*注解的工作原理
1.已经使用过的@Enable*注解
@EnableAspectJAutoProxy:开启AspectJ自动代理的支持。
@EnableAsync:开启异步方法的支持
@EnableScheduling:开启计划任务的支持 
2.即将使用到的@Enable*注解
@EnableMvc:开启springMvc的配置支持
@EnableConfigurationproperties:开启Configurationproperties注解配置bean的支持
@EnableJpaRepositories:开启对springData Jpa Respository的支持。
@EnableCaching:开启注解式的缓存支持。
通过观察(汪云飞老师)@Enable*注解的源码,发现所有的注解都有一个@Import注解,

@Import事用来导入配置类的,这也就意味这些自动开启的实现其实事导入了一些自动配置的Bean。


主要分为一下三种方式。
(本人用的是v4.3.3.RELEASE)

https://github.com/spring-projects/spring-framework/releases/tag/v4.3.3.RELEASE

案例:@EnableScheduling //1@EnableScheduling:开启计划任务的支持

一:直接导入配置类

点击进入源码页面:

第一步被使用注解的源码

/*
 * Copyright 2002-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.scheduling.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.Executor;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;

/**
 * Enables Spring's scheduled task execution capability, similar to
 * functionality found in Spring's {@code } XML namespace. To be used
 * on @{@link Configuration} classes as follows:
 *
 * 
 * @Configuration
 * @EnableScheduling
 * public class AppConfig {
 *
 *     // various @Bean definitions
 * }
* * This enables detection of @{@link Scheduled} annotations on any Spring-managed * bean in the container. For example, given a class {@code MyTask} * *
 * package com.myco.tasks;
 *
 * public class MyTask {
 *
 *     @Scheduled(fixedRate=1000)
 *     public void work() {
 *         // task execution logic
 *     }
 * }
* * the following configuration would ensure that {@code MyTask.work()} is called * once every 1000 ms: * *
 * @Configuration
 * @EnableScheduling
 * public class AppConfig {
 *
 *     @Bean
 *     public MyTask task() {
 *         return new MyTask();
 *     }
 * }
* * Alternatively, if {@code MyTask} were annotated with {@code @Component}, the * following configuration would ensure that its {@code @Scheduled} method is * invoked at the desired interval: * *
 * @Configuration
 * @EnableScheduling
 * @ComponentScan(basePackages="com.myco.tasks")
 * public class AppConfig {
 * }
* * Methods annotated with {@code @Scheduled} may even be declared directly within * {@code @Configuration} classes: * *
 * @Configuration
 * @EnableScheduling
 * public class AppConfig {
 *
 *     @Scheduled(fixedRate=1000)
 *     public void work() {
 *         // task execution logic
 *     }
 * }
* *

By default, will be searching for an associated scheduler definition: either * a unique {@link org.springframework.scheduling.TaskScheduler} bean in the context, * or a {@code TaskScheduler} bean named "taskScheduler" otherwise; the same lookup * will also be performed for a {@link java.util.concurrent.ScheduledExecutorService} * bean. If neither of the two is resolvable, a local single-threaded default * scheduler will be created and used within the registrar. * *

When more control is desired, a {@code @Configuration} class may implement * {@link SchedulingConfigurer}. This allows access to the underlying * {@link ScheduledTaskRegistrar} instance. For example, the following example * demonstrates how to customize the {@link Executor} used to execute scheduled * tasks: * *

 * @Configuration
 * @EnableScheduling
 * public class AppConfig implements SchedulingConfigurer {
 *
 *     @Override
 *     public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
 *         taskRegistrar.setScheduler(taskExecutor());
 *     }
 *
 *     @Bean(destroyMethod="shutdown")
 *     public Executor taskExecutor() {
 *         return Executors.newScheduledThreadPool(100);
 *     }
 * }
* *

Note in the example above the use of {@code @Bean(destroyMethod="shutdown")}. * This ensures that the task executor is properly shut down when the Spring * application context itself is closed. * *

Implementing {@code SchedulingConfigurer} also allows for fine-grained * control over task registration via the {@code ScheduledTaskRegistrar}. * For example, the following configures the execution of a particular bean * method per a custom {@code Trigger} implementation: * *

 * @Configuration
 * @EnableScheduling
 * public class AppConfig implements SchedulingConfigurer {
 *
 *     @Override
 *     public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
 *         taskRegistrar.setScheduler(taskScheduler());
 *         taskRegistrar.addTriggerTask(
 *             new Runnable() {
 *                 public void run() {
 *                     myTask().work();
 *                 }
 *             },
 *             new CustomTrigger()
 *         );
 *     }
 *
 *     @Bean(destroyMethod="shutdown")
 *     public Executor taskScheduler() {
 *         return Executors.newScheduledThreadPool(42);
 *     }
 *
 *     @Bean
 *     public MyTask myTask() {
 *         return new MyTask();
 *     }
 * }
* *

For reference, the example above can be compared to the following Spring XML * configuration: * *

 * {@code
 * 
 *
 *     
 *
 *     
 *
 *     
 *         
 *     
 *
 *     
 *
 * 
 * }
* * The examples are equivalent save that in XML a fixed-rate period is used * instead of a custom {@code Trigger} implementation; this is because the * {@code task:} namespace {@code scheduled} cannot easily expose such support. This is * but one demonstration how the code-based approach allows for maximum configurability * through direct access to actual componentry.

* * @author Chris Beams * @author Juergen Hoeller * @since 3.1 * @see Scheduled * @see SchedulingConfiguration * @see SchedulingConfigurer * @see ScheduledTaskRegistrar * @see Trigger * @see ScheduledAnnotationBeanPostProcessor */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Import(SchedulingConfiguration.class) @Documented public @interface EnableScheduling { }

 除了以下部分其他都注释了。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling {

}
通过@Import注解直接导入了配置类SchedulingConfiguration.class

第二步: 注解中的注解类进入SchedulingConfiguration.class源码

/*
 * Copyright 2002-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.scheduling.annotation;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.scheduling.config.TaskManagementConfigUtils;

/**
 * {@code @Configuration} class that registers a {@link ScheduledAnnotationBeanPostProcessor}
 * bean capable of processing Spring's @{@link Scheduled} annotation.
 *
 * 

This configuration class is automatically imported when using the * {@link EnableScheduling @EnableScheduling} annotation. See * {@code @EnableScheduling}'s javadoc for complete usage details. * * @author Chris Beams * @since 3.1 * @see EnableScheduling * @see ScheduledAnnotationBeanPostProcessor */ @Configuration @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public class SchedulingConfiguration { @Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() { return new ScheduledAnnotationBeanPostProcessor(); } }

这个类注解了@Configuration,并且注册了一个bean

@Configuration@Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)

二:根据条件选择配置类

案例2

@EnableAsync // 1开启异步任务支持

第一步被使用注解的源码

/*
 * Copyright 2002-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.scheduling.annotation;

import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;

/**
 * Enables Spring's asynchronous method execution capability, similar to functionality
 * found in Spring's {@code } XML namespace.
 *
 * 

To be used on @{@link Configuration} classes as follows, where {@code MyAsyncBean} * is a user-defined type with one or more methods annotated with either Spring's * {@code @Async} annotation, the EJB 3.1 {@code @javax.ejb.Asynchronous} annotation, * or any custom annotation specified via the {@link #annotation} attribute. * *

 * @Configuration
 * @EnableAsync
 * public class AppConfig {
 *
 *     @Bean
 *     public MyAsyncBean asyncBean() {
 *         return new MyAsyncBean();
 *     }
 * }
* *

The {@link #mode} attribute controls how advice is applied; if the mode is * {@link AdviceMode#PROXY} (the default), then the other attributes control the behavior * of the proxying. * *

Note that if the {@linkplain #mode} is set to {@link AdviceMode#ASPECTJ}, then the * value of the {@link #proxyTargetClass} attribute will be ignored. Note also that in * this case the {@code spring-aspects} module JAR must be present on the classpath. * *

By default, Spring will be searching for an associated thread pool definition: * either a unique {@link org.springframework.core.task.TaskExecutor} bean in the context, * or an {@link java.util.concurrent.Executor} bean named "taskExecutor" otherwise. If * neither of the two is resolvable, a {@link org.springframework.core.task.SimpleAsyncTaskExecutor} * will be used to process async method invocations. Besides, annotated methods having a * {@code void} return type cannot transmit any exception back to the caller. By default, * such uncaught exceptions are only logged. * *

To customize all this, implement {@link AsyncConfigurer} and provide: *

    *
  • your own {@link java.util.concurrent.Executor Executor} through the * {@link AsyncConfigurer#getAsyncExecutor getAsyncExecutor()} method, and
  • *
  • your own {@link org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler * AsyncUncaughtExceptionHandler} through the {@link AsyncConfigurer#getAsyncUncaughtExceptionHandler * getAsyncUncaughtExceptionHandler()} * method.
  • *
* *
 * @Configuration
 * @EnableAsync
 * public class AppConfig implements AsyncConfigurer {
 *
 *     @Bean
 *     public MyAsyncBean asyncBean() {
 *         return new MyAsyncBean();
 *     }
 *
 *     @Override
 *     public Executor getAsyncExecutor() {
 *         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
 *         executor.setCorePoolSize(7);
 *         executor.setMaxPoolSize(42);
 *         executor.setQueueCapacity(11);
 *         executor.setThreadNamePrefix("MyExecutor-");
 *         executor.initialize();
 *         return executor;
 *     }
 *
 *     @Override
 *     public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
 *         return MyAsyncUncaughtExceptionHandler();
 *     }
 * }
* *

If only one item needs to be customized, {@code null} can be returned to * keep the default settings. Consider also extending from {@link AsyncConfigurerSupport} * when possible. * *

Note: In the above example the {@code ThreadPoolTaskExecutor} is not a fully managed * Spring bean. Add the {@code @Bean} annotation to the {@code getAsyncExecutor()} method * if you want a fully managed bean. In such circumstances it is no longer necessary to * manually call the {@code executor.initialize()} method as this will be invoked * automatically when the bean is initialized. * *

For reference, the example above can be compared to the following Spring XML * configuration: * *

 * {@code
 * 
 *
 *     
 *
 *     
 *
 *     
 *
 *     
 *
 * 
 * }
* * The above XML-based and JavaConfig-based examples are equivalent except for the * setting of the thread name prefix of the {@code Executor}; this is because * the {@code } element does not expose such an attribute. This * demonstrates how the JavaConfig-based approach allows for maximum configurability * through direct access to actual componentry. * * @author Chris Beams * @author Juergen Hoeller * @author Stephane Nicoll * @author Sam Brannen * @since 3.1 * @see Async * @see AsyncConfigurer * @see AsyncConfigurationSelector */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AsyncConfigurationSelector.class) public @interface EnableAsync { /** * Indicate the 'async' annotation type to be detected at either class * or method level. *

By default, both Spring's @{@link Async} annotation and the EJB 3.1 * {@code @javax.ejb.Asynchronous} annotation will be detected. *

This attribute exists so that developers can provide their own * custom annotation type to indicate that a method (or all methods of * a given class) should be invoked asynchronously. */ Class annotation() default Annotation.class; /** * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed * to standard Java interface-based proxies. *

Applicable only if the {@link #mode} is set to {@link AdviceMode#PROXY}. *

The default is {@code false}. *

Note that setting this attribute to {@code true} will affect all * Spring-managed beans requiring proxying, not just those marked with {@code @Async}. * For example, other beans marked with Spring's {@code @Transactional} annotation * will be upgraded to subclass proxying at the same time. This approach has no * negative impact in practice unless one is explicitly expecting one type of proxy * vs. another — for example, in tests. */ boolean proxyTargetClass() default false; /** * Indicate how async advice should be applied. *

The default is {@link AdviceMode#PROXY}. * @see AdviceMode */ AdviceMode mode() default AdviceMode.PROXY; /** * Indicate the order in which the {@link AsyncAnnotationBeanPostProcessor} * should be applied. *

The default is {@link Ordered#LOWEST_PRECEDENCE} in order to run * after all other post-processors, so that it can add an advisor to * existing proxies rather than double-proxy. */ int order() default Ordered.LOWEST_PRECEDENCE; }

第二步:注解中的注解类AsyncConfigurationSelector.class

AsyncConfigurationSelector通过条件来选择需要导入对配置类, AsyncConfigurationSelector的根接口(AsyncConfigurationSelector继承AdviceModeImportSelector),AdviceModeImportSelector实现ImportSelector

AdviceModeImportSelector implements ImportSelector
这个接口需要重写selectImports方法,在此方法内进行事先判断条件。

/*
 * Copyright 2002-2014 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.scheduling.annotation;

import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.AdviceModeImportSelector;

/**
 * Selects which implementation of {@link AbstractAsyncConfiguration} should be used based
 * on the value of {@link EnableAsync#mode} on the importing {@code @Configuration} class.
 *
 * @author Chris Beams
 * @since 3.1
 * @see EnableAsync
 * @see ProxyAsyncConfiguration
 */
public class AsyncConfigurationSelector extends AdviceModeImportSelector {

	private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
			"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";

	/**
	 * {@inheritDoc}
	 * @return {@link ProxyAsyncConfiguration} or {@code AspectJAsyncConfiguration} for
	 * {@code PROXY} and {@code ASPECTJ} values of {@link EnableAsync#mode()}, respectively
	 */
	@Override
	public String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] { ProxyAsyncConfiguration.class.getName() };
			case ASPECTJ:
				return new String[] { ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME };
			default:
				return null;
		}
	}

}
三:动态注册bean

案例:@EnableAspectJAutoProxy注解

第一步:被使用注解的源码

/*
 * Copyright 2002-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Enables support for handling components marked with AspectJ's {@code @Aspect} annotation,
 * similar to functionality found in Spring's {@code } XML element.
 * To be used on @{@link Configuration} classes as follows:
 *
 * 
 * @Configuration
 * @EnableAspectJAutoProxy
 * public class AppConfig {
 *
 *     @Bean
 *     public FooService fooService() {
 *         return new FooService();
 *     }
 *
 *     @Bean
 *     public MyAspect myAspect() {
 *         return new MyAspect();
 *     }
 * }
* * Where {@code FooService} is a typical POJO component and {@code MyAspect} is an * {@code @Aspect}-style aspect: * *
 * public class FooService {
 *
 *     // various methods
 * }
* *
 * @Aspect
 * public class MyAspect {
 *
 *     @Before("execution(* FooService+.*(..))")
 *     public void advice() {
 *         // advise FooService methods as appropriate
 *     }
 * }
* * In the scenario above, {@code @EnableAspectJAutoProxy} ensures that {@code MyAspect} * will be properly processed and that {@code FooService} will be proxied mixing in the * advice that it contributes. * *

Users can control the type of proxy that gets created for {@code FooService} using * the {@link #proxyTargetClass()} attribute. The following enables CGLIB-style 'subclass' * proxies as opposed to the default interface-based JDK proxy approach. * *

 * @Configuration
 * @EnableAspectJAutoProxy(proxyTargetClass=true)
 * public class AppConfig {
 *     // ...
 * }
* *

Note that {@code @Aspect} beans may be component-scanned like any other. Simply * mark the aspect with both {@code @Aspect} and {@code @Component}: * *

 * package com.foo;
 *
 * @Component
 * public class FooService { ... }
 *
 * @Aspect
 * @Component
 * public class MyAspect { ... }
* * Then use the @{@link ComponentScan} annotation to pick both up: * *
 * @Configuration
 * @ComponentScan("com.foo")
 * @EnableAspectJAutoProxy
 * public class AppConfig {
 *
 *     // no explicit @Bean definitions required
 * }
* * @author Chris Beams * @author Juergen Hoeller * @since 3.1 * @see org.aspectj.lang.annotation.Aspect */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy { /** * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed * to standard Java interface-based proxies. The default is {@code false}. */ boolean proxyTargetClass() default false; /** * Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal} * for retrieval via the {@link org.springframework.aop.framework.AopContext} class. * Off by default, i.e. no guarantees that {@code AopContext} access will work. * @since 4.3.1 */ boolean exposeProxy() default false; }
第二步:注解的注解类AspectJAutoProxyRegistrar.class源码

/*
 * Copyright 2002-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.context.annotation;

import org.springframework.aop.config.AopConfigUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;

/**
 * Registers an {@link org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
 * AnnotationAwareAspectJAutoProxyCreator} against the current {@link BeanDefinitionRegistry}
 * as appropriate based on a given @{@link EnableAspectJAutoProxy} annotation.
 *
 * @author Chris Beams
 * @author Juergen Hoeller
 * @since 3.1
 * @see EnableAspectJAutoProxy
 */
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

	/**
	 * Register, escalate, and configure the AspectJ auto proxy creator based on the value
	 * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
	 * {@code @Configuration} class.
	 */
	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
			AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
		}
		if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
			AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
		}
	}

}

AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar, ImportBeanDefinitionRegistrar的作用是在运行时自动添加bean到已有的配置类,通过方法重写
public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

其中AnnotationMetadata参数用来获取当前配置类的上的注解;BeanDefinitionRegistry参数用来注册bean。


你可能感兴趣的:(spring,boot之mvc注解)