Tôi có thể làm điều đó với sự phản chiếu hoặc một cái gì đó tương tự không?
Tôi có thể làm điều đó với sự phản chiếu hoặc một cái gì đó tương tự không?
Câu trả lời:
Tôi đã tìm kiếm trong một thời gian và dường như có nhiều cách tiếp cận khác nhau, đây là bản tóm tắt:
Thư viện phản xạ khá phổ biến nếu bạn không ngại thêm phần phụ thuộc. Nó sẽ trông như thế này:
Reflections reflections = new Reflections("firstdeveloper.examples.reflections");
Set<Class<? extends Pet>> classes = reflections.getSubTypesOf(Pet.class);
ServiceLoader (theo câu trả lời erickson) và nó sẽ giống như sau:
ServiceLoader<Pet> loader = ServiceLoader.load(Pet.class);
for (Pet implClass : loader) {
System.out.println(implClass.getClass().getSimpleName()); // prints Dog, Cat
}
Lưu ý rằng để điều này hoạt động, bạn cần phải xác định Pet
là ServiceProviderInterface (SPI) và khai báo các triển khai của nó. bạn làm điều đó bằng cách tạo một tệp resources/META-INF/services
với tên examples.reflections.Pet
và khai báo tất cả các triển khai Pet
trong đó
examples.reflections.Dog
examples.reflections.Cat
chú thích cấp gói . đây là một ví dụ:
Package[] packages = Package.getPackages();
for (Package p : packages) {
MyPackageAnnotation annotation = p.getAnnotation(MyPackageAnnotation.class);
if (annotation != null) {
Class<?>[] implementations = annotation.implementationsOfPet();
for (Class<?> impl : implementations) {
System.out.println(impl.getSimpleName());
}
}
}
và định nghĩa chú thích:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PACKAGE)
public @interface MyPackageAnnotation {
Class<?>[] implementationsOfPet() default {};
}
và bạn phải khai báo chú thích mức gói trong một tệp có tên package-info.java
bên trong gói đó. đây là nội dung mẫu:
@MyPackageAnnotation(implementationsOfPet = {Dog.class, Cat.class})
package examples.reflections;
Lưu ý rằng chỉ những gói được biết đến với ClassLoader tại thời điểm đó mới được tải bởi một cuộc gọi đến Package.getPackages()
.
Ngoài ra, có những cách tiếp cận khác dựa trên URLClassLoader sẽ luôn bị giới hạn đối với các lớp đã được tải, trừ khi bạn thực hiện tìm kiếm dựa trên thư mục.
Điều mà erickson đã nói, nhưng nếu bạn vẫn muốn làm điều đó thì hãy xem Reflections . Từ trang của họ:
Sử dụng Phản ánh, bạn có thể truy vấn siêu dữ liệu của mình cho:
- lấy tất cả các loại phụ của một số loại
- nhận tất cả các loại được chú thích với một số chú thích
- nhận tất cả các loại được chú thích với một số chú thích, bao gồm các thông số chú thích khớp
- lấy tất cả các phương thức được chú thích bằng một số
new Reflections("my.package").getSubTypesOf(MyInterface.class)
Nói chung, rất tốn kém để làm điều này. Để sử dụng phản xạ, lớp phải được tải. Nếu bạn muốn tải mọi lớp có sẵn trên classpath, điều đó sẽ tốn thời gian và bộ nhớ và không được khuyến khích.
Nếu muốn tránh điều này, bạn cần triển khai trình phân tích cú pháp tệp lớp của riêng mình hoạt động hiệu quả hơn, thay vì phản chiếu. Thư viện kỹ thuật mã byte có thể giúp thực hiện phương pháp này.
Các cơ chế cung cấp dịch vụ là phương tiện thông thường để liệt kê các hiện thực của một dịch vụ pluggable, và đã trở thành thành lập hơn với sự ra đời của dự án Jigsaw (module) trong Java 9. Sử dụng ServiceLoader
trong Java 6, hoặc thực hiện của riêng bạn trong các phiên bản trước đó. Tôi đã cung cấp một ví dụ trong một câu trả lời khác.
META-INF/services/java.sql.Driver
Spring có một cách khá đơn giản để đạt được điều này:
public interface ITask {
void doStuff();
}
@Component
public class MyTask implements ITask {
public void doStuff(){}
}
Sau đó, bạn có thể tự động tạo một danh sách các loại ITask
và Spring sẽ điền nó với tất cả các triển khai:
@Service
public class TaskService {
@Autowired
private List<ITask> tasks;
}
Cơ chế mạnh mẽ nhất để liệt kê tất cả các lớp triển khai một giao diện nhất định hiện là ClassGraph , bởi 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ả.)
try (ScanResult scanResult = new ClassGraph().whitelistPackages("x.y.z")
.enableClassInfo().scan()) {
for (ClassInfo ci : scanResult.getClassesImplementing("x.y.z.SomeInterface")) {
foundImplementingClass(ci); // Do something with the ClassInfo object
}
}
Có, bước đầu tiên là xác định "tất cả" các lớp mà bạn quan tâm. Nếu bạn đã có thông tin này, bạn có thể liệt kê qua từng thông tin và sử dụng instanceof để xác thực mối quan hệ. Bài viết liên quan ở đây: http://www.javaworld.com/javaworld/javatips/jw-javatip113.html
Những gì erikson nói là tốt nhất. Đây là một chuỗi câu hỏi và câu trả lời liên quan - http://www.velocityreviews.com/forums/t137693-find-all-implecting-classes-in-classpath.html
Thư viện Apache BCEL cho phép bạn đọc các lớp mà không cần tải chúng. Tôi tin rằng nó sẽ nhanh hơn vì bạn có thể bỏ qua bước xác minh. Một vấn đề khác khi tải tất cả các lớp bằng trình tải lớp là bạn sẽ phải chịu một tác động lớn về bộ nhớ cũng như vô tình chạy bất kỳ khối mã tĩnh nào mà bạn có thể không muốn làm.
Liên kết thư viện Apache BCEL - http://jakarta.apache.org/bcel/
Với ClassGraph, nó khá đơn giản:
Mã Groovy để tìm các triển khai của my.package.MyInterface
:
@Grab('io.github.classgraph:classgraph:4.6.18')
import io.github.classgraph.*
new ClassGraph().enableClassInfo().scan().withCloseable { scanResult ->
scanResult.getClassesImplementing('my.package.MyInterface').findAll{!it.abstract}*.name
}
scan().withCloseable { ... }
trong Groovy, hoặc sử dụng try-with-resources trong Java: github.com/classgraph/classgraph/wiki/… Ngoài ra, phần cuối cùng nên là .name
, không .className
, vì đây .getName()
là phương pháp phù hợp để lấy tên của một lớp từ một ClassInfo
đối tượng.
Một phiên bản mới của câu trả lời của @ kaybee99, nhưng giờ đây trả lại những gì người dùng yêu cầu: triển khai ...
Spring có một cách khá đơn giản để đạt được điều này:
public interface ITask {
void doStuff();
default ITask getImplementation() {
return this;
}
}
@Component
public class MyTask implements ITask {
public void doStuff(){}
}
Sau đó, bạn có thể tự động tạo một danh sách các loạiITask
và Spring sẽ điền nó với tất cả các triển khai:
@Service
public class TaskService {
@Autowired(required = false)
private List<ITask> tasks;
if ( tasks != null)
for (ITask<?> taskImpl: tasks) {
taskImpl.doStuff();
}
}
Tôi gặp phải vấn đề tương tự. Giải pháp của tôi là sử dụng phản chiếu để kiểm tra tất cả các phương thức trong một lớp ObjectFactory, loại bỏ những phương thức không phải là phương thức createXXX () trả về một phiên bản của một trong những POJO bị ràng buộc của tôi. Mỗi lớp được phát hiện như vậy sẽ được thêm vào một mảng Lớp [], mảng này sau đó được chuyển tới lệnh gọi thuyết minh JAXBContext. Điều này hoạt động tốt, chỉ cần tải lớp ObjectFactory, lớp này sắp cần thiết. Tôi chỉ cần duy trì lớp ObjectFactory, một tác vụ được thực hiện bằng tay (trong trường hợp của tôi, vì tôi đã bắt đầu với POJO và sử dụng schemagen) hoặc có thể được tạo ra khi cần bằng xjc. Dù bằng cách nào, nó vẫn hiệu quả, đơn giản và hiệu quả.