springBoot详解

springBoot

原理初探

pom.xml

  • spring-boot-deoendencies:核心依赖都在父工程中
  • 我们写或者引入springboot依赖不需要指定版本就是因为有版本仓库

启动器

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>

启动器:springboot的启动场景

比如spring-boot-starter-web会帮我们自动导入web环境所需的依赖

springboot会将所有的功能场景变成启动器

依赖版本查看地址:https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.build-systems.dependency-management

主程序

#标记这是一个springboot应用
@SpringBootApplication
public class XxlJobExecutorApplication {

    public static void main(String[] args) {
        SpringApplication.run(XxlJobExecutorApplication.class, args);
    }

}
@SpringBootApplication核心注解
  • @SpringBootConfiguration:springboot的配置

    • @SpringBootConfiguration:spring配置类
      • @Compent:说明这是一个spring组件
  • @EnableAutoConfiguration:自动配置

    • @AutoConfigurationPackage:自动配置包

      • @Import(@Import(AutoConfigurcationPackages.Registrar.class)):自动配置包注册
    • @Import(AutoConfigurationImportSelector.class):自动配置导入选择

      #获取所有的配置
      List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
      

获取候选配置

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
    #spring.factories:资源工厂文件包含加载启动需要的文件
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

getSpringFactoriesLoaderFactoryClass() :获取标记自动加载的类

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    return EnableAutoConfiguration.class;
}

SpringFactoriesLoader:获取加载器工厂

#spring加载器工厂核心文件
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
        Assert.notNull(factoryType, "'factoryType' must not be null");
        ClassLoader classLoaderToUse = classLoader;
        if (classLoader == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }

        List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
        if (logger.isTraceEnabled()) {
            logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
        }

        List<T> result = new ArrayList(factoryImplementationNames.size());
        Iterator var5 = factoryImplementationNames.iterator();

        while(var5.hasNext()) {
            String factoryImplementationName = (String)var5.next();
            result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
        }

        AnnotationAwareOrderComparator.sort(result);
        return result;
    }

#标记自动装配的类,遍历资源并封装成properties资源文件
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
        Map<String, List<String>> result = (Map)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            HashMap result = new HashMap();

            try {
                Enumeration urls = classLoader.getResources("META-INF/spring.factories");

                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        String factoryTypeName = ((String)entry.getKey()).trim();
                        String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        String[] var10 = factoryImplementationNames;
                        int var11 = factoryImplementationNames.length;

                        for(int var12 = 0; var12 < var11; ++var12) {
                            String factoryImplementationName = var10[var12];
                            ((List)result.computeIfAbsent(factoryTypeName, (key) -> {
                                return new ArrayList();
                            })).add(factoryImplementationName.trim());
                        }
                    }
                }

                result.replaceAll((factoryType, implementations) -> {
                    return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
                });
                cache.put(classLoader, result);
                return result;
            } catch (IOException var14) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
            }
        }
    }

资源加载判断

spring.factories:spring所需资源文件

