美文网首页Java面试面试题集
【面试题集】Java如何实现多重继承及Spring内部类继承实例

【面试题集】Java如何实现多重继承及Spring内部类继承实例

作者: 逍遥天扬 | 来源:发表于2019-10-11 10:52 被阅读0次

    概念

    品茗IT-面试题集-首发

    如果大家正在寻找一个java的学习环境,或者在开发中遇到困难,可以加入我们的java学习圈,点击即可加入,共同学习,节约学习时间,减少很多在学习中遇到的难题。

    多重继承指的是一个类可以同时从多于一个的父类那里继承行为和特征,C++是允许多继承的,可以加上作用域来访问相应的父类变量和函数;然而我们知道Java为了保证数据安全,它只允许单继承。

    一般情况下,我们是不需要使用多重继承的,如果必须使用,就要先考虑下你的代码设计适合合理;但是也不排除它的使用场景,Spring的代码中就有很多多重继承的使用场景。

    这里讲述下Java提供了两种实现多重继承的方式:接口和内部类。同时,最后讲述下Spring中内部类继承的实现及如何从Spring当前的HttpServletRequest获取tomcat真实的Request。

    接口实现

    子类只能继承一个父类,但是却可以实现多个接口,事实上并不算多继承:

    下面是使用接口实现多继承实例,假设有两个client:httpclient和tcpclient,想实现一个通用得客户端,根据类型选择http还是tcp:

    public interface HttpClient{
        void doHttp();
    }
    public interface TcpClient{
        void doTcp();
    }
    public class BaseHttpClient implements HttpClient {
        @Override
        public void doHttp() {
            System.out.println("我是 http");
        }
    }
    public interface NetType {
        public static final String HTTP = "HTTP";
        public static final String TCP = "TCP";
        
        void choseType(String type);
    }
    public class NetworkClientTask extends BaseHttpClient implements TcpClient, NetType {
        private String curNetType;
    
        public void doTask() {
            if (HTTP.equalsIgnoreCase(curNetType)) {
                super.doHttp();
            } else {
                this.doTcp();
            }
        }
    
        @Override
        public void doTcp() {
            System.out.println("我得自己实现tcp");
        }
    
        @Override
        public void choseType(String type) {
            this.curNetType = type;
        }
    }
    

    上面的代码里,NetworkClientTask 继承了BaseHttpClient ,可以直接使用BaseHttpClient 的方法,但是要自己实现TcpClient的方法。

    内部类实现

    因为上面的接口方式需要自己实现TcpClient的方法,很不方便,我们可以在上面的基础上加点东西:

    public class BaseTcpClient implements TcpClient {
        @Override
        public void doTcp() {
            System.out.println("我是 tcp");
        }
    }
    public class NetworkClientTask extends BaseHttpClient implements NetType {
        private String curNetType;
    
        public void doTask() {
            if (HTTP.equalsIgnoreCase(curNetType)) {
                super.doHttp();
            } else {
                new MyTcpClient().doTcp();
            }
        }
    
        class MyTcpClient extends BaseTcpClient {
    
        }
    
        @Override
        public void choseType(String type) {
            this.curNetType = type;
        }
    }
    

    上面的代码里,增加了一个实现TcpClient的类BaseTcpClient,同时,在NetworkClientTask类中,我们可以不再实现TcpClient接口,而是让内部类MyTcpClient 继承BaseTcpClient,这样就可以通过调用MyTcpClient来实现调用BaseTcpClient的方法(有人会问,为什么不直接调BaseTcpClient?这个地方只是举例而已,事实上,MyTcpClient可以对BaseTcpClient做装饰,并供外部使用,这样就隐藏了细节,Spring就是这样做的)。

    下面简单讲解下Spring中对内部类继承的使用。

    Spring内部类继承实例

    在Spring的Controller中,我们可以通过将HttpServletRequest作为参数,来获取当然的Request对象,但是这个Request对象距离它的实现(比如tomcat对Request的封装)到底有多远?

    下面图是Spring的HttpServletRequest

    在这里插入图片描述

    Spring当前的HttpServletRequestSecurityContextHolderAwareRequestWrapper类,但是SecurityContextHolderAwareRequestWrapper类拿的是HttpSessionSecurityContextRepository的内部类Servlet3SaveToSessionRequestWrapper的对象,HttpSessionSecurityContextRepository和Request是没有关系的,但是它有个Request相关的内部类,内部类是这样的:

    private static class Servlet3SaveToSessionRequestWrapper extends
                HttpServletRequestWrapper {
        private final SaveContextOnUpdateOrErrorResponseWrapper response;
    
        public Servlet3SaveToSessionRequestWrapper(HttpServletRequest request,
                SaveContextOnUpdateOrErrorResponseWrapper response) {
            super(request);
            this.response = response;
        }
    
        @Override
        public AsyncContext startAsync() {
            response.disableSaveOnResponseCommitted();
            return super.startAsync();
        }
    
        @Override
        public AsyncContext startAsync(ServletRequest servletRequest,
                ServletResponse servletResponse) throws IllegalStateException {
            response.disableSaveOnResponseCommitted();
            return super.startAsync(servletRequest, servletResponse);
        }
    }
    

    内部类实现了HttpServletRequestWrapper,继而实现了HttpServletRequest接口。但是这个内部类是private static类型的,是这样被开放出去的:

    public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
        ···code····
    
        if (isServlet3) {
            requestResponseHolder.setRequest(new Servlet3SaveToSessionRequestWrapper(
                    request, wrappedResponse));
        }
    
        return context;
    }
    

    Spring的HttpServletRequest中获取tomcat真实的Request

    Spring通过这种方式,将tomcat的Request包裹的死死的,但是并不是不能拿到,通过多次反射还是可以拿到的。

    下面介绍下如何通过Spring当前的HttpServletRequest获取tomcat真实的Request,继而获取session个数:

    /**
     * 当前在线人数
     * @param request
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/test", method = { RequestMethod.GET })
    public ResultModel num(HttpServletRequest request) throws Exception {
        try {
            int activeSessions = getActiveSessions(request);
            if (activeSessions == -1) {
                while(true){
                    Field requestFieldTop = getRequsest(request);
                    if(requestFieldTop == null)break;
                    requestFieldTop.setAccessible(true);
                    request = (HttpServletRequest) requestFieldTop.get(request);
                    activeSessions = getActiveSessions(request);
                    if(activeSessions != -1){
                        break;
                    }
                }
    
            }
            return ResultModel.ok(activeSessions);
        } catch (Exception e) {
            return ResultModel.ok(10);
        }
    }
    
    private int getActiveSessions(HttpServletRequest request) throws Exception {
        if (request instanceof RequestFacade) {
            Field requestField = request.getClass().getDeclaredField("request");
            requestField.setAccessible(true);
            Request req = (Request) requestField.get(request);
            Context context = req.getContext();
            Manager manager = context.getManager();
            int activeSessions = manager.getActiveSessions();
            return activeSessions;
        }
        return -1;
    }
    
    private Field getRequsest(HttpServletRequest request) {
        Class<?> className = request.getClass();
        for (; className != Object.class; className = className.getSuperclass()) {// 获取本身和父级对象
            try {
                Field requestFieldtmp = className.getDeclaredField("request");
                if (requestFieldtmp != null) {
                    return requestFieldtmp;
                }
            } catch (Exception e) {
                continue;
            }
        }
        return null;
    }
    

    相关文章

      网友评论

        本文标题:【面试题集】Java如何实现多重继承及Spring内部类继承实例

        本文链接:https://www.haomeiwen.com/subject/dhltmctx.html