HServer文档

HServer入门

注解认识

以下注解基本模拟Spring的功能

  • @Bean
  • 将Bean对象加入IOC容器中比如

    //按默名字加入IOC容器
    @Bean
    class TestService{}
    //指定名字加入容器,装配的时候就只能通过名字装配了
    @Bean("testService")
    class Test{}
                
  • @Autowired
  • 自动装配注解

    //按类型注入
    @Autowired
    private TestService testService;
    //按Bean名字注入
    @Autowired("testServer1")
    private TestService testService;
                
  • @Controller
  • 控制器注解,将控制器加入IOC容器中,类似Spring mvc
  • 注解在类上面直接加上即可比如

    //Index控制器
    @Controller
    class IndexController{}
                
  • @GET
  • @POST
  • 方法注解,在@Controller注解类类中使用,标注一个方法为GET或者POST方法,例如

    @GET("/index")
    public void index(){}
    @POST("/index")
    public void index(){}
                
  • @Filter
  • 拦截器注解,标注一个类为拦截器,和JavaEE的Filter类似

    @Filter(1)//1表示拦截优先级,越小越优先
    public class MyFilter1 implements FilterAdapter {}
    //需要实现FilterAdapter接口
                
  • @Hook
  • hook注解就是Aop

    @Hook(value = Test.class, method = "show")
    public class HookTest implements HookAdapter {}
    //value表示aop的类,method要hook的方法,必须实现HookAdapter
                
  • @Task
  • 定时任务

    @Task(name = "测试定时任务", time ="*/5 * * * * ?")
    //标记在方法上,同时该类需要被@Bean 标记
    @Task(name = "测试定时任务1", time = "2000")
    public void timerTask() {}
                
  • @WebSocket
  • 实现websocket通信

    @WebSocket("/ws")
    public class WebSocketTest implements WebSocketHandler {}
    //这样就可以完成基本的通信了
                
  • @Configuration
  • 自定配置注解,需要配合@Bean注解一起使用,最后会把方法里面的返回的对象 存储到IOC容器中,同时可以通过Autowired注解注入

    @Configuration
    public class DataConfig {

        //自定义名字(用例:比如多数据源注入)
        @Bean("createUser")
        public User createUser(){
            User user = new User();
            user.setAge(999);
            user.setName("我是配置类自定义名字的数据");
            user.setSex("未知");
            return user;
        }

       //按类型存储
        @Bean
        public User createUser1(){
            User user = new User();
            user.setAge(999);
            user.setName("我是配置类的默认数据");
            user.setSex("未知");
            return user;
        }

    }
                
  • @RpcService
  • 标注一个Bean对象是一个rpc服务,同时也可以给定一个名字,@RpcService("rpc")。

    @Bean
    @RpcService
    public class RpcServiceImp implements RpcService{
        public String test(String name){
            return name+"我是RPC";
        }
    }

    @Bean
    @RpcService("rpc")
    public class RpcServiceImp implements RpcService{
        public String test(String name){
            return name+"我是RPC";
        }
    }
                
  • @Resource
  • 注入一个Rpc服务,同时可以通过名字注入,@Resource("rpc")。详情,请看文档介绍

    @Resource
    private RpcService rpcService;

    @Resource("rpc")
    private RpcService rpcService;
                
  • @Sign
  • @RequiresRoles
  • @RequiresPermissions
  • 该注解用于标注控制器里面的方法,方便自己实现sign签名算法,角色检查,权限检查,实现token等,详情下面的对应接口.

    @Sign("MD5")
    @RequiresRoles("角色")
    @RequiresPermissions(value = {"/权限1","/权限2"}, logical=Logical.OR)

                

Hello World项目

第一步pom依赖引入


    <dependency>
        <groupId>top.hserver </groupId>
        <artifactId>HServer </artifactId>
        <version>最新版(源码地址上可查) </version>
    </dependency>
                

第二步建立一个主函数


    public class WebApp {
    public static void main(String[] args) {
            //运行官方例子,直接运行既可以了,默认自带了一些例子。
            //这样运行,直接启动hserver内部测试的程序,地址栏直接运行即可
            HServerApplication.run(TestWebApp.class, 8888);
        }
    }

             

第三步,如果不是启动Demo例子那么就这样建立一个文件


    public class WebApp {
        public static void main(String[] args) {
            HServerApplication.run(WebApp.class, 8888);
        }
    }
           

第四步同主函数建立一个包文件夹比如controller


    @Controller
    public class Hello {

        @GET("/hello")
        public Map index(HttpRequest request, String name) {
            Map res = new HashMap<>();
            res.put("code", 200);
            res.put("res", request.getRequestParams());
            res.put("msg", "Hello");
            return res;
        }

        /**
         * 模板测试
         * @param httpResponse
         */
        @GET("/template")
        public void template(HttpResponse httpResponse) {
            User user = new User();
            user.setAge(20);
            user.setName("xx");
            user.setSex("男");
            Map obj=new HashMap<>();
            obj.put("user",user);
    //        httpResponse.sendTemplate("/admin/user/list.ftl", obj);
            httpResponse.sendTemplate("a.ftl", obj);
        }
    }
                

