ソースを参照

+ SocketServer注解自动注册

chen.cheng 6 ヶ月 前
コミット
c98d7535e7

+ 2 - 0
bd-park/park-backend/park-application/src/main/java/com/huashe/park/application/BDApplication.java

@@ -1,6 +1,7 @@
 package com.huashe.park.application;
 
 import com.dtflys.forest.springboot.annotation.ForestScan;
+import com.huashe.park.application.socket.cfg.EnableSocketServer;
 import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -16,6 +17,7 @@ import org.springframework.context.annotation.ComponentScan;
 @ComponentScan(basePackages = {"com.ruoyi", "com.huashe.park"})
 @MapperScan("com.huashe.park.core.mapper")
 @ForestScan(basePackages = {"com.huashe.park.infrastructure.uwb"})
+@EnableSocketServer(basePackages = {"com.huashe.park.application.socket.server"})
 public class BDApplication {
     public static void main(String[] args) {
         // System.setProperty("spring.devtools.restart.enabled", "false");

+ 33 - 0
bd-park/park-backend/park-application/src/main/java/com/huashe/park/application/socket/cfg/EnableSocketServer.java

@@ -0,0 +1,33 @@
+package com.huashe.park.application.socket.cfg;
+
+import org.springframework.context.annotation.Import;
+
+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;
+
+/**
+ * The interface Enable socket server.
+ * 自动注册SocketServer
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Documented
+@Import(value = SocketServerRegistrar.class)
+public @interface EnableSocketServer {
+    /**
+     * Base packages string [ ].
+     *
+     * @return the string [ ]
+     */
+    String[] basePackages() default {};
+
+    /**
+     * Base package classes class [ ].
+     *
+     * @return the class [ ]
+     */
+    Class<?>[] basePackageClasses() default {};
+}

+ 43 - 0
bd-park/park-backend/park-application/src/main/java/com/huashe/park/application/socket/cfg/SocketServerFactoryBean.java

@@ -0,0 +1,43 @@
+package com.huashe.park.application.socket.cfg;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.aop.framework.ProxyFactory;
+import org.springframework.beans.factory.FactoryBean;
+
+public class SocketServerFactoryBean<T> implements FactoryBean<T> {
+    private static final Logger log = LoggerFactory.getLogger(SocketServerFactoryBean.class);
+
+    private Class socketServerClz;
+
+    public SocketServerFactoryBean() {
+    }
+
+    public SocketServerFactoryBean(Class socketServerClz) {
+        this.socketServerClz = socketServerClz;
+    }
+
+    public Class getLogClz() {
+        return socketServerClz;
+    }
+
+    public void setLogClz(Class socketServerClz) {
+        this.socketServerClz = socketServerClz;
+    }
+
+    @Override
+    public T getObject() throws Exception {
+        ProxyFactory proxyFactory = new ProxyFactory();
+        try {
+            proxyFactory.setTarget(socketServerClz.newInstance());
+        } catch (InstantiationException | IllegalAccessException e) {
+            log.error(e.getMessage(), e);
+        }
+        return (T) proxyFactory.getProxy();
+    }
+
+    @Override
+    public Class<?> getObjectType() {
+        return socketServerClz;
+    }
+}

+ 160 - 0
bd-park/park-backend/park-application/src/main/java/com/huashe/park/application/socket/cfg/SocketServerRegistrar.java

@@ -0,0 +1,160 @@
+package com.huashe.park.application.socket.cfg;
+
+import com.huashe.park.common.websocket.SocketEndPoint;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.BeanClassLoaderAware;
+import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.support.GenericBeanDefinition;
+import org.springframework.context.EnvironmentAware;
+import org.springframework.context.ResourceLoaderAware;
+import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
+import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
+import org.springframework.core.env.Environment;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.core.type.AnnotationMetadata;
+import org.springframework.core.type.filter.AnnotationTypeFilter;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.StringUtils;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class SocketServerRegistrar implements ImportBeanDefinitionRegistrar,
+        ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
+    private static final Logger log = LoggerFactory.getLogger(SocketServerRegistrar.class);
+
+    private ResourceLoader resourceLoader;
+
+    private ClassLoader classLoader;
+
+    private Environment environment;
+
+    public SocketServerRegistrar() {
+    }
+
+    @Override
+    public void setResourceLoader(ResourceLoader resourceLoader) {
+        this.resourceLoader = resourceLoader;
+    }
+
+    @Override
+    public void setBeanClassLoader(ClassLoader classLoader) {
+        this.classLoader = classLoader;
+    }
+
+    @Override
+    public void setEnvironment(Environment environment) {
+        this.environment = environment;
+    }
+
+
+    @Override
+    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
+
+        Set<String> basePackages = getBasePackages(importingClassMetadata);
+        ClassPathScanningCandidateComponentProvider scanner = getScanner();
+        scanner.setResourceLoader(this.resourceLoader);
+        scanner.addIncludeFilter(new AnnotationTypeFilter(SocketEndPoint.class));
+
+
+        for (String basePackage : basePackages) {
+            Set<BeanDefinition> candidateComponents = scanner
+                    .findCandidateComponents(basePackage);
+            for (BeanDefinition candidateComponent : candidateComponents) {
+                if (candidateComponent instanceof AnnotatedBeanDefinition) {
+                    AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
+                    AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
+                    registerSocketServer(registry, annotationMetadata);
+                }
+            }
+        }
+
+    }
+
+
+    protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
+        Map<String, Object> attributes = importingClassMetadata
+                .getAnnotationAttributes(EnableSocketServer.class.getCanonicalName());
+
+        Set<String> basePackages = new HashSet<>();
+
+        for (String pkg : (String[]) attributes.get("basePackages")) {
+            if (StringUtils.hasText(pkg)) {
+                basePackages.add(pkg);
+            }
+        }
+        for (Class<?> clazz : (Class[]) attributes.get("basePackageClasses")) {
+            basePackages.add(ClassUtils.getPackageName(clazz));
+        }
+
+        if (basePackages.isEmpty()) {
+            basePackages.add(
+                    ClassUtils.getPackageName(importingClassMetadata.getClassName()));
+        }
+        return basePackages;
+    }
+
+
+    protected ClassPathScanningCandidateComponentProvider getScanner() {
+        return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
+            @Override
+            protected boolean isCandidateComponent(
+                    AnnotatedBeanDefinition beanDefinition) {
+                if (!beanDefinition.getMetadata().isIndependent()) {
+                    return false;
+                }
+                if (beanDefinition.getMetadata().isInterface()
+                        && beanDefinition.getMetadata()
+                        .getInterfaceNames().length == 1
+                        && Annotation.class.getName().equals(beanDefinition
+                        .getMetadata().getInterfaceNames()[0])) {
+                    try {
+                        Class<?> target = ClassUtils.forName(
+                                beanDefinition.getMetadata().getClassName(),
+                                SocketServerRegistrar.this.classLoader);
+                        return !target.isAnnotation();
+                    } catch (Exception ex) {
+                        log.error("Could not load target class: {}", beanDefinition.getMetadata().getClassName(), ex);
+                    }
+                }
+                return true;
+            }
+        };
+    }
+
+
+    private void registerSocketServer(BeanDefinitionRegistry registry,
+                                      AnnotationMetadata annotationMetadata) {
+        try {
+            String className = annotationMetadata.getClassName();
+            Class<?> beanClazz = Class.forName(className);
+            if (!beanClazz.isAnnotationPresent(SocketEndPoint.class)) {
+                return;
+            }
+            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(beanClazz);
+            GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
+            definition.getPropertyValues().add("logClz", beanClazz);
+            definition.setBeanClass(SocketServerFactoryBean.class);
+            definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
+            String beanId = StringUtils.uncapitalize(beanClazz.getSimpleName());
+            registry.registerBeanDefinition(beanId, definition);
+        } catch (ClassNotFoundException e) {
+            log.error("Could not register target class: {}", annotationMetadata.getClassName(), e);
+        }
+    }
+
+    private void registryFieldBean(BeanDefinitionRegistry registry, BeanDefinitionBuilder builder, Field field) {
+        BeanDefinitionBuilder fieldBuilder = BeanDefinitionBuilder.genericBeanDefinition(field.getClass());
+        BeanDefinition beanDefinition = builder.getBeanDefinition();
+        registry.registerBeanDefinition(field.getName(), beanDefinition);
+    }
+
+
+}

