คลาสโหลดเดอร์การคอมไพล์แบบกำหนดเองของแอปพลิเคชัน Java EE

ข้อกำหนดมีดังต่อไปนี้:

  1. เว็บแอปพลิเคชัน Java EE (ใน Tomcat)...
  2. ตายง่าย ๆ เพียงแค่ JSP เซิร์ฟเล็ตและขวด - ไม่มีเฟรมเวิร์ก ...
  3. ไม่มีอะไรจำเป็นต้องโหลดซ้ำ ไม่ใช่เซิร์ฟเวอร์ ไม่ใช่บริบท ไม่มีอะไร...

แนวคิดจนถึงขณะนี้คือการขยาย WebappClassLoader (catalina.jar) เพื่อสร้าง classloader ที่คุณกำหนดเอง และลงทะเบียนใน context.xml เป็นองค์ประกอบ Loader ด้วยโค้ดเพียงเล็กน้อย คุณก็จะสามารถเขียน classloader ของคุณได้อย่างดี ใครจะรู้ว่าจะหาไฟล์ต้นฉบับ Java ได้ที่ไหน จากนั้นจึงคอมไพล์เป็นไฟล์คลาส หากจำเป็น จากนั้นจึงโหลด int memory เมื่อถูกขอให้ทำเช่นนั้น ตรรกะนั้นธรรมดาและเรียบง่าย

ยกเว้น:

Jasper จะรู้ได้อย่างไรว่าจะค้นหาคลาสของคุณโดยอัตโนมัติซึ่งสร้างโดย classloader ที่คุณกำหนดเอง เพื่อให้สามารถคอมไพล์ JSP ที่อ้างอิงถึงคลาสเหล่านั้น และแม้แต่รีเฟรชคลาสเหล่านั้น (คลาสของคุณ) ได้ทันที เป็นไปไม่ได้หรือที่จะบรรลุผล?

คุณคิดอย่างไร?

(โปรดอย่าพยายามเปลี่ยนบทสนทนาโดยชี้ไปที่เฟรมเวิร์กที่มีอยู่มากมายที่ดูแลสิ่งเหล่านี้ให้กับคุณ ข้อกำหนดมีความเฉพาะเจาะจงมาก: ไม่มีเฟรมเวิร์ก ไม่มีอะไรเลย)


person Takis Bouyouris    schedule 07.12.2011    source แหล่งที่มา
comment
เอกสารประกอบของ Jasper กำหนดว่า: JDT ใช้ในการคอมไพล์เพจ JSP - ขณะนี้คอมไพลเลอร์ Eclipse JDT Java ใช้ในการคอมไพล์ซอร์สโค้ด JSP java คอมไพลเลอร์นี้โหลดการขึ้นต่อกันของซอร์สจากคอนเทนเนอร์คลาสโหลดเดอร์ Ant และ javac ยังคงสามารถใช้ได้.. ดังนั้นคำถามจึงเกี่ยวข้องกับ container classloader!   -  person Takis Bouyouris    schedule 08.12.2011
comment
คอนเทนเนอร์คลาสโหลดเดอร์เป็นอินสแตนซ์ของ org.apache.catalina.loader.StandardClassLoader มันเป็นคลาสโหลดเดอร์ เครื่องยนต์ ฉันไม่เห็นวิธีที่ชัดเจนในการแทนที่/เสริมสิ่งนี้ให้ทำงานโดยอัตโนมัติ...   -  person Takis Bouyouris    schedule 08.12.2011


คำตอบ (1)


คุณสามารถเพิ่ม classloader ในบริบทของเว็บแอปพลิเคชันปัจจุบัน (context.xml):

<Context>
    <Loader loaderClass = "gr.nevma.cccl.CompilingClassLoader"/>
</Context>

จากนั้นสร้าง classloader เช่น:

package package gr.nevma.cccl;

import ...

public class CompilingClassLoader extends WebappClassLoader {

    public CompilingClassLoader ( ClassLoader parentClassLoader ) {

        super( parentClassLoader );

    }

    public Class<?> loadClass ( String className, boolean resolve ) throws ClassNotFoundException { 

        Class<?> theClass = null;

        // Do you stuff here
        return theClass;

    }

}

