SpringApplication
实例public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
SpringApplication
构造方法:属性赋值// 收集所有的容器初始化组件对象:解析 spring.factories 文件获取容器自定义组件
private List<ApplicationContextInitializer<?>> initializers;
// 收集所有的监听器:扩展方式和上面的初始化组件方式差不多
private List<ApplicationListener<?>> listeners;
SpringApplication
的构建都是为了run()
方法启动做铺垫,构造方法中总共就有几行代码,最重要的部分就是设置应用类型、设置初始化器、设置监听器。ApplicationContextInitializer
public class CustomInitializer implements ApplicationContextInitializer {
private static final Logger logger = LoggerFactory.getLogger(CustomInitializer.class);
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
logger.info("====================CustomInitializer start======================");
logger.info("CustomInitializer initialize方法被执行...");
logger.info("ApplicationName: {}", applicationContext.getApplicationName());
logger.info("isActive: {}", applicationContext.isActive());
logger.info("====================CustomInitializer end======================");
}
}
SpringBoot
的SPI
扩展 META-INF
下面 spring.factories
配置自定义组件的位置org.springframework.context.ApplicationContextInitializer=com.li.springbootproject.spring.initializer.CustomInitializer
add
方式添加@SpringBootApplication
public class MySpringBootApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MySpringBootApplication.class);
application.addInitializers(new MyApplicationContextInitializer());
application.run(args);
}
}
ApplicationContextInitializer
是 Spring
对外提供的扩展点之一,用于在 ApplicationContext
容器加载 Bean
之前对当前的上下文进行配置。public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 获取运行监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
// 启动运行监听器
listeners.starting();
try
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 环境构建这一步加载了系统环境配置、用户自定义配置并且广播了ApplicationEnvironmentPreparedEvent事件,触发监听器。
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
// 打印 Banner
Banner printedBanner = printBanner(environment);
// 依据是否为 web 环境创建 web 容器或者普通的 IOC 容器(只是创建) 见下面截图
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// IOC 容器的前置处理,为刷新容器之前做准备,关键操作(将启动类注入容器)
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 刷新容器,完成组件的扫描,创建,加载等
refreshContext(context);
// IOC容器的后置处理
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
BeanFactory
run
方法,run
方法完成了所有Spring
的整个启动过程:准备Environment
——发布事件——创建上下文、bean
——刷新上下文——结束ApplicationContext
,ConfigurableApplicationContext
提供了配置上下文的接口,如设置Environment
、监听器、切面类、关闭上下文的钩子等。@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
// 告诉子类去刷新bean工厂,这步完成后配置文件就解析成一个个bean定义,注册到BeanFactory(但是未被初始化,仅将信息写到了beanDefination的map中)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try {
// 处理自定义的BeanFactoryPostProcess:允许BeanFactoryPostProcessor在容器实例化任何bean之前读取bean的定义(配置元数据)
postProcessBeanFactory(beanFactory);
// 调用BeanFactoryPostProcessor各个实现类的方法
invokeBeanFactoryPostProcessors(beanFactory);
// 注册 BeanPostProcessor 的实现类
// 此接口两个方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
// 两个方法分别在 Bean 初始化之前和初始化之后得到执行。
registerBeanPostProcessors(beanFactory);
// 国际化
initMessageSource();
initApplicationEventMulticaster();
onRefresh();
registerListeners();
// 实例化所有剩余的非懒加载单例 bean
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
}catch(Exception e){
// ...
}
}
}
首先这个注解是由Java
提供的,它用来修饰一个非静态的void
方法。它会在服务器加载Servlet
的时候运行,并且只运行一次。
它的作用在于声明一个Bean
对象初始化完成后执行的方法。
Bean
初始化中的执行顺序:
Constructor(
构造方法) -> @Autowired
(依赖注入) -> @PostConstruct
(注释的方法)
@Component
public class InitStaticVariableThree {
private static final Logger logger = LoggerFactory.getLogger(InitStaticVariableThree.class);
/**
* 静态成员变量
*/
public static RedissonClient redissonClientOne;
/**
* 注入 redissonClient
*/
@Autowired
private RedissonClient redissonClient;
/**
* 使用 PostConstruct 给静态成员变量赋值
*/
@PostConstruct
public void init() {
redissonClientOne = redissonClient;
logger.info(String.valueOf(redissonClient));
logger.info(String.valueOf(redissonClientOne));
}
}
Java
配置和XML
配置的方式来创建Bean
,还有另外一种方式来创建我们的BeanDefinition
。通过 BeanDefinitionRegistryPostProcessor
可以创建一个特别的后置处理器,来将BeanDefinition
添加到BeanDefinitionRegistry
中。BeanDefinition
是对 Bean
的定义,其保存了 Bean
的各种信息,如属性、构造方法参数、是否单例、是否延迟加载等。这里的注册 Bean
是指将 Bean
定义成 BeanDefinition
,之后放入 容器中BeanPostProcessor
不同,BeanPostProcessor
只是在Bean
初始化的时候有个钩子让我们加入一些自定义操作;而BeanDefinitionRegistryPostProcessor
可以让我们在BeanDefinition
中添加一些自定义操作。这就跟类与类实例之间的区别类似。Bean
定义没有被加载,bean
实例还没有被初始化时候。PersonBean
:MyBeanDefinitionRegistryPostProcessor.java
@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
private static final Logger logger = LoggerFactory.getLogger(MyBeanDefinitionRegistryPostProcessor.class);
/**
* 注册 Bean
*
* @param registry
* @throws BeansException
*/
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
logger.info(LogConst.LOG_SUCCESS_PREFIX + "MyBeanDefinitionRegistryPostProcessor 中的 postProcessBeanDefinitionRegistry 方法");
logger.info(LogConst.LOG_SUCCESS_PREFIX + "bean 定义的数据量:" + registry.getBeanDefinitionCount());
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(PersonBean.class);
registry.registerBeanDefinition("personbean", rootBeanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
logger.info(LogConst.LOG_SUCCESS_PREFIX + "MyBeanDefinitionRegisterPostProcessor中的postProcessBeanFactory方法");
logger.info(LogConst.LOG_SUCCESS_PREFIX + "bean 定义的数据量:" + beanFactory.getBeanDefinitionCount());
}
}
PersonBean
:直接注入使用Bean
实现 InitializingBean
public class InitializingBeanExample implements InitializingBean {
private static final Logger logger = LoggerFactory.getLogger(InitializingBeanExample.class);
private int id;
private String name;
@Override
public void afterPropertiesSet() {
logger.info(LogConst.LOG_SUCCESS_PREFIX + " 初始化前的值为:{}", this);
logger.info(LogConst.LOG_SUCCESS_PREFIX + " 我要对 PeopleBean 进行初始化!");
this.id = 100;
this.name = "李四";
logger.info(LogConst.LOG_SUCCESS_PREFIX + " 初始化后的值为:{}", this);
}
}
Bean
@Configuration
public class InitializingBeanExampleTest {
private static final Logger logger = LoggerFactory.getLogger(InitializingBeanExampleTest.class);
@Bean
public InitializingBeanExample initializingBeanExample() {
InitializingBeanExample initializingBeanExample = new InitializingBeanExample();
initializingBeanExample.setId(1);
initializingBeanExample.setName("test1");
logger.info(LogConst.LOG_SUCCESS_PREFIX + initializingBeanExample);
return initializingBeanExample;
}
}
Bean
@Bean(initMethod = "initBean")
@Lazy
public InitMethodBean initMethodBean() {
InitMethodBean initMethodBean = new InitMethodBean();
logger.info(LogConst.LOG_SUCCESS_PREFIX + "实例化 InitMethodBean 信息为:" + initMethodBean);
return initMethodBean;
}
Bean
的 inti-method
方法public class InitMethodBean {
private static final Logger logger = LoggerFactory.getLogger(InitMethodBean.class);
private UUID id;
private String name;
private void initBean() {
this.id = UUID.randomUUID();
this.name = "InitMethodBean";
logger.info(LogConst.LOG_SUCCESS_PREFIX + "执行 InitMethodBean 的 initBean():" + this);
}
// ...
}
Bean
的顺序Constructor > @PostConstruct > InitializingBean > init-method
实现BeanPostProcessor
接口,重写postProcessBeforeInitialization()、postProcessAfterInitialization(),在重写的方法中,可以获得Bean的属性,对Bean
进行相关的操作。
@Component
public class UserBeanPostProcessor implements BeanPostProcessor {
private static final Logger logger = Logger.getLogger(String.valueOf(UserBeanPostProcessor.class));
// 在这里可以拿到bean的相关信息,进行相关操作
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
final String name = "myUser";
if(name.equals(beanName)){
User user = (User) bean;
user.setName("李四");
user.setDate(new Date());
logger.info("=====> postProcessBeforeInitialization():"+ JSONObject.toJSONString(user));
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
postProcessBeforeInitialization()
方法:在 Bean
实例化、属性注入后,初始化前调用。postProcessAfterInitialization()
方法:在 Bean
实例化、属性注入、初始化都完成后调用。springboot
给我们提供了两种方式:ApplicationRunner
和CommandLineRunner
。
这两种方法提供的目的是为了满足,在项目启动的时候立刻执行某些方法。我们可以通过实现ApplicationRunner
和CommandLineRunner
,来实现,他们都是在SpringApplication
执行之后开始执行的。
Spring
容器启动后可以加载一些资源或者做一些业务操作CommandLineRunner
@Component
@Order(1)
public class OrderTestOne implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(OrderTestOne.class);
/**
* 执行
*
* @param args 参数
* @throws Exception
*/
@Override
public void run(String... args) throws Exception {
logger.info("OrderTestOne...");
}
}
order
的值越小,优先级越高order
如果不标注数字,默认最低优先级,因为其默认值是int
最大值@Component
public class OrderTestTwo implements ApplicationRunner, Ordered {
private static final Logger logger = LoggerFactory.getLogger(OrderTestTwo.class);
/**
* 执行
*
* @param args 参数
* @throws Exception
*/
@Override
public void run(ApplicationArguments args) throws Exception {
logger.info("OrderTestTwo...");
}
/**
* 定义执行的顺序
*
* @return int
*/
@Override
public int getOrder() {
return 2;
}
}
@Autowired
private ApplicationContext applicationContextBean;
SpringContextUtils
,定义全局的获取上下文的工具类@Component
public class SpringContextUtils implements ApplicationContextAware {
private static final Logger logger = LoggerFactory.getLogger(SpringContextUtils.class);
/**
* 上下文对象实例
*/
private static ApplicationContext applicationContext = null;
private SpringContextUtils() {
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (Objects.isNull(SpringContextUtils.applicationContext)) {
logger.info(LogConst.LOG_SUCCESS_PREFIX + "ApplicationUtils初始化...");
SpringContextUtils.applicationContext = applicationContext;
}
logger.info(LogConst.LOG_SUCCESS_PREFIX + "ApplicationUtils初始化成功!");
}
/**
* 获得当前的ApplicationContext
*
* @return ApplicationContext
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 根据名称拿到 Bean
*
* @param name Bean 的名称
* @return Object
*/
@SuppressWarnings("all")
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
/**
* 从ApplicationContext中获得Bean并且转型
*
* @param tClass 类
* @param T
* @return T
*/
public static <T> T getBean(Class<T> tClass) {
return getApplicationContext().getBean(tClass);
}
}
Spring
在初始化AutoConfiguration
时会自动传入ApplicationContext
,这时我们就可以使用下面的方式来获取ApplicationContext
:@Configuration
public class TestConfig {
private static final Logger logger = LoggerFactory.getLogger(TestConfig.class);
/**
* 作为参数获取 Bean
*
* @param applicationContext applicationContext
*/
public TestConfig(ApplicationContext applicationContext) {
logger.info(String.valueOf(applicationContext));
}
}
Spring
提供了一个工具类用于获取ApplicationContext
对象:@Autowired
private ServletContext servletContext;
@Override
public String testSix() {
WebApplicationContext context1 = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
WebApplicationContext context2 = WebApplicationContextUtils.getWebApplicationContext(servletContext);
return "测试成功!";
}
Aware这个类主要是辅助Spring
进行访问容器中的数据,在实现这个类重写的方法中,可以拿到相关组件的信息,从而可以进行相应的扩展。
BeanFactoryAware
:获取BeanFactory
容器BeanNameAware
:获取Bean
的名称ApplicationContextAware
:获取ApplicationContext
实现BeanFactoryAware,在setXXX()
中获得相关的Spring
组件。
@Component
public class GetBeanFactory implements BeanFactoryAware {
private static final Logger logger = LoggerFactory.getLogger(GetBeanFactory.class);
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
MyBeans bean = beanFactory.getBean(MyBeans.class);
logger.info("-----> 获得当前的BeanFactory "+ beanFactory);
}
}
Mybatis
中 Mapper
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JHUfeeFt-1647340618646)(images/image-20220313111913059.png)]
BeanFactoryPostProcessor
是实现spring
容器功能扩展的重要接口,例如修改bean
属性值,实现bean
动态代理等。
FactoryBean
不是简单的Bean
,而是一个能生产或者修饰对象生成的工厂Bean
,它的实现与设计模式中的工厂模式和修饰器模式类似。
BeanProduct
@Configuration
public class BeanProduct {
/**
* 定义引擎 Bean
*
* @return SpecialBeanForEngine
*/
@Bean
public SpecialBeanForEngine specialBeanForEngine() {
return new SpecialBeanForEngine();
}
/**
* 定义汽车 Bean 依赖引擎 Bean : 实现 Car 装配 引擎的过程
*
* @param carEngine 汽车引擎
* @return Car
*/
@Bean(initMethod = "start")
public Car car(CarEngine carEngine) {
Car car = new Car();
car.setEngine(carEngine);
return car;
}
}
EngineFactory
的 BeanDefine
public class SpecialBeanForEngine implements BeanFactoryPostProcessor, BeanNameAware {
public String name;
@Override
public void setBeanName(String name) {
this.name = name;
}
public class SpecialBeanForEngine implements BeanFactoryPostProcessor, BeanNameAware {
public String name;
@Override
public void setBeanName(String name) {
this.name = name;
}
/**
* 注册 EngineFactory 的 BeanDefine
*
* @param beanFactory
* @throws BeansException
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) beanFactory;
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(EngineFactory.class);
genericBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
genericBeanDefinition.setAutowireCandidate(true);
beanDefinitionRegistry.registerBeanDefinition("engine007", genericBeanDefinition);
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) beanFactory;
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(EngineFactory.class);
genericBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
genericBeanDefinition.setAutowireCandidate(true);
beanDefinitionRegistry.registerBeanDefinition("engine007",genericBeanDefinition);
}
}
Bean
的配置public class Car implements InitializingBean {
private static final Logger logger = LoggerFactory.getLogger(Car.class);
private String code = String.valueOf(UUID.randomUUID());
/**
* 引擎
*/
private CarEngine engine;
public Car() {
logger.info(LogConst.LOG_SUCCESS_PREFIX + "Car Constructor...");
logger.info(LogConst.LOG_SUCCESS_PREFIX + "Car engine not setting");
}
private void start() {
logger.info(LogConst.LOG_SUCCESS_PREFIX + "Car start...");
engine.fire();
}
@Override
public void afterPropertiesSet() throws Exception {
logger.info("Car initializingBean after propertieSet");
if (engine == null) {
logger.info("Car's engine not setting, in initializingBean ");
} else {
logger.info("Car's engine installed, in initializingBean");
// 使用接口方法
engine.fire();
}
}
@PostConstruct
public void postConstruct() {
logger.info("Car postConstruct");
if (engine == null) {
logger.info("Car engine not setting, in postConstruct");
} else {
logger.info("Car engine installed, in postConstruct");
}
}
}
public interface CarEngine {
void fire();
void close();
}
Bean
EngineFactory
public class EngineFactory implements FactoryBean<CarEngine>, BeanNameAware, InvocationHandler {
private static final Logger logger = LoggerFactory.getLogger(EngineFactory.class);
private String name;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("fire".equalsIgnoreCase(method.getName())) {
logger.info(LogConst.LOG_SUCCESS_PREFIX + "here is invoke engine:" + method.getName());
}
if ("close".equalsIgnoreCase(method.getName())) {
logger.info(LogConst.LOG_SUCCESS_PREFIX + "here is invoke engine:" + method.getName());
}
return null;
}
@Override
public void setBeanName(String name) {
this.name = name;
}
/**
* 获取引擎 CarEngine 的代理对象
*
* @return
*/
@Override
public CarEngine getObject() {
logger.info(LogConst.LOG_SUCCESS_PREFIX + "EngineFactory to build Engine01 , EngineFactory :" + name);
CarEngine carEngine = (CarEngine) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{CarEngine.class}, this);
return carEngine;
}
@Override
public Class<?> getObjectType() {
return CarEngine.class;
}
/**
* 是否是单例 Bean
*
* @return
*/
@Override
public boolean isSingleton() {
return true;
}
}
@Autowired
private CarEngine carEngine;
carEngine.fire();
carEngine.close();
2022-03-12 20:04:25.508 INFO 12836 --- [ main] c.l.s.s.beanfactorypostprocessor.Car : ==========> Car Constructor...
2022-03-12 20:04:25.508 INFO 12836 --- [ main] c.l.s.s.beanfactorypostprocessor.Car : ==========> Car engine not setting
2022-03-12 20:04:25.509 INFO 12836 --- [ main] c.l.s.s.beanfactorypostprocessor.Car : ==========> Car postConstruct
2022-03-12 20:04:25.509 INFO 12836 --- [ main] c.l.s.s.beanfactorypostprocessor.Car : ==========> Car engine installed, in postConstruct
2022-03-12 20:04:25.509 INFO 12836 --- [ main] c.l.s.s.beanfactorypostprocessor.Car : ==========> Car initializingBean after propertieSet
2022-03-12 20:04:25.509 INFO 12836 --- [ main] c.l.s.s.beanfactorypostprocessor.Car : ==========> Car engine installed, in initializingBean
2022-03-12 20:04:25.509 INFO 12836 --- [ main] c.l.s.s.b.EngineFactory : ==========> here is invoke engine:fire
2022-03-12 20:04:25.509 INFO 12836 --- [ main] c.l.s.s.beanfactorypostprocessor.Car : ==========> Car start...
2022-03-12 20:04:25.509 INFO 12836 --- [ main] c.l.s.s.b.EngineFactory : ==========> here is invoke engine:fire
在 Spring
中,初始化容器时会调用 org.springframework.context.ConfigurableApplicationContext
接口中的 reFresh()
方法进行 Bean
的加载,该方法会进行事件的监听注册。
public class UserEvent<T> extends ApplicationEvent {
private T data;
public UserEvent(T source) {
super(source);
this.data =source;
}
public T getData() {
return this.data;
}
public void setData(final T data) {
this.data = data;
}
}
@Autowired
private ApplicationEventPublisher publisher;
/**
* 发布插入user表的事件
*/
private void sendInsertUser() {
User user = new User();
user.setId(20210317);
user.setUsername("测试张三");
UserEvent<User> userEvent = new UserEvent<>(user);
// 发布事件
publisher.publishEvent(userEvent);
}
/**
* 监听插入User的事件 : 在上个事务提交之后在执行
*/
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT,fallbackExecution = true)
public void insertUserLinster(UserEvent<User> userEvent) {
User data = userEvent.getData();
String message = String.format("get user message: %s", JSON.toJSONString(data));
log.info(message);
// 后续操作;继续处理
}
ExitCodeGenerator
接口,生成自己的退出代码@Component
public class ExitCodeApplication implements ExitCodeGenerator {
private static final Logger logger = LoggerFactory.getLogger(ExitCodeApplication.class);
@Override
public int getExitCode() {
logger.info(LogConst.LOG_SUCCESS_PREFIX + "容器已经关机...");
return 999;
}
}
@Autowired
private ApplicationContext applicationContext;
@RequestMapping("/close")
public void testFive(HttpServletRequest request, HttpServletResponse response) {
SpringApplication.exit(applicationContext);
}
SpringBoot
在启动时会去依赖的 starter
包中寻找 /META-INF/spring.factories
文件,然后根据文件中配置的 Jar
包去扫描项目所依赖的 Jar
包,这类似于 Java
的 SPI
机制。
SpringBoot
中的starter
是一种非常重要的机制(自动化配置),能够抛弃以前繁杂的配置,将其统一集成进starter
@Configuration
@ConditionalOnClass(StarterService.class)
@EnableConfigurationProperties(StarterServiceProperties.class)
public class StarterAutoConfigure {
@Autowired
private StarterServiceProperties properties;
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "example.service", value = "enabled", havingValue = "true")
public StarterService starterService() {
return new StarterService(properties.getConfig());
}
}
resources
下META-INF
的spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.li.StarterAutoConfigure
<dependency>
<groupId>com.ligroupId>
<artifactId>springboot-starter-demoartifactId>
<version>1.0-SNAPSHOTversion>
dependency>