+ 13 - 0
bd-park/park-backend/park-application/src/main/java/com/huashe/park/application/socket/server/TestSocket.java

@@ -0,0 +1,13 @@
+package com.huashe.park.application.socket.server;
+
+import com.huashe.park.common.websocket.SocketEndPoint;
+import com.huashe.park.common.websocket.SocketHandle;
+import org.springframework.web.socket.WebSocketSession;
+
+@SocketEndPoint(url = "/pkb/test")
+public class TestSocket extends SocketHandle {
+    @Override
+    public String custSessionKey(WebSocketSession session) {
+        return "test";
+    }
+}

+ 14 - 0
bd-park/park-backend/park-application/src/main/java/com/huashe/park/application/socket/server/TestSocket1.java

@@ -0,0 +1,14 @@
+package com.huashe.park.application.socket.server;
+
+import com.huashe.park.common.websocket.SocketEndPoint;
+import com.huashe.park.common.websocket.SocketHandle;
+import org.springframework.stereotype.Service;
+import org.springframework.web.socket.WebSocketSession;
+
+@SocketEndPoint(url = "/pkd/test1")
+public class TestSocket1 extends SocketHandle {
+    @Override
+    public String custSessionKey(WebSocketSession session) {
+        return session.getId();
+    }
+}

+ 19 - 2
bd-park/park-backend/park-application/src/main/java/com/huashe/park/application/web/controller/bd/BdFenceInfoController.java