#判断资源是否满足加载条件 不满足class文件报红则表示不加载对应所需的资源文件
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@ConditionalOnClass({ MongoClient.class, Flux.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)

@EnableConfigurationProperties(MongoProperties.class):开启配置文件

结论:springboot所有的自动配置都是在启动的时候扫描加载。所有的配置类都在spring.factories里面,但是1需要判断是否有对应的jar包,满足才会加载。

  1. springboot启动会在spring.factories获取指定的值
  2. 将这些自动配置的类导入容器
启动

配置文件

application.yml

对空格要求严格

#普通key value
serverName: 应用
#对象
server:
  port: 8888
  name: 应用、
#行内写法
server: {port: 8888,name: 应用}
#数组
server: [port,name,address]
server:
  -port
  -name
  -address
#可以给实体类赋值
server:
 name: 服务
 port: 8888
 address: 127.0.0.1
@Compent
//数据校验
@ValiDated
//绑定配置文件中对应配置
@ConfigurationProperties(prefix="server")
public class server{
    //规定长度
    @Length(min = 6, max = 19, message = "服务名长度是6-18位")
    private String name;
    //不能为空
    @NotEmpty
    private String port;
    //不能为null
    @NotNull
    private String address;
}

@ValiDated

img

img

多环境配置

properties

#首先创建配置文件作为主文件(默认加载文件)、其次创建dev开发环境、product生产环境、test测试环境配置文件
#开启springboot多环境配置,可选择激活配置
spring.profiles.active=dev/test/product

yaml

server:
 port: 8888
spring:
 profiles: test
  #配置文件选择
  active: test/dev/product
---
server:
 port: 8899
spring:
 profiles: dev
---
server:
 port: 8887
spring:
 profiles: product
debug: true

加载静态资源文件

资源文件加载

WebMvcAutoConfiguration:web自动配置类

private static final String SERVLET_LOCATION = "/";

        @SuppressWarnings("deprecation")
        public EnableWebMvcConfiguration(
                org.springframework.boot.autoconfigure.web.ResourceProperties resourceProperties,
                WebMvcProperties mvcProperties, WebProperties webProperties,
                ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider,
                ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,
                ListableBeanFactory beanFactory) {
            //resourceProperties.hasBeenCustomized()根据chain cache是否定制化静态资源加载默认是false
            //如果resourceProperties.hasBeenCustomized() true 执行 resourceProperties 否则webProperties.getResources()
            this.resourceProperties = resourceProperties.hasBeenCustomized() ? resourceProperties
                    : webProperties.getResources();
            this.mvcProperties = mvcProperties;
            this.webProperties = webProperties;
            this.mvcRegistrations = mvcRegistrationsProvider.getIfUnique();
            this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
            this.beanFactory = beanFactory;
        }        
        @Override
        //静态资源映射注册
        protected void addResourceHandlers(ResourceHandlerRegistry registry) {
            super.addResourceHandlers(registry);
             //判断资源是否自定 --自定义失效
            if (!this.resourceProperties.isAddMappings()) {
                logger.debug("Default resource handling disabled");
                return;
            }
            ServletContext servletContext = getServletContext();
             //webJars静态资源加载
            addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
             //加载指定路径--关键默认
            addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
                registration.addResourceLocations(this.resourceProperties.getStaticLocations());
                if (servletContext != null) {
                    registration.addResourceLocations(new ServletContextResource(servletContext, SERVLET_LOCATION));
                }
            });
        }

        private void addResourceHandler(ResourceHandlerRegistry registry, String pattern, String... locations) {
            addResourceHandler(registry, pattern, (registration) -> registration.addResourceLocations(locations));
        }

        private void addResourceHandler(ResourceHandlerRegistry registry, String pattern,
                Consumer<ResourceHandlerRegistration> customizer) {
            if (registry.hasMappingForPattern(pattern)) {
                return;
            }
            ResourceHandlerRegistration registration = registry.addResourceHandler(pattern);
            customizer.accept(registration);
            registration.setCachePeriod(getSeconds(this.resourceProperties.getCache().getPeriod()));
            registration.setCacheControl(this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl());
            customizeResourceHandlerRegistration(registration);
        }
        //首页视图解析
        @Bean
        public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
                FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
            WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
                    //获取是否默认
                    new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
                    this.mvcProperties.getStaticPathPattern());
            welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
            welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());
            return welcomePageHandlerMapping;
        }
        //获取index.html资源
        private Resource getWelcomePage() {
            for (String location : this.resourceProperties.getStaticLocations()) {
                 //调用getIndexHtml获取静态资源文件信息
                Resource indexHtml = getIndexHtml(location);
                 //不为空说明存在index.html
                if (indexHtml != null) {
                    return indexHtml;
                }
            }
            //获取上下文对象
            ServletContext servletContext = getServletContext();
            if (servletContext != null) {
                 //返回上下文对象/
                return getIndexHtml(new ServletContextResource(servletContext, SERVLET_LOCATION));
            }
            return null;
        }

        private Resource getIndexHtml(String location) {
            return getIndexHtml(this.resourceLoader.getResource(location));
        }

        private Resource getIndexHtml(Resource location) {
            try {
                Resource resource = location.createRelative("index.html");
                if (resource.exists() && (resource.getURL() != null)) {
                    return resource;
                }
            }
            catch (Exception ex) {
            }
            return null;
        }    