就这样你就完成了一个简单得get请求定义,直接运行即可。更多例子,可以参考包top.test下面的例子

文件上传下载操作

    #File类型得
    @GET("/downFile")
    public void downFile(HttpRequest request, HttpResponse response) {
      response.setDownloadFile(new File("D:\\Java\\HServer\\README.md"));
    }
    #InputStream 类型得
    @GET("/downInputStream")
    public void downInputStream(HttpRequest request, HttpResponse response) throws Exception {
      File file = new File("D:\\Java\\HServer\\README.md");
      InputStream fileInputStream = new FileInputStream(file);
      response.setDownloadFile(fileInputStream,"README.md");
    }
                

Aop操作


    #必须实现HookAdapter的接口
    #同时被@Hook注解标注
    @Slf4j
    @Hook(value = Test.class, method = "show")
    public class HookTest implements HookAdapter {

        @Override
        public void before(Object[] objects) {
            log.info("aop.-前置拦截:" + objects[0]);
            objects[0]="666";
        }

        @Override
        public Object after(Object object) {
            return object + "aop-后置拦截";
        }
    }
                

Filter拦截器


    //必须实现FilterAdapter接口,同时被@Filter标注,数字越小,优先级越高,切不要重复
    @Slf4j
    @Filter(1)
    public class MyFilter2 implements FilterAdapter {
        @Override
        public void doFilter(FilterChain chain, Webkit webkit) {
            log.info("MyFilter->1");
            chain.doFilter(webkit);
        }
    }

                

定时任务


    #需要被@Bean注解标注,可以通过TaskManager类进行定时任务的控制,动态添加和删除
    #2.8版本之后,调整@Task注解的time类型为字符串类型,同时支持毫秒和cron表达式,time会自动转化对应的格式
    @Bean
    public class TaskTest {

        @Autowired
        private TestService testService;

        private boolean flag = true;

        public void dynamicAddTimer() {
            System.out.println("动态添加定时任务");
            TaskManager.addTask("测试任务2", "2000", TestTask.class,"666");
        }


        @Task(name = "测试定时任务1", time ="*/5 * * * * ?")
        public void timerTask() {
            System.out.println("测试定时任务,注入的对象调用结果:" + testService.testa());
            if (flag) {
                dynamicAddTimer();
                flag = false;
            }
        }

        @Task(name = "测试定时任务2", time = "2000")
        public void removeTask() {
            //干掉方法注解版本
            boolean task1 = TaskManager.removeTask("测试定时任务1");
            //干掉动态添加的
            boolean task2 = TaskManager.removeTask("测试任务2");
            //干掉自己
            boolean task3 = TaskManager.removeTask("测试定时任务2");
            //结果
            System.out.println("任务已经被干掉了 tash1=" + task1 + ",task2=" + task2 + ",task3=" + task3);
        }

    }

    //动态添加定时任务的实现类必须要实现一个TaskJob,样才能被TaskManager管理
    //添加任务 TaskManager.addTask("测试任务2", "2000", TestTask.class,"666");
    //删除任务  boolean is_success = TaskManager.removeTask("测试任务2");
    public class TestTask implements TaskJob {

        @Override
        public void exec(Object... args) {
            String args_ = "";
            for (Object arg : args) {
                args_ += arg.toString();
            }
            System.out.println("测试定时器动态添加任务,参数是:" + args_);
        }
    }

                

WebSocket操作


    #需要被@WebSocket标注同时给一个连接地址,最后实现WebSocketHandler接口,
    #Ws类定义了简单的发送方法,如果有其他的业务操作,可以获取ChannelHandlerContext,进行操作

    @WebSocket("/ws")
    public class WebSocketTest implements WebSocketHandler {

        @Autowired
        private TestService testService;

        @Override
        public void onConnect(Ws ws) {
            System.out.println("连接成功,分配的UID:" + ws.getUid());
        }

        @Override
        public void onMessage(Ws ws) {
            ws.send("666" + testService.testa() + ws.getUid());
            System.out.println("收到的消息,"+ws.getMessage()+",UID:" + ws.getUid());
        }

        @Override
        public void disConnect(Ws ws) {
            System.out.println("断开连接,UID:" + ws.getUid());
        }
    }

                

监控

    application.properties文件配置
        #开启访问统计
        statistics=true
        #统计规则:以逗号分割的正则表达式
        statisticalRules=/hel.*,/admin/.*
    #StatisticsHandler操作
        #获取所有的IP地址
        StatisticsHandler.getIpMap()
        #最近50个请求队列(调用的URI,(发送大小,接收大小)宽带监视,耗时时间)
        StatisticsHandler.getLogRequestQue()
        #唯一IP请求的数量 uv
        StatisticsHandler.getUniqueIpCount()
        #请求总数   pv
        StatisticsHandler.getCount()
        #uri记录 被访问的记录()
        StatisticsHandler.getUriData()
    #提示:
    1,如果自己要做统计,完全可以自定义一个定时器,动态保存数据哦
    2,StatisticsHandler,提供了一个remove方法,remove,用来清除,或者保存数据用,它会返回一个最新的数据同时清除自己
    3,如果开启统计,请务必,执行Remove方法,不然,内存可能就会蹦
                