@@ -1,17 +1,28 @@
 package com.huashe.park.application.web.controller.bd;
 
+import com.alibaba.fastjson2.JSON;
+import com.huashe.common.domain.AjaxResult;
+import com.huashe.park.application.socket.server.TestSocket;
 import com.huashe.park.core.service.IBdFenceInfoService;
 import com.huashe.park.domain.entity.BdFenceInfo;
 import com.ruoyi.common.annotation.Anonymous;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.core.controller.BaseController;
-import com.huashe.common.domain.AjaxResult;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.BusinessType;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
 
+import javax.annotation.Resource;
 import javax.servlet.http.HttpServletResponse;
 import java.util.List;
 
@@ -29,11 +40,17 @@ public class BdFenceInfoController extends BaseController {
     @Autowired
     private IBdFenceInfoService bdFenceInfoService;
 
+    @Resource
+    private TestSocket testSocket;
+
     /**
      * 查询围栏基础信息列表
      */
     @GetMapping("/list")
     public TableDataInfo list(BdFenceInfo bdFenceInfo) {
+        testSocket.sendMessage("test", JSON.parseObject(JSON.toJSONString(new BdFenceInfo() {{
+            setAltitude(22);
+        }})));
         startPage();
         List<BdFenceInfo> list = bdFenceInfoService.selectBdFenceInfoList(bdFenceInfo);
         return getDataTable(list);

+ 6 - 0
bd-park/park-backend/park-application/src/main/resources/application.yml

@@ -48,6 +48,9 @@ user:
 
 # Spring配置
 spring:
+  mvc:
+    pathmatch:
+      matching-strategy: ant_path_matcher
   # 资源信息
   messages:
     # 国际化资源文件路径
@@ -97,6 +100,9 @@ token:
   # 令牌有效期(默认30分钟)
   expireTime: 76000
 
+huashe:
+  permit:
+    urlPatterns: /pkb/*,/pkbs/*
 # MyBatis配置
 mybatis:
   # 搜索指定包别名

+ 4 - 0
bd-park/park-backend/park-common/pom.xml

@@ -132,6 +132,10 @@
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-math3</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-websocket</artifactId>
+        </dependency>
     </dependencies>
 
 </project>

+ 142 - 0
bd-park/park-backend/park-common/src/main/java/com/huashe/park/common/SpringBeanUtils.java

@@ -0,0 +1,142 @@
+package com.huashe.park.common;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.springframework.aop.framework.AopContext;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+@Component
+public class SpringBeanUtils implements BeanFactoryPostProcessor, ApplicationContextAware {
+    /**
+     * Spring应用上下文环境
+     */
+    private static ConfigurableListableBeanFactory beanFactory;
+
+    private static ApplicationContext applicationContext;
+
+    @Override
+    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+        SpringBeanUtils.beanFactory = beanFactory;
+    }
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        SpringBeanUtils.applicationContext = applicationContext;
+    }
+
+    /**
+     * 获取对象
+     *
+     * @param name
+     * @return Object 一个以所给名字注册的bean的实例
+     * @throws org.springframework.beans.BeansException
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T getBean(String name) throws BeansException {
+        return (T) beanFactory.getBean(name);
+    }
+
+    /**
+     * 获取类型为requiredType的对象
+     *
+     * @param clz
+     * @return
+     * @throws org.springframework.beans.BeansException
+     */
+    public static <T> T getBean(Class<T> clz) throws BeansException {
+        T result = (T) beanFactory.getBean(clz);
+        return result;
+    }
+
+    public static <T> Map<String, T> getBeansOfType(Class<T> clz) throws BeansException {
+        return beanFactory.getBeansOfType(clz);
+    }
+
+    /**
+     * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
+     *
+     * @param name
+     * @return boolean
+     */
+    public static boolean containsBean(String name) {
+        return beanFactory.containsBean(name);
+    }
+
+    /**
+     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
+     *
+     * @param name
+     * @return boolean
+     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
+     */
+    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
+        return beanFactory.isSingleton(name);
+    }
+
+    /**
+     * @param name
+     * @return Class 注册对象的类型
+     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
+     */
+    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
+        return beanFactory.getType(name);
+    }
+
+    /**
+     * 如果给定的bean名字在bean定义中有别名,则返回这些别名
+     *
+     * @param name
+     * @return
+     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
+     */
+    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
+        return beanFactory.getAliases(name);
+    }
+
+    /**
+     * 获取aop代理对象
+     *
+     * @param invoker
+     * @return
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T getAopProxy(T invoker) {
+        return (T) AopContext.currentProxy();
+    }
+
+    /**
+     * 获取当前的环境配置,无配置返回null
+     *
+     * @return 当前的环境配置
+     */
+    public static String[] getActiveProfiles() {
+        return applicationContext.getEnvironment().getActiveProfiles();
+    }
+
+    /**
+     * 获取当前的环境配置,当有多个环境配置时,只获取第一个
+     *
+     * @return 当前的环境配置
+     */
+    public static String getActiveProfile() {
+        final String[] activeProfiles = getActiveProfiles();
+        return ArrayUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null;
+    }
+
+    /**
+     * 获取配置文件中的值
+     *
+     * @param key 配置文件的key
+     * @return 当前的配置文件的值
+     */
+    public static String getRequiredProperty(String key) {
+        return applicationContext.getEnvironment().getRequiredProperty(key);
+    }
+}

+ 10 - 0
bd-park/park-backend/park-common/src/main/java/com/huashe/park/common/websocket/ISocketHandle.java

@@ -0,0 +1,10 @@
+package com.huashe.park.common.websocket;
+
+import com.alibaba.fastjson2.JSONObject;
+import org.springframework.web.socket.WebSocketSession;
+
+public interface ISocketHandle {
+    String custSessionKey(WebSocketSession session);
+
+    void sendMessage(String sessionKey, JSONObject message);
+}

+ 14 - 0
bd-park/park-backend/park-common/src/main/java/com/huashe/park/common/websocket/SocketEndPoint.java

@@ -0,0 +1,14 @@
+package com.huashe.park.common.websocket;
+
+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;
+
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface SocketEndPoint {
+    public String url();
+}