webProperties:web文件资源

//指定加载的文件目录名称和加载优先级
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
                "classpath:/resources/", "classpath:/static/", "classpath:/public/" };
private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;

public String[] getStaticLocations() {
            return this.staticLocations;
        }

        public void setStaticLocations(String[] staticLocations) {
            this.staticLocations = appendSlashIfNecessary(staticLocations);
            this.customized = true;
        }

        private String[] appendSlashIfNecessary(String[] staticLocations) {
            String[] normalized = new String[staticLocations.length];
            for (int i = 0; i < staticLocations.length; i++) {
                String location = staticLocations[i];
                normalized[i] = location.endsWith("/") ? location : location + "/";
            }
            return normalized;
        }

servletContextResource:上下文资源

public class ServletContextResource extends AbstractFileResolvingResource implements ContextResource {
    private final ServletContext servletContext;
    private final String path;

    public ServletContextResource(ServletContext servletContext, String path) {
        Assert.notNull(servletContext, "Cannot resolve ServletContextResource without ServletContext");
        this.servletContext = servletContext;
        //断言不为空
        Assert.notNull(path, "Path is required");
        String pathToUse = StringUtils.cleanPath(path);
        if (!pathToUse.startsWith("/")) {
            pathToUse = "/" + pathToUse;
        }

        this.path = pathToUse;
    }

    public final ServletContext getServletContext() {
        return this.servletContext;
    }

    public final String getPath() {
        return this.path;
    }

    public boolean exists() {
        try {
            URL url = this.servletContext.getResource(this.path);
            return url != null;
        } catch (MalformedURLException var2) {
            return false;
        }
    }

    public boolean isReadable() {
        InputStream is = this.servletContext.getResourceAsStream(this.path);
        if (is != null) {
            try {
                is.close();
            } catch (IOException var3) {
            }

            return true;
        } else {
            return false;
        }
    }

    public boolean isFile() {
        try {
            URL url = this.servletContext.getResource(this.path);
            if (url != null && ResourceUtils.isFileURL(url)) {
                return true;
            } else {
                return this.servletContext.getRealPath(this.path) != null;
            }
        } catch (MalformedURLException var2) {
            return false;
        }
    }

    public InputStream getInputStream() throws IOException {
        InputStream is = this.servletContext.getResourceAsStream(this.path);
        if (is == null) {
            throw new FileNotFoundException("Could not open " + this.getDescription());
        } else {
            return is;
        }
    }

    public URL getURL() throws IOException {
        URL url = this.servletContext.getResource(this.path);
        if (url == null) {
            throw new FileNotFoundException(this.getDescription() + " cannot be resolved to URL because it does not exist");
        } else {
            return url;
        }
    }

    public File getFile() throws IOException {
        URL url = this.servletContext.getResource(this.path);
        if (url != null && ResourceUtils.isFileURL(url)) {
            return super.getFile();
        } else {
            String realPath = WebUtils.getRealPath(this.servletContext, this.path);
            return new File(realPath);
        }
    }

    public Resource createRelative(String relativePath) {
        String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);
        return new ServletContextResource(this.servletContext, pathToUse);
    }

    @Nullable
    public String getFilename() {
        return StringUtils.getFilename(this.path);
    }

    public String getDescription() {
        return "ServletContext resource [" + this.path + "]";
    }

    public String getPathWithinContext() {
        return this.path;
    }

    public boolean equals(@Nullable Object other) {
        if (this == other) {
            return true;
        } else if (!(other instanceof ServletContextResource)) {
            return false;
        } else {
            ServletContextResource otherRes = (ServletContextResource)other;
            return this.servletContext.equals(otherRes.servletContext) && this.path.equals(otherRes.path);
        }
    }

    public int hashCode() {
        return this.path.hashCode();
    }
}

模板引擎

thymeleaf:推荐

官方在线文档:https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html

using:

使用thymeleaf开启命名空间,所有html标签固定th开头