全局异常处理


    //类必须要被@Bean注解,同时实现GlobalException接口,
    //一个项目中最多只有一个GlobalException实现哦,可以没有.没有异常处理,同时又报错了,那么直接显示错误
    @Bean
    public class WebException implements GlobalException {

        @Override
        public void handler(Exception exception, Webkit webkit) {
            exception.printStackTrace();
            System.out.println(webkit.httpRequest.getUri() + "--->" + exception.getMessage());
            webkit.httpResponse.sendHtml("全局异常处理");
        }
    }
                

服务器启动完成的回调


    //类必须要被@Bean注解,同时实现InitRunner接口,
    @Bean
    public class RunInit implements InitRunner {

     @Autowired
     private User user;

     @Override
     public void init() {
         System.out.println("初始化方法:注入的User对象的名字是-->"+user.getName());
        }
    }
                

技巧与配置说明

maven打包Jar


    <build>
            #jar名字
            <finalName>HServer</finalName>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.1</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>

                <plugin>
                    <artifactId>maven-assembly-plugin</artifactId>
                    <configuration>
                        <finalName>${project.build.finalName}</finalName>
                        <appendAssemblyId>false</appendAssemblyId>
                        <archive>
                            <manifest>
                                #主函数的包路径
                                <mainClass>top.test.WebApp</mainClass>
                            </manifest>
                        </archive>
                        <descriptorRefs>
                            <descriptorRef>jar-with-dependencies</descriptorRef>
                        </descriptorRefs>
                    </configuration>
                    <executions>
                        <execution>
                            <id>make-assembly</id>
                            <phase>package</phase>
                            <goals>
                                <goal>single</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
    </build>
                    

静态资源路径

  • resources/static

模板路径

  • resources/template

集群环境搭建

  • 依赖版本要大于等于2.5版本
  • 要有主节点
  • 要有从节点
  • 主从节点共用接口层
  • 源码例子点我

举栗子

1.主节点

        #-----------------集群-------------
        #是否开启集群模式
        app.cloud.open=true
        #集群名字
        app.cloud.name=ServerA
        #是否是主机
        app.cloud.master.open=true

        @Resource
        private RpcService rpcService;
        @Resource("rpc")
        private RpcService rpcService;

                

2.从节点

        #-----------------集群-------------
        #是否开启集群模式
        app.cloud.open=true
        #集群名字
        app.cloud.name=ServerB
        #是否是从机
        app.cloud.slave.open=true
        #从机连接主机的IP地址
        app.cloud.slave.master.host=127.0.0.1

        @Bean
        @RpcService
        public class RpcServiceImp implements RpcService{
            public String test(String name){
                return name+"我是RPC";
            }
        }

        @Bean
        @RpcService("rpc")
        public class RpcServiceImp implements RpcService{
            public String test(String name){
                return name+"我是RPC";
            }
        }
                

可以多从机注册到同一个主机上,调用随机调用。启动主机,在启动从机,从机自动连接主机断开也会自动重新连接

鉴权认证相关操作

  • 要使用相关注解进行标注。
  • 要实现PermissionAdapter接口
  • 实现的子类要用@Bean注解标注
  • 然后你就可以自由控制了

        //请使用相关注解对控制器的方法做标记,这样在执行到被注解标记的方法就会执行下面的相关方法
        //  List routerPermissions = PermissionAdapter.getRouterPermissions();
        // 通过上面的代码可以获取到所有标记的注解,他可以干嘛?
        // 同步后台数据库里面的权限,后台管理面里面可以动态给角色分配权限。
        // 自己做一个下拉选择列表,创建角色分配权限时,多选即可。

        /**
         * 验证逻辑请自己实现哦
         */
        @Bean
        public class TestPermission implements PermissionAdapter {

            @Override
            public void requiresPermissions(RequiresPermissions requiresPermissions, Webkit webkit) {
                //这里你可以实现一套自己的权限检查算法逻辑,判断,
                //如果满足权限,不用其他操作,如果不满足权限,那么你可以通过,Webkit里面的方法直接输出相关内容
                //或者自定义一个异常类,在全局异常类做相关操作
                System.out.println(requiresPermissions.value()[0]);
            }

            @Override
            public void requiresRoles(RequiresRoles requiresRoles, Webkit webkit) {
                //这里你可以实现一套自己的角色检查算法逻辑,判断,
                //其他逻辑同上
                System.out.println(requiresRoles.value()[0]);
            }

            @Override
            public void sign(Sign sign, Webkit webkit) {
               //这里你可以实现一套自己的接口签名算法检查算法逻辑,判断,
               //其他逻辑同上
               Map requestParams = webkit.httpRequest.getRequestParams();
               String sign1 = webkit.httpRequest.getHeader("sign");
               System.out.println(sign.value());
            }
        }