Tôi cần đọc các lớp có trong một gói Java. Các lớp đó nằm trong classpath. Tôi cần thực hiện tác vụ này trực tiếp từ một chương trình Java. Bạn có biết một cách đơn giản để làm không?
List<Class> classes = readClassesFrom("my.package")
Tôi cần đọc các lớp có trong một gói Java. Các lớp đó nằm trong classpath. Tôi cần thực hiện tác vụ này trực tiếp từ một chương trình Java. Bạn có biết một cách đơn giản để làm không?
List<Class> classes = readClassesFrom("my.package")
Câu trả lời:
Nếu bạn có Spring in you classpath thì thao tác sau sẽ thực hiện.
Tìm tất cả các lớp trong một gói được chú thích bằng 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;
}
Bạn có thể sử dụng Dự án Phản chiếu được mô tả ở đây
Nó khá đầy đủ và dễ sử dụng.
Mô tả ngắn gọn từ trang web trên:
Phản xạ quét đường dẫn classpath của bạn, lập chỉ mục siêu dữ liệu, cho phép bạn truy vấn nó trong thời gian chạy và có thể lưu và thu thập thông tin đó cho nhiều mô-đun trong dự án của bạn.
Thí dụ:
Reflections reflections = new Reflections(
new ConfigurationBuilder()
.setUrls(ClasspathHelper.forJavaClassPath())
);
Set<Class<?>> types = reflections.getTypesAnnotatedWith(Scannable.class);
Tôi sử dụng cái này, nó hoạt động với các tệp hoặc kho lưu trữ 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 đã triển khai một chức năng tìm kiếm classpath tuyệt vời trong PathMatchingResourcePatternResolver
. Nếu bạn sử dụng classpath*
tiền tố:, bạn có thể tìm thấy tất cả các tài nguyên, bao gồm các lớp trong một hệ thống phân cấp nhất định và thậm chí lọc chúng nếu bạn muốn. Sau đó, bạn có thể sử dụng con cái AbstractTypeHierarchyTraversingFilter
, AnnotationTypeFilter
và AssignableTypeFilter
để lọc những nguồn lực hoặc chú thích mức lớp hoặc trên giao diện họ thực hiện.
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[]{});
}
Giải pháp này đã được thử nghiệm trong môi trường EJB .
Scannotation và Reflections sử dụng phương pháp quét đường dẫn lớp:
Reflections reflections = new Reflections("my.package");
Set<Class<? extends Object>> classes = reflections.getSubTypesOf(Object.class);
Một cách tiếp cận khác là sử dụng API xử lý chú thích có thể cắm được của Java để viết bộ xử lý chú thích sẽ thu thập tất cả các lớp chú thích tại thời điểm biên dịch và xây dựng tệp chỉ mục để sử dụng trong thời gian chạy. Cơ chế này được thực hiện trong thư viện ClassIndex :
Iterable<Class> classes = ClassIndex.getPackageClasses("my.package");
Chức năng đó vẫn bị thiếu một cách đáng ngờ trong API phản chiếu Java theo như tôi biết. Bạn có thể lấy một đối tượng gói chỉ bằng cách thực hiện điều này:
Package packageObj = Package.getPackage("my.package");
Nhưng như bạn có thể nhận thấy, điều đó sẽ không cho phép bạn liệt kê các lớp trong gói đó. Ngay bây giờ, bạn phải thực hiện một cách tiếp cận theo định hướng hệ thống tệp hơn.
Tôi đã tìm thấy một số triển khai mẫu trong bài đăng này
Tôi không chắc 100% các phương pháp này sẽ hoạt động khi các lớp của bạn bị chôn vùi trong các tệp JAR, nhưng tôi hy vọng một trong số đó sẽ làm được điều đó cho bạn.
Tôi đồng ý với @skaffman ... nếu bạn có cách khác để giải quyết vấn đề này, tôi khuyên bạn nên làm điều đó thay thế.
Cơ chế mạnh mẽ nhất để liệt kê tất cả các lớp trong một gói nhất định hiện là ClassGraph , vì nó xử lý mảng cơ chế đặc tả classpath rộng nhất có thể , bao gồm cả hệ thống mô-đun JPMS mới. (Tôi là tác giả.)
List<String> classNames;
try (ScanResult scanResult = new ClassGraph().whitelistPackages("my.package")
.enableClassInfo().scan()) {
classNames = scanResult.getAllClasses().getNames();
}
eXtcos có vẻ đầy hứa hẹn. Hãy tưởng tượng bạn muốn tìm tất cả các lớp:
Với eXtcos, điều này đơn giản như
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));
}
});
Bill Burke đã viết một ( bài báo hay về quét lớp) và sau đó ông viết Scannotation .
Hibernate đã viết điều này:
CDI có thể giải quyết vấn đề này, nhưng không biết - chưa điều tra đầy đủ
.
@Inject Instance< MyClass> x;
...
x.iterator()
Cũng cho chú thích:
abstract class MyAnnotationQualifier
extends AnnotationLiteral<Entity> implements Entity {}
Tôi tình cờ đã triển khai nó và nó hoạt động trong hầu hết các trường hợp. Vì nó dài, tôi đã đưa nó vào một tập tin ở đây .
Ý tưởng là tìm vị trí của tệp nguồn lớp có sẵn trong hầu hết các trường hợp (một ngoại lệ đã biết là các tệp lớp JVM - theo như tôi đã thử nghiệm). Nếu mã nằm trong một thư mục, hãy quét qua tất cả các tệp và chỉ quét các tệp lớp. Nếu mã nằm trong tệp JAR, hãy quét tất cả các mục nhập.
Phương pháp này chỉ có thể được sử dụng khi:
Bạn có một lớp nằm trong cùng một gói mà bạn muốn khám phá, Lớp này được gọi là SeedClass. Ví dụ: nếu bạn muốn liệt kê tất cả các lớp trong 'java.io', thì lớp hạt giống có thể là java.io.File
.
Các lớp của bạn nằm trong một thư mục hoặc trong một tệp JAR, nó có thông tin tệp nguồn (không phải tệp mã nguồn mà chỉ là tệp nguồn). Theo như tôi đã thử, nó hoạt động gần như 100% ngoại trừ lớp JVM (những lớp đó đi kèm với JVM).
Chương trình của bạn phải có quyền truy cập vào ProtectionDomain của các lớp đó. Nếu chương trình của bạn được tải cục bộ, sẽ không có vấn đề gì.
Tôi đã thử nghiệm chương trình chỉ để sử dụng thường xuyên, vì vậy nó có thể vẫn gặp sự cố.
Tôi hi vọng cái này giúp được.
Đây là một tùy chọn khác, sửa đổi một chút cho một câu trả lời khác ở trên / dưới:
Reflections reflections = new Reflections("com.example.project.package",
new SubTypesScanner(false));
Set<Class<? extends Object>> allClasses =
reflections.getSubTypesOf(Object.class);
Quay lại khi các applet là nơi phổ biến, một trong những có thể có một URL trên classpath. Khi trình nạp lớp yêu cầu một lớp, nó sẽ tìm kiếm tất cả các vị trí trên classpath, bao gồm cả tài nguyên http. Bởi vì bạn có thể có những thứ như URL và thư mục trên classpath, không có cách nào dễ dàng để có được danh sách chính xác của các lớp.
Tuy nhiên, bạn có thể đến khá gần. Một số thư viện Spring hiện đang làm việc này. Bạn có thể lấy tất cả các jar trên classpath và mở chúng lên như các tệp. Sau đó, bạn có thể lấy danh sách tệp này và tạo cấu trúc dữ liệu chứa các lớp của bạn.
sử dụng maven phụ thuộc:
groupId: net.sf.extcos
artifactId: extcos
version: 0.4b
sau đó sử dụng mã này:
ComponentScanner scanner = new ComponentScanner();
Set classes = scanner.getClasses(new ComponentQuery() {
@Override
protected void query() {
select().from("com.leyton").returning(allExtending(DynamicForm.class));
}
});
Brent - lý do liên kết là một cách liên quan đến thực tế là bất kỳ lớp nào trên bất kỳ thành phần nào của CLASSPATH của bạn đều có thể tự khai báo trong bất kỳ gói nào (ngoại trừ java / javax). Vì vậy, không có ánh xạ của TẤT CẢ các lớp trong một "gói" đã cho bởi vì không ai biết và cũng không thể biết. Bạn có thể cập nhật tệp jar vào ngày mai và xóa hoặc thêm lớp. Nó giống như việc cố gắng lấy một danh sách tất cả những người có tên John / Jon / Johan ở tất cả các quốc gia trên thế giới - không ai trong chúng ta là người toàn trí, do đó không ai trong chúng ta sẽ có câu trả lời chính xác.