แต่สิ่งนี้จะช่วยได้ก็ต่อเมื่อเราต้องการโหลดคลาสอย่างชัดเจนจาก classloader เองโดยการเรียก ClassLoader::loadClass(...) สิ่งนี้จะไม่ช่วยในการรวบรวม JSP โดย Jasper ซึ่งหมายความว่าเมื่อ Jasper จะพยายามคอมไพล์ JSP ที่ใช้คลาสที่โหลดโดย classloader นี้ การคอมไพล์จะล้มเหลว เนื่องจาก Jasper จะไม่ขอให้ classloader นี้โหลดคลาสใด ๆ เลยด้วยซ้ำ

ดังนั้นด้วยวิธีนี้ การโหลดและการโหลดคลาสซ้ำอัตโนมัติจึงสามารถทำได้แต่ในลักษณะแบบแมนนวล คลาสเหล่านี้สามารถใช้ได้ทั่วทั้งเว็บแอปพลิเคชัน เช่น หน้า JSP!

person Takis Bouyouris    schedule 12.12.2011
comment
ดูเหมือนว่าเมธอด getResourceAsStream() ของ WebappClassLoader จะถูกเรียกเพื่อโหลดคลาสที่ Jasper ต้องการเพื่อคอมไพล์เพจ JSP กล่าวอีกนัยหนึ่ง เมื่อ Jasper พยายามคอมไพล์เพจ JSP มันจะร้องขอคลาสที่จำเป็นจาก classloader โดยการเรียก getResourceAsStream() ดังนั้นจึงต้องแทนที่วิธีนี้เพื่อโหลดสิ่งใดก็ตามที่ต้องโหลดแบบไดนามิก ... - person Takis Bouyouris; 14.12.2011
comment
เพื่อให้สามารถโหลดแล้วโหลดคลาสใดๆ ก็ได้ (เช่น กำหนดไว้) เราจะต้องลบและสร้างคลาสโหลดเดอร์ใหม่ กล่าวอีกนัยหนึ่ง อินสแตนซ์ของ classloader สามารถโหลด (กำหนด) คลาสได้เพียงครั้งเดียวเท่านั้น! หากจำเป็นต้องโหลดคลาสใหม่จริง (กำหนดอีกครั้ง) จะต้องสร้าง classloader ใหม่สำหรับงานนี้และลบคลาสเก่าออก - person Takis Bouyouris; 16.12.2011
comment
เมื่อ classloader โหลดคลาสหนึ่งครั้ง ดูเหมือนว่า JVM จะไม่ขอคลาสนี้จากคลาสนั้นอีก อย่างน้อยก็ไม่นานตราบใดที่หน่วยความจำ PermGen (ที่คลาสถูกจัดเก็บใน RAM) ไม่เต็ม ดังนั้นจึงหลีกเลี่ยงไม่ได้ที่จะต้องลบคลาสโหลดเดอร์และสร้างใหม่เพื่อให้สามารถโหลดคำจำกัดความของคลาสอีกครั้งได้ และสิ่งนี้ทำให้ชัดเจนยิ่งขึ้นว่าหากการอ้างอิงของคลาสที่รีโหลดยังคงวนเวียนอยู่ อินสแตนซ์ของคลาสโหลดเดอร์ที่โหลดคลาสเหล่านั้นจะไม่ถูกเก็บขยะ - person Takis Bouyouris; 19.12.2011
comment
นอกจากนี้ เมื่อดูที่ซอร์สโค้ดของ Tomcat ในคลาส JspCompilationContext ของ Jasper เราจะเห็นได้ว่านี่คือสิ่งที่ Jasper ทำเพื่อคอมไพล์และโหลดเพจ JSP ใหม่! โดยจะตรวจสอบซอร์สไฟล์ JSP เป็นระยะเพื่อดูการเปลี่ยนแปลง และหากพบการเปลี่ยนแปลงใดๆ ก็จะทำลายคลาสโหลดเดอร์ สร้างอินสแตนซ์ใหม่ คอมไพล์เพจ JSP ที่ต้องการคอมไพล์ และโหลดซ้ำด้วยอินสแตนซ์ใหม่ของคลาสโหลดเดอร์ และใช่ ดูเหมือนเซิร์ฟเวอร์จะต้องทำงานหนักมากจริงๆ หากเซิร์ฟเวอร์ดำเนินการบ่อยมาก - person Takis Bouyouris; 19.12.2011