<!DOCTYPE html>
<-->开启命名空间</-->
<html xmlns:th="http://www.thymeleaf.org">

  <head>
    <title>Good Thymes Virtual Grocery</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link rel="stylesheet" type="text/css" media="all" 
          href="../../css/gtvg.css" th:href="@{/css/gtvg.css}" />
  </head>

  <body>
    <p th:text="#{home.welcome}">Welcome to our grocery store!</p>

  </body>

</html>

freemarker

过滤器

可以设置多个过滤器之间优先级

//创建过滤器
public class MyFilter implements Filter {

        @Override
        public void destroy() {
        }

        @Override
        public void doFilter(ServletRequest srequest, ServletResponse sresponse, FilterChain
                filterChain)
                        throws IOException, ServletException {
            HttpServletRequest request = (HttpServletRequest) srequest;
            //放入basePath,供html页面调用
            request.setAttribute("basePath", BasePath.getBasePath(request));
            //打印请求Url
            /*System.out.println("ConfigurationFilter->url->" + request.getRequestURI());
            System.out.println("ConfigurationFilter->param↓");
            Set keSet=request.getParameterMap().entrySet();  
            for(Iterator itr=keSet.iterator();itr.hasNext();){  
                Map.Entry me=(Map.Entry)itr.next();  
                Object ok=me.getKey();  
                Object ov=me.getValue();  
                String[] value=new String[1];  
                if(ov instanceof String[]){  
                    value=(String[])ov;  
                }else{  
                    value[0]=ov.toString();  
                }  
                for(int k=0;k<value.length;k++){  
                    System.out.println(ok+"="+value[k]);  
                }  
              }  */
            filterChain.doFilter(srequest, sresponse);
        }

        @Override
        public void init(FilterConfig arg0) {
        }
    }

//过滤器配置文件
@Configuration
public class ConfigurationFilter{

    @Bean
    public RemoteIpFilter remoteIpFilter() {
        return new RemoteIpFilter();
    }

    @Bean
    public FilterRegistrationBean<MyFilter> testFilterRegistration() {
        FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<MyFilter>();
        registration.setFilter(new MyFilter());//添加过滤器
        registration.addUrlPatterns("/*");//设置过滤路径,/*所有路径
        registration.addInitParameter("admin", "moshow");//添加默认参数
        registration.setName("MyFilter");//设置优先级
        registration.setOrder(1);//设置优先级 值越小优先级越高
        return registration;
    }
    @Bean
    public FilterRegistrationBean<HttpPutFormContentFilter> testFilterRegistration2() {
        FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.addUrlPatterns("/demo");
        registrationBean.setFilter(new Filter() {
            @Override
            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,                         ServletException {
                chain.doFilter(request,response);
            }
        });
        FilterRegistrationBean<HttpPutFormContentFilter> registration = new FilterRegistrationBean<HttpPutFormContentFilter>();
        registration.addUrlPatterns("/*");//设置过滤路径,/*所有路径
        registration.setName("HttpPutFormContentFilter");//设置优先级
        registration.setOrder(2);//设置优先级
        return registration;
    }
}    

拦截器

 package com.lyp.interceptor.interceptor;

import com.lyp.interceptor.entity.User;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * 拦截带有用户信息的请求,如果请求携带登录信息跳转至登录页面
 *
 * lyp
 */
public class InterceptorConfiguration implements HandlerInterceptor {

    /**
     * 功能描述: 能直接拦截到请求 当返回为true会进行下一步操作,false结束
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        request.setCharacterEncoding("UTF-8");//设置字符集
        User user = (User) request.getSession().getAttribute("User");
        if (null != user) {
            return true;
        }
        response.sendRedirect(request.getContextPath()+"login.shtml");
        return false;
    }

    /**
     * 功能描述: 在拦截请求之后,在数据处理完成返回页面之前,这一步可以对返回的数据进行操作
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        User user = (User) request.getSession().getAttribute("User");
        Map<String, Object> model = modelAndView.getModel();
        model.put("test","测试");
        modelAndView.addAllObjects(model);
    }

    /**
     * 功能描述: 在整个请求完成之后执行,主要用于对使用资源的清理
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

AOP

自定义注解

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

/**
 * @author 拦截使用该注解的方法
 */
@Aspect
@Component
public class DemoAnnotationFilter {

