ฉันต้องการอ่านคลาสที่อยู่ในแพ็คเกจ Java ชั้นเรียนเหล่านั้นอยู่ใน classpath ฉันต้องทำงานนี้จากโปรแกรม Java โดยตรง คุณรู้วิธีง่ายๆในการทำหรือไม่?
List<Class> classes = readClassesFrom("my.package")
ฉันต้องการอ่านคลาสที่อยู่ในแพ็คเกจ Java ชั้นเรียนเหล่านั้นอยู่ใน classpath ฉันต้องทำงานนี้จากโปรแกรม Java โดยตรง คุณรู้วิธีง่ายๆในการทำหรือไม่?
List<Class> classes = readClassesFrom("my.package")
คำตอบ:
หากคุณมีSpringใน classpath ของคุณสิ่งต่อไปนี้จะทำ
ค้นหาคลาสทั้งหมดในแพ็คเกจที่มีคำอธิบายประกอบ XmlRootElement:
private List<Class> findMyTypes(String basePackage) throws IOException, ClassNotFoundException
{
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
List<Class> candidates = new ArrayList<Class>();
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + "/" + "**/*.class";
Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
for (Resource resource : resources) {
if (resource.isReadable()) {
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
if (isCandidate(metadataReader)) {
candidates.add(Class.forName(metadataReader.getClassMetadata().getClassName()));
}
}
}
return candidates;
}
private String resolveBasePackage(String basePackage) {
return ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage));
}
private boolean isCandidate(MetadataReader metadataReader) throws ClassNotFoundException
{
try {
Class c = Class.forName(metadataReader.getClassMetadata().getClassName());
if (c.getAnnotation(XmlRootElement.class) != null) {
return true;
}
}
catch(Throwable e){
}
return false;
}
คุณสามารถใช้โครงการ Reflections ที่อธิบายไว้ที่นี่
ค่อนข้างสมบูรณ์และใช้งานง่าย
คำอธิบายสั้น ๆ จากเว็บไซต์ด้านบน:
การสะท้อนจะสแกนพา ธ คลาสของคุณจัดทำดัชนีเมทาดาทาช่วยให้คุณสืบค้นบนรันไทม์และอาจบันทึกและรวบรวมข้อมูลนั้นสำหรับโมดูลต่างๆภายในโปรเจ็กต์ของคุณ
ตัวอย่าง:
Reflections reflections = new Reflections(
new ConfigurationBuilder()
.setUrls(ClasspathHelper.forJavaClassPath())
);
Set<Class<?>> types = reflections.getTypesAnnotatedWith(Scannable.class);
ฉันใช้อันนี้มันใช้ได้กับไฟล์หรือที่เก็บถาวรของ jar
public static ArrayList<String>getClassNamesFromPackage(String packageName) throws IOException{
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
URL packageURL;
ArrayList<String> names = new ArrayList<String>();;
packageName = packageName.replace(".", "/");
packageURL = classLoader.getResource(packageName);
if(packageURL.getProtocol().equals("jar")){
String jarFileName;
JarFile jf ;
Enumeration<JarEntry> jarEntries;
String entryName;
// build jar file name, then loop through zipped entries
jarFileName = URLDecoder.decode(packageURL.getFile(), "UTF-8");
jarFileName = jarFileName.substring(5,jarFileName.indexOf("!"));
System.out.println(">"+jarFileName);
jf = new JarFile(jarFileName);
jarEntries = jf.entries();
while(jarEntries.hasMoreElements()){
entryName = jarEntries.nextElement().getName();
if(entryName.startsWith(packageName) && entryName.length()>packageName.length()+5){
entryName = entryName.substring(packageName.length(),entryName.lastIndexOf('.'));
names.add(entryName);
}
}
// loop through files in classpath
}else{
URI uri = new URI(packageURL.toString());
File folder = new File(uri.getPath());
// won't work with path which contains blank (%20)
// File folder = new File(packageURL.getFile());
File[] contenuti = folder.listFiles();
String entryName;
for(File actual: contenuti){
entryName = actual.getName();
entryName = entryName.substring(0, entryName.lastIndexOf('.'));
names.add(entryName);
}
}
return names;
}
Spring ได้ใช้ฟังก์ชันการค้นหา classpath ที่ยอดเยี่ยมในไฟล์PathMatchingResourcePatternResolver
. หากคุณใช้classpath*
คำนำหน้า: คุณสามารถค้นหาทรัพยากรทั้งหมดรวมถึงคลาสในลำดับชั้นที่กำหนดและยังกรองทรัพยากรได้หากต้องการ แล้วคุณสามารถใช้เด็กของAbstractTypeHierarchyTraversingFilter
, AnnotationTypeFilter
และAssignableTypeFilter
การกรองทรัพยากรเหล่านั้นทั้งในคำอธิบายประกอบระดับชั้นเรียนหรือในอินเตอร์เฟซที่พวกเขาใช้
Java 1.6.0_24:
public static File[] getPackageContent(String packageName) throws IOException{
ArrayList<File> list = new ArrayList<File>();
Enumeration<URL> urls = Thread.currentThread().getContextClassLoader()
.getResources(packageName);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
File dir = new File(url.getFile());
for (File f : dir.listFiles()) {
list.add(f);
}
}
return list.toArray(new File[]{});
}
โซลูชันนี้ได้รับการทดสอบภายในสภาพแวดล้อมEJB
การสแกนและการสะท้อนใช้วิธีการสแกนพา ธ คลาส:
Reflections reflections = new Reflections("my.package");
Set<Class<? extends Object>> classes = reflections.getSubTypesOf(Object.class);
อีกวิธีหนึ่งคือการใช้Java Pluggable Annotation Processing APIเพื่อเขียนตัวประมวลผลคำอธิบายประกอบซึ่งจะรวบรวมคลาสที่มีคำอธิบายประกอบทั้งหมดในเวลาคอมไพล์และสร้างไฟล์ดัชนีสำหรับการใช้งานรันไทม์ กลไกนี้ถูกนำไปใช้ในไลบรารีClassIndex :
Iterable<Class> classes = ClassIndex.getPackageClasses("my.package");
กลไกที่มีประสิทธิภาพมากที่สุดสำหรับการแสดงรายการคลาสทั้งหมดในแพ็คเกจที่กำหนดคือClassGraphเนื่องจากจัดการกับอาร์เรย์ของกลไกข้อกำหนดคลาสพา ธ ที่กว้างที่สุดรวมถึงระบบโมดูล JPMS ใหม่ (ฉันเป็นคนเขียน)
List<String> classNames;
try (ScanResult scanResult = new ClassGraph().whitelistPackages("my.package")
.enableClassInfo().scan()) {
classNames = scanResult.getAllClasses().getNames();
}
ฟังก์ชันดังกล่าวยังคงหายไปอย่างน่าสงสัยจาก API การสะท้อนของ Java เท่าที่ฉันทราบ คุณสามารถรับวัตถุแพคเกจได้โดยทำสิ่งนี้:
Package packageObj = Package.getPackage("my.package");
แต่อย่างที่คุณสังเกตเห็นว่าจะไม่ให้คุณแสดงรายการชั้นเรียนในแพ็คเกจนั้น ณ ตอนนี้คุณต้องใช้วิธีการที่เน้นระบบไฟล์มากขึ้น
ฉันพบการใช้งานตัวอย่างบางส่วนในสิ่งนี้โพสต์
ฉันไม่แน่ใจ 100% ว่าวิธีการเหล่านี้จะใช้ได้ผลเมื่อชั้นเรียนของคุณถูกฝังอยู่ในไฟล์ JAR แต่ฉันหวังว่าหนึ่งในนั้นจะทำเพื่อคุณ
ฉันเห็นด้วยกับ @skaffman ... ถ้าคุณมีวิธีอื่นในการดำเนินการนี้ฉันขอแนะนำให้ทำเช่นนั้นแทน
eXtcosมีแนวโน้มที่ดี ลองนึกภาพว่าคุณต้องการค้นหาคลาสทั้งหมดที่:
ด้วย eXtcos มันง่ายพอ ๆ กับ
ClasspathScanner scanner = new ClasspathScanner();
final Set<Class> classStore = new ArraySet<Class>();
Set<Class> classes = scanner.getClasses(new ClassQuery() {
protected void query() {
select().
from(“common”).
andStore(thoseExtending(Component.class).into(classStore)).
returning(allAnnotatedWith(MyComponent.class));
}
});
บิลเบิร์คได้เขียน (ดีบทความเกี่ยวกับการสแกนระดับ] และแล้วเขาก็เขียนScannotation
Hibernateเขียนไว้แล้ว:
CDIอาจแก้ปัญหานี้ได้ แต่ไม่รู้ - ยังไม่ได้ตรวจสอบอย่างละเอียด
.
@Inject Instance< MyClass> x;
...
x.iterator()
นอกจากนี้สำหรับคำอธิบายประกอบ:
abstract class MyAnnotationQualifier
extends AnnotationLiteral<Entity> implements Entity {}
ฉันได้นำไปใช้และใช้ได้ในกรณีส่วนใหญ่ เพราะมันเป็นนานผมวางไว้ในแฟ้มได้ที่นี่
แนวคิดคือการค้นหาตำแหน่งของซอร์สไฟล์คลาสที่มีอยู่ในกรณีส่วนใหญ่ (ข้อยกเว้นที่ทราบคือไฟล์คลาส JVM - เท่าที่ฉันได้ทดสอบ) หากรหัสอยู่ในไดเร็กทอรีให้สแกนไฟล์ทั้งหมดและเฉพาะไฟล์คลาสเฉพาะจุด หากรหัสอยู่ในไฟล์ JAR ให้สแกนรายการทั้งหมด
วิธีนี้สามารถใช้ได้เฉพาะเมื่อ:
คุณมีคลาสที่อยู่ในแพ็คเกจเดียวกับที่คุณต้องการค้นพบคลาสนี้เรียกว่า SeedClass ตัวอย่างเช่นถ้าคุณต้องการที่จะแสดงรายการทุกชั้นใน 'java.io' java.io.File
ชั้นเมล็ดอาจจะ
คลาสของคุณอยู่ในไดเร็กทอรีหรือในไฟล์ JAR ซึ่งมีข้อมูลซอร์สไฟล์ (ไม่ใช่ไฟล์ซอร์สโค้ด แต่เป็นเพียงไฟล์ซอร์ส) เท่าที่ฉันได้ลองใช้งานได้เกือบ 100% ยกเว้นคลาส JVM (คลาสเหล่านั้นมาพร้อมกับ JVM)
โปรแกรมของคุณต้องได้รับอนุญาตให้เข้าถึง ProtectionDomain ของคลาสเหล่านั้น หากโปรแกรมของคุณโหลดภายในเครื่องก็ไม่น่ามีปัญหา
ฉันได้ทดสอบโปรแกรมสำหรับการใช้งานปกติเท่านั้นดังนั้นจึงอาจยังมีปัญหา
ฉันหวังว่านี่จะช่วยได้.
นี่คืออีกทางเลือกหนึ่งการปรับเปลี่ยนเล็กน้อยสำหรับคำตอบอื่นในด้านบน / ด้านล่าง:
Reflections reflections = new Reflections("com.example.project.package",
new SubTypesScanner(false));
Set<Class<? extends Object>> allClasses =
reflections.getSubTypesOf(Object.class);
ย้อนกลับไปเมื่อแอพเพล็ตเป็นสถานที่ทั่วไปอาจมี URL บน classpath เมื่อ classloader ต้องการคลาสมันจะค้นหาตำแหน่งทั้งหมดบน classpath รวมถึงทรัพยากร http เนื่องจากคุณสามารถมีสิ่งต่างๆเช่น URL และไดเร็กทอรีบน classpath จึงไม่มีวิธีง่ายๆในการรับรายชื่อคลาสที่ชัดเจน
อย่างไรก็ตามคุณสามารถเข้าใกล้ได้ดี ห้องสมุด Spring บางแห่งกำลังดำเนินการอยู่ในขณะนี้ คุณสามารถรับ jar ทั้งหมดบน classpath และเปิดขึ้นมาเหมือนไฟล์ จากนั้นคุณสามารถใช้รายการไฟล์นี้และสร้างโครงสร้างข้อมูลที่มีคลาสของคุณ
ใช้การพึ่งพา maven:
groupId: net.sf.extcos
artifactId: extcos
version: 0.4b
จากนั้นใช้รหัสนี้:
ComponentScanner scanner = new ComponentScanner();
Set classes = scanner.getClasses(new ComponentQuery() {
@Override
protected void query() {
select().from("com.leyton").returning(allExtending(DynamicForm.class));
}
});
Brent - เหตุผลที่การเชื่อมโยงเป็นวิธีหนึ่งที่เกี่ยวข้องกับความจริงที่ว่าคลาสใด ๆ ในส่วนประกอบใด ๆ ของ CLASSPATH ของคุณสามารถประกาศตัวเองในแพ็คเกจใดก็ได้ (ยกเว้น java / javax) ดังนั้นจึงไม่มีการแมปคลาสทั้งหมดใน "แพ็กเกจ" ที่กำหนดเพราะไม่มีใครรู้และไม่สามารถรู้ได้ คุณสามารถอัปเดตไฟล์ jar ในวันพรุ่งนี้และลบหรือเพิ่มคลาสได้ มันเหมือนกับการพยายามหารายชื่อคนที่ชื่อ John / Jon / Johan ในทุกประเทศทั่วโลก - ไม่มีพวกเราที่ฉลาดรอบรู้ดังนั้นพวกเราจะไม่มีคำตอบที่ถูกต้อง