JSP 가 어떻게 servlet 로 컴 파일 되 어 서 비 스 를 제공 하 는 지 에 대해 논 하 다.

11778 단어 JSP컴 파일servlet
개술
서버 가 대외 적 으로 JSP 요청 서 비 스 를 제공 하 는 것 은 JspServlet 로 HttpServlet 에서 계승 합 니 다.핵심 서비스 입 구 는 service 방법 이 고 대체적으로 다음 과 같다.
  • 먼저 요청 한 jspUri 를 가 져 옵 니 다.클 라 이언 트 가 요청 하면:https://xxx.xx.com/jsp/test.jsp,그러면 획득 한 jspUri 는:/jsp/test.jsp
  • 입 니 다.
  • 그리고 캐 시(Map 구조)에 jspUri 의 JspServletWrapper 가 포함 되 어 있 는 지 확인 합 니 다.없 으 면 JspServletWrapper 를 만 들 고 캐 시 를 해 야 합 니 다.그리고 JspServletWrapper 의 service 방법
  • 을 호출 합 니 다.
  • development 모드 나 첫 요청 이 라면 JspCompilation Context.copile()방법
  • 을 실행 해 야 합 니 다.
  • JSpCompilation Context.copile 방법 에 서 는 jsp 파일 의 lastModified 에 따라 파일 이 업데이트 되 었 는 지(out dated)여 부 를 판단 합 니 다.업데이트 되 었 으 면 이전에 생 성 된 관련 파일 을 삭제 한 다음 jspLoader 를 비 워 두 어야 합 니 다.(뒤에 불 러 올 때 jspLoader 가 비어 있 으 면 새로운 jspLoader 를 만 듭 니 다.)Compiler.copile 방법 으로 servlet 를 생 성하 고 reload 를 true 로 설정 합 니 다(기본 값 은 true).그 다음 에 reload 매개 변수 에 따라 이 servlet 를 다시 불 러 올 필요 가 있 는 지 판단 합 니 다
  • JspServletWrapper.getServlet 방법 을 호출 하여 최종 서 비 스 를 제공 하 는 servlet 를 가 져 옵 니 다.이 과정 은 reload 매개 변수 에 따라 servlet 를 다시 불 러 올 필요 가 있 는 지 확인 합 니 다.다시 불 러 올 필요 가 있 으 면 jspLoader 를 가 져 옵 니 다.(jsp 파일 이 만 료 된 것 을 앞에서 발견 하면 jspLoader 가 비어 있 으 면 새로운 jspLoader 를 만 듭 니 다)또한 reload 를 false
  • 로 설정 합 니 다.
  • servlet 의 service 방법 을 호출 하여 서 비 스 를 제공 합 니 다.servlet 이 Single ThreadModel 인 터 페 이 스 를 실현 하면 synchronized 로 동기 화 제어
  • 를 합 니 다.

    소스 코드 분석
    먼저 JspServlet 의 핵심 논 리 를 보면 주로 jspUri 를 얻 고 JspServletWrapper 를 얻 는 것 입 니 다.각각 입구 service 방법 과 serviceJspFile 방법 입 니 다.코드 는 다음 과 같 습 니 다(부분).
    
    /**
     *   jspUri,    serviceJspFile  
     */
    public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            String jspUri = this.jspFile;
            String pathInfo;
            if (jspUri == null) {
                pathInfo = (String)request.getAttribute(Constants.JSP_FILE);
                if (pathInfo != null) {
                    jspUri = pathInfo;
                    request.removeAttribute(Constants.JSP_FILE);
                }
            }
     
            if (jspUri == null) {
                jspUri = (String)request.getAttribute("javax.servlet.include.servlet_path");
                if (jspUri != null) {
                    pathInfo = (String)request.getAttribute("javax.servlet.include.path_info");
                    if (pathInfo != null) {
                        jspUri = jspUri + pathInfo;
                    }
                } else {
                    jspUri = request.getServletPath();
                    pathInfo = request.getPathInfo();
                    if (pathInfo != null) {
                        jspUri = jspUri + pathInfo;
                    }
                }
            }
     
     
            boolean precompile = this.preCompile(request);
            this.serviceJspFile(request, response, jspUri, precompile);
        }
     
     
    /**
     *     JspServletWrapper,    JspServletWrapper.service  
     */
    private void serviceJspFile(HttpServletRequest request, HttpServletResponse response, String jspUri, boolean precompile) throws ServletException, IOException {
            JspServletWrapper wrapper = this.rctxt.getWrapper(jspUri);
            if (wrapper == null) {
                synchronized(this) {
                    wrapper = this.rctxt.getWrapper(jspUri);
                    if (wrapper == null) {
                        if (null == this.context.getResource(jspUri)) {
                            this.handleMissingResource(request, response, jspUri);
                            return;
                        }
     
                        wrapper = new JspServletWrapper(this.config, this.options, jspUri, this.rctxt);
                        this.rctxt.addWrapper(jspUri, wrapper);
                    }
                }
            }
     
            try {
                //      
                wrapper.service(request, response, precompile);
            } catch (FileNotFoundException var8) {
                this.handleMissingResource(request, response, jspUri);
            }
     
        }
    그리고 JspServletWrapper.service 방법(부분 코드)에 들 어 갑 니 다.
    
    //    reload volatile   
    private volatile boolean reload = true;
     
    public void service(HttpServletRequest request, HttpServletResponse response, boolean precompile) throws ServletException, IOException, FileNotFoundException {
            Servlet servlet;
            try {
                if (this.ctxt.isRemoved()) {
                    throw new FileNotFoundException(this.jspUri);
                }
                //  development   firstTime(    )
                if (!this.options.getDevelopment() && !this.firstTime) {
                    if (this.compileException != null) {
                        throw this.compileException;
                    }
                } else {
                    synchronized (this) {
                        this.firstTime = false;
                        //  JspCompilationContext.compile  
                        this.ctxt.compile();
                    }
                }
                //         servlet
                servlet = this.getServlet();
                if (precompile) {
                    return;
                }
            }
            try {
                //      SingleThreadModel           
                if (servlet instanceof SingleThreadModel) {
                    synchronized (this) {
                        servlet.service(request, response);
                    }
                } else {
                    servlet.service(request, response);
                }
            }
        }
    여 기 는 주로 JSpCompilation Context.com plie 방법 을 봅 니 다.
    
    public void compile() throws JasperException, FileNotFoundException {
            this.createCompiler();
            if (this.jspCompiler.isOutDated()) {
                if (this.isRemoved()) {
                    throw new FileNotFoundException(this.jspUri);
                }
                try {
                    //      
                    this.jspCompiler.removeGeneratedFiles();
                    //  jspLoader,   null,          JspLoader
                    this.jspLoader = null;
                    //  jsp  servlet   ,     AntCompiler JDTCompiler,  JDTCompiler
                    this.jspCompiler.compile();
                    //  reload true,    reload            
                    this.jsw.setReload(true);
                    this.jsw.setCompilationException((JasperException) null);
                }
            }
        }
    isOutDated 방법 에 대한 판단 에 주의해 야 합 니 다.요청 할 때마다 jsp 파일 이 업데이트 되 었 는 지 확인 하 는 것 이 아니 라 간격 이 있 습 니 다.만약 에 이번 업데이트 시간 이 지난번 업데이트 검사+간격 내 에 간격 을 초과 하지 않 았 다 면 jsp 파일 의 업 데 이 트 를 검사 하지 않 았 을 것 입 니 다.이것 이 바로 우리 가 말 한 jsp 열 업데이트 지연 시간 입 니 다.isOutDated 는 Compiler 의 방법 입 니 다.다음 과 같 습 니 다(일부 코드).
    
        public boolean isOutDated(boolean checkClass) {
            if (this.jsw != null && this.ctxt.getOptions().getModificationTestInterval() > 0) {
                //getModificationTestInterval          ,    
                if (this.jsw.getLastModificationTest() + (long)(this.ctxt.getOptions().getModificationTestInterval() * 1000) > System.currentTimeMillis()) {
                    return false;
                }
     
                this.jsw.setLastModificationTest(System.currentTimeMillis());
            }
     
            Long jspRealLastModified = this.ctxt.getLastModified(this.ctxt.getJspFile());
            if (jspRealLastModified < 0L) {
                return true;
            } else {
                long targetLastModified = 0L;
                File targetFile;
                if (checkClass) {
                    targetFile = new File(this.ctxt.getClassFileName());
                } else {
                    targetFile = new File(this.ctxt.getServletJavaFileName());
                }
     
                if (!targetFile.exists()) {
                    return true;
                } else {
                    targetLastModified = targetFile.lastModified();
                    if (checkClass && this.jsw != null) {
                        this.jsw.setServletClassLastModifiedTime(targetLastModified);
                    }
     
                    if (targetLastModified != jspRealLastModified) {
                        if (this.log.isDebugEnabled()) {
                            this.log.debug("Compiler: outdated: " + targetFile + " " + targetLastModified);
                        }
     
                        return true;
                    } else if (this.jsw == null) {
                        return false;
                    } 
                }
            }
    또한,여 기 는 JSP 의 컴 파일 작업 과 관련 되 어 있 습 니 다.컴 파일 작업 은 주로 org.apache.jsper.copiler.Compiler 컴 파일 러 가 맡 습 니 다.Compiler 는 추상 적 인 유형 입 니 다.apache-jsp 에서 두 가지 실현 을 제공 합 니 다.AntCompiler 와 JDTCompiler,기본 적 으로 사용 하 는 컴 파일 러 는 JDTCompiler 입 니 다.
    마지막 으로 JspServletWrapper.getServlet 방법 으로 돌아 가기:
    
    private volatile boolean reload = true;
    public Servlet getServlet() throws ServletException {
            //reload  volatile     boolean  
            //        
            if (this.reload) {
                synchronized (this) {
                    if (this.reload) {
                        //    
                        this.destroy();
                        Servlet servlet;
                        try {
                            InstanceManager instanceManager = InstanceManagerFactory.getInstanceManager(this.config);
                            //      serlvet    ,     getJspLoader  
                            servlet = (Servlet) instanceManager.newInstance(this.ctxt.getFQCN(), this.ctxt.getJspLoader());
                        } catch (Exception var6) {
                            Throwable t = ExceptionUtils.unwrapInvocationTargetException(var6);
                            ExceptionUtils.handleThrowable(t);
                            throw new JasperException(t);
                        }
     
                        servlet.init(this.config);
                        if (!this.firstTime) {
                            this.ctxt.getRuntimeContext().incrementJspReloadCount();
                        }
                        this.theServlet = servlet;
                        this.reload = false;
                    }
                }
            }
            return this.theServlet;
        }
    이 를 통 해 알 수 있 듯 이 방법 에서 이중 검 측 체 제 를 사용 하여 과부하 가 필요 한 지 여 부 를 판단 하고 reload 매개 변 수 는 volatile 수식 으로 가시 성 을 확보 합 니 다.새로운 servlet 인 스 턴 스 를 만 들 때 classLoader 는 JspCompilation Context.getJspLoader 방법 으로 얻 었 습 니 다.이 방법의 논 리 를 보십시오.
    
    public ClassLoader getJspLoader() {
            if (this.jspLoader == null) {
                this.jspLoader = new JasperLoader(new URL[]{this.baseUrl}, this.getClassLoader(), this.rctxt.getPermissionCollection());
            }
     
            return this.jspLoader;
        }
    앞의 JspCompilation Context.coplie 의 논리 에서 jsp 파일 이 업데이트 되 었 음(만 료)이 감지 되면 jspLoader 는 null 로 설 정 됩 니 다.이때 새로운 jspLoader(JasperLoader)를 만 들 고 새로운 loader 를 사용 하여 새로운 servlet 를 불 러 와 jsp 의 열 업 데 이 트 를 완료 합 니 다.오래된 classloader 는 나중에 GC 에서 직접 회수 합 니 다.
    JSP 가 servlet 로 어떻게 컴 파일 되 고 서 비 스 를 제공 하 는 지 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 관련 JSP 가 servlet 로 컴 파일 된 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 조회 하 시기 바 랍 니 다.앞으로 많은 응원 바 랍 니 다!

    좋은 웹페이지 즐겨찾기