    /** 配置指定注解成功执行完毕之后*/
    @AfterReturning(pointcut = "@annotation(demoAnnotation)",returning = "res")
    public void demoHandler(JoinPoint joinPoint, DemoAnnotation demoAnnotation,boolean res) {
        if (res) {
            Object[] args = joinPoint.getArgs();
            //注解获得的数据
            User user = (User) args[0];
            //注解获得的内容
            String value = demoAnnotation.defaultValue();
            if ("type".equals(value)) {
                //TODO 数据处理 
            }
        }
    }
}

切点

/**
 * @author lyp
 */
@Controller
public class DemoController {

    public ResponseEntity<String> demo() {
        return new ResponseEntity<>("", HttpStatus.ACCEPTED);
    }
}

/**
 * @author 日志操作
 */
@Aspect
@Component
public class DemoControllerAnnotation {

    /** 测试请求切入点*/
    @Pointcut(value = "execution(com.lyp.*.contorller.DemoController.demo())")
    private void demoController() {}

    /** 对当前方法进行操作*/
    @AfterReturning(value = "demoController()",returning = "res")
    private void afterExecution(JoinPoint joinPoint,boolean res) {

    }
}

kafka

配置

spring:
  # Kafka 配置项,对应 KafkaProperties 配置类
  kafka:
    bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔
    # Kafka Producer 配置项
    producer:
      acks: 1 # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。
      retries: 3 # 发送失败时,重试发送的次数
      key-serializer: org.apache.kafka.common.serialization.StringSerializer # 消息的 key 的序列化
      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化
    # Kafka Consumer 配置项
    consumer:
      auto-offset-reset: earliest # 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
      properties:
        spring:
          json:
            trusted:
              packages: cn.iocoder.springboot.lab03.kafkademo.message
    # Kafka Consumer Listener 监听器配置
    listener:
      missing-topics-fatal: false # 消费监听接口监听的主题不存在时,默认会报错。所以通过设置为 false ,解决报错

logging:
  level:
    org:
      springframework:
        kafka: ERROR # spring-kafka INFO 日志太多了,所以我们限制只打印 ERROR 级别
      apache:
        kafka: ERROR # kafka INFO 日志太多了,所以我们限制只打印 ERROR 级别

@Configuration
public class KafkaConfiguration {

    @Bean
    @Primary
    public ErrorHandler kafkaErrorHandler(KafkaTemplate<?, ?> template) {
        // 创建 DeadLetterPublishingRecoverer 对象
        ConsumerRecordRecoverer recoverer = new DeadLetterPublishingRecoverer(template);
        // 创建 FixedBackOff 对象
        BackOff backOff = new FixedBackOff(10 * 1000L, 3L);
        // 创建 SeekToCurrentErrorHandler 对象
        return new SeekToCurrentErrorHandler(recoverer, backOff);
    }
}

业务

public interface KafkaAdminClientService {

    Set<String> findTopics(String bootstrapServers);

    boolean createTopic(String bootstrapServers,String topicName,int partitions,short replication);

    List<String> deleteTopic(String bootstrapServers,Set<String> topicNames);
}

@Service
public class KafkaAdminClientServiceImpl implements KafkaAdminClientService {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 功能描述:
     * @param bootstrapServers
     * @return
     * @author SHINELON
     * @date 2022/1/12 14:42
     */
    private AdminClient createAdminClient(String bootstrapServers) {
        Properties properties = new Properties();
        properties.put(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
        return KafkaAdminClient.create(properties);
    }

    /**
     * 功能描述:
     * @param bootstrapServers kafka地址
     * @return Set<String> 可使用的主题名称
     * @author lyp
     * @date 2022/1/12 14:41
     */
    @Override
    public Set<String> findTopics(String bootstrapServers) {
        Set<String> nameSet = new HashSet<>();
        try {
            AdminClient adminClient = createAdminClient(bootstrapServers);
            ListTopicsResult listTopicsResult = adminClient.listTopics();
            KafkaFuture<Set<String>> names = listTopicsResult.names();
            nameSet = names.get();
            adminClient.close();
        } catch (Exception e) {
            logger.error("查询异常",e);
        }
        return nameSet;
    }

    /**
     * 功能描述:
     * @param bootstrapServers kafka地址
     * @param topicName 名称
     * @param partitions 分区数
     * @param replication 复制因子
     * @return boolean 是否成功
     * @author lyp
     * @date 2022/1/12 14:35
     */
    @Override
    public boolean createTopic(String bootstrapServers, String topicName, int partitions, short replication) {
        boolean res = false;
        try {
            Properties properties = new Properties();
            properties.put(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
            properties.put("sasl.jaas.config",
                    "org.apache.kafka.common.security.plain.PlainLoginModule required username=\"admin\" password=\"admin\";");
            AdminClient adminClient = KafkaAdminClient.create(properties);
            NewTopic newTopic = new NewTopic(topicName, partitions, replication);
            adminClient.createTopics(Arrays.asList(newTopic));
            adminClient.close();
            logger.info("创建Topic:"+topicName+"成功!");
            res = true;
        } catch (Exception e) {
            logger.error("创建异常!",e);
        }
        return res;
    }

    /**
     * 功能描述:
     * @param bootstrapServers kafka地址
     * @param topicNames 需要删除topic名称
     * @return List<String> 删除失败topic名称
     * @author lyp
     * @date 2022/1/12 14:33
     */
    @Override
    public List<String> deleteTopic(String bootstrapServers, Set<String> topicNames) {
        List<String> losers = new ArrayList<>();
        AdminClient adminClient = createAdminClient(bootstrapServers);
        DeleteTopicsResult result = adminClient.deleteTopics(topicNames);
        for (Map.Entry<String, KafkaFuture<Void>> futureEntry : result.values().entrySet()) {
            try {
                String topic = futureEntry.getKey();
                KafkaFuture<Void> future = futureEntry.getValue();
                future.get();
                if (future.isCompletedExceptionally()) {
                    losers.add(topic);
                }
                adminClient.close();
            } catch (InterruptedException | ExecutionException e) {
                logger.error("主题删除异常",e);
            }
        }
        return losers;
    }
}

flowable

配置

  • 从官网下载flowable-6.6.0 : https://github.com/flowable/flowable-engine/releases/download/flowable-6.6.0/flowable-6.6.0.zip
  • 将压缩包中的 flowable-6.6.0 warsflowable-ui.war 丢到Tomcat中跑起来
  • 打开http://localhost:8080/flowable-ui 用账户:admin/test 登录
  • 进入APP.MODELER创建流程,之后可以导出流程到项目中使用,或者配置apache-tomcat-9.0.37/webapps/flowable-ui/WEB-INF/classes/flowable-default.properties连接本地数据库
  • 注意:需要将java驱动jar(mysql-connector-java-5.1.45.jar)复制到 `apache-tomcat-9.0.37webapps/flowable-rest/WEB-INF/lib
  • mysql创建flowable库并导入sql文件创建数据表

项目依赖pom.xml

<dependency>
    <groupId>org.flowable</groupId>
    <artifactId>flowable-spring-boot-starter</artifactId>
    <version>6.6.0</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.45</version>
</dependency>

项目配置application.yml

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/flowable?useSSL=false&characterEncoding=UTF-8&serverTimezone=GMT%2B8
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: 123456
复制代码

业务

service作用
RepositoryService管理与控制部署(deployments)与流程定义(process definitions)的操作,管理静态信息
RuntimeService启动流程定义的新流程实例
IdentityService用于管理(创建,更新,删除,查询……)组与用户
FormService可选服务,没有也能很好地运行,而不牺牲任何功能
HistoryService暴露Flowable引擎收集的所有历史数据,要提供查询历史数据的能力
ManagementService读取数据库表与表原始数据的信息,也提供了对作业(job)的查询与管理操作
DynamicBpmnService修改流程定义中的部分内容,而不需要重新部署它
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值