Rumah > Java > javaTutorial > Menggunakan anotasi dalam Java untuk membuat strategi

Menggunakan anotasi dalam Java untuk membuat strategi

Susan Sarandon
Lepaskan: 2025-01-10 12:13:43
asal
270 orang telah melayarinya

Usando annotations em Java para fazer um strategy

Saya melalui situasi yang sangat menarik di tempat kerja dan saya ingin berkongsi penyelesaiannya di sini.

Bayangkan anda perlu memproses satu set data. Dan untuk menangani set data ini, anda mempunyai beberapa strategi berbeza untuk ini. Contohnya, saya perlu mencipta strategi untuk cara mengambil koleksi data daripada S3, atau contoh dalam repositori setempat, atau diluluskan sebagai input.

Dan sesiapa yang akan menentukan strategi ini adalah orang yang membuat permintaan:

Saya ingin mendapatkan data dalam S3. Ambil data yang dijana pada hari X antara jam H1 dan H2, iaitu daripada klien Abóbora. Dapatkan 3000 data terakhir yang memenuhi ini.

Atau sebaliknya:

Ambil data contoh yang anda ada di sana, salin 10000 kali untuk melakukan ujian tekanan.

Atau pun:

Saya mempunyai direktori ini, anda juga mempunyai akses kepadanya. Dapatkan segala-galanya dalam direktori itu dan secara rekursif ke dalam subdirektori.

Dan juga akhirnya:

Ambil unit data ini yang terdapat dalam input dan gunakannya.

Bagaimana untuk melaksanakan?

Fikiran pertama saya ialah: "bagaimana saya boleh menentukan bentuk input saya dalam Java?"

Dan saya mencapai kesimpulan pertama, sangat penting untuk projek: "anda tahu apa? Saya tidak akan menentukan bentuk. Tambah Peta yang boleh mengendalikannya."

Selain itu, kerana saya tidak meletakkan sebarang bentuk dalam DTO, saya mempunyai kebebasan sepenuhnya untuk mencuba input.

Jadi selepas mewujudkan bukti konsep, kita sampai pada situasi: kita perlu keluar daripada tekanan POC dan beralih kepada sesuatu yang hampir dengan penggunaan sebenar.

Perkhidmatan yang saya lakukan adalah untuk mengesahkan peraturan. Pada asasnya, apabila menukar peraturan, saya perlu mengambil peraturan itu dan memadankannya dengan peristiwa yang berlaku dalam aplikasi pengeluaran. Atau, jika aplikasi telah ditukar dan tiada pepijat, jangkaan ialah keputusan untuk peraturan yang sama akan kekal sama untuk data yang sama; Sekarang, jika keputusan untuk peraturan yang sama menggunakan set data yang sama diubah... nah, itu potensi masalah.

Jadi, saya memerlukan aplikasi ini untuk menjalankan ujian belakang peraturan. Saya perlu menekan aplikasi sebenar yang menghantar data untuk penilaian dan peraturan yang dipersoalkan. Penggunaan ini agak pelbagai:

  • sahkan potensi penyelewengan apabila mengemas kini aplikasi
  • sahkan sama ada peraturan yang diubah mengekalkan tingkah laku yang sama
    • contohnya, mengoptimumkan masa pelaksanaan peraturan
  • semak sama ada perubahan dalam peraturan menjana perubahan yang dijangkakan dalam keputusan
  • sahkan bahawa perubahan dalam aplikasi sebenarnya menjadikannya lebih cekap
    • contohnya, menggunakan versi baharu GraalVM dengan JVMCI dihidupkan meningkatkan bilangan permintaan yang boleh saya buat?

Jadi, untuk itu, saya memerlukan beberapa strategi untuk asal usul peristiwa:

  • dapatkan data sebenar daripada S3
  • ambil data yang menjadi sampel dalam repositori dan salinnya beberapa kali
  • dapatkan data dari lokasi tertentu pada mesin tempatan saya

Dan saya juga memerlukan strategi yang berbeza daripada peraturan saya:

  • lulus melalui input
  • menggunakan rintisan yang berjalan pantas
  • menggunakan sampel berdasarkan peraturan pengeluaran
  • gunakan laluan ini di sini pada mesin saya

Bagaimana untuk menangani perkara ini? Baiklah, biarkan pengguna memberikan data!

API untuk Strategi

Adakah anda tahu sesuatu yang selalu menarik perhatian saya tentang json-schema? Ini di sini:

{
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "$id": "https://json-schema.org/draft/2020-12/schema",
    "$vocabulary": {
        //...
    }
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Medan ini bermula dengan $. Pada pendapat saya, mereka digunakan untuk menunjukkan metadata. Jadi mengapa tidak gunakan ini dalam input data untuk menunjukkan metadata strategi mana yang sedang digunakan?

{
    "dados": {
        "$strategy": "sample",
        "copias": 15000
    },
    //...
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Sebagai contoh, saya boleh memesan 15000 salinan data yang saya ada sebagai sampel. Atau minta beberapa perkara daripada S3, membuat pertanyaan dalam Athena:

{
    "dados": {
        "$strategy": "athena-query",
        "limit": 15000,
        "inicio": "2024-11-25",
        "fim": "2024-11-26",
        "cliente": "Abóbora"
    },
    //...
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Atau dalam laluan setempat?

{
    "dados": {
        "$strategy": "localpath",
        "cwd": "/home/jeffque/random-project-file",
        "dir": "../payloads/esses-daqui/top10-hard/"
    },
    //...
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Jadi saya boleh mewakilkan kepada pemilihan strategi di hadapan.

Semakan kod dan fasad

Pendekatan pertama saya untuk menangani strategi ialah:

public DataLoader getDataLoader(Map<String, Object> inputDados) {
    final var strategy = (String) inputDados.get("$strategy");
    return switch (strategy) {
        case "localpath" -> new LocalpathDataLoader();
        case "sample" -> new SampleDataLoader(resourcePatternResolver_spring);
        case "athena-query" -> new AthenaQueryDataLoader(athenaClient, s3Client);
        default -> new AthenaQueryDataLoader(athenaClient, s3Client);
    }
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Jadi arkitek saya bertanya dua soalan semasa semakan kod:

  • "mengapa anda membuat seketika segala-galanya dan tidak membiarkan Spring bekerja untuk anda?"
  • dia mencipta DataLoaderFacade dalam kod dan meninggalkannya separuh masak

Apa yang saya faham daripada ini? Menggunakan fasad adalah idea yang baik untuk menyerahkan pemprosesan ke sudut yang betul dan... untuk melepaskan kawalan manual?

Nah, banyak keajaiban berlaku kerana Musim Bunga. Memandangkan kami berada di rumah Java dengan kepakaran Java, mengapa tidak menggunakan Java/Spring idiomatik, bukan? Hanya kerana Saya sebagai individu mendapati beberapa perkara sukar difahami tidak semestinya ianya rumit. Jadi, mari kita menghayati dunia sihir suntikan pergantungan Java.

Mencipta objek fasad

Apa dulu:

final var dataLoader = getDataLoader(inputDados)
dataLoader.loadData(inputDados, workingPath);
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Menjadi:

{
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "$id": "https://json-schema.org/draft/2020-12/schema",
    "$vocabulary": {
        //...
    }
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Jadi lapisan pengawal saya tidak perlu mengurus ini. Biarkan ia pada fasad.

Jadi, bagaimana kita akan membuat fasad? Nah, untuk memulakan, saya perlu menyuntik semua objek ke dalamnya:

{
    "dados": {
        "$strategy": "sample",
        "copias": 15000
    },
    //...
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Ok, untuk DataLoader utama saya menulisnya sebagai @Primary sebagai tambahan kepada @Service. Selebihnya saya tulis sahaja dengan @Service.

Uji ini di sini, tetapkan getDataLoader untuk mengembalikan null hanya untuk mencuba cara Spring memanggil pembina dan... ia berfungsi. Sekarang saya perlu nota dengan metadata setiap perkhidmatan strategi yang mereka gunakan...

Bagaimana untuk melakukan ini...

Nah, lihat! Di Java kami mempunyai anotasi! Saya boleh mencipta anotasi masa jalan yang mempunyai di dalamnya strategi yang digunakan oleh komponen itu!

Jadi saya boleh mempunyai sesuatu seperti ini dalam AthenaQueryDataLoader:

{
    "dados": {
        "$strategy": "athena-query",
        "limit": 15000,
        "inicio": "2024-11-25",
        "fim": "2024-11-26",
        "cliente": "Abóbora"
    },
    //...
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Dan saya juga boleh mempunyai alias, mengapa tidak?

{
    "dados": {
        "$strategy": "localpath",
        "cwd": "/home/jeffque/random-project-file",
        "dir": "../payloads/esses-daqui/top10-hard/"
    },
    //...
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Dan tunjukkan!

Tetapi bagaimana untuk mencipta anotasi ini? Nah, saya memerlukannya untuk mempunyai atribut yang merupakan vektor rentetan (pengkompil Java sudah berurusan dengan menyediakan rentetan tunggal dan mengubahnya menjadi vektor dengan 1 kedudukan). Nilai lalai ialah nilai. Ia kelihatan seperti ini:

public DataLoader getDataLoader(Map<String, Object> inputDados) {
    final var strategy = (String) inputDados.get("$strategy");
    return switch (strategy) {
        case "localpath" -> new LocalpathDataLoader();
        case "sample" -> new SampleDataLoader(resourcePatternResolver_spring);
        case "athena-query" -> new AthenaQueryDataLoader(athenaClient, s3Client);
        default -> new AthenaQueryDataLoader(athenaClient, s3Client);
    }
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Jika medan anotasi bukan nilai, saya perlu menyatakannya dengan jelas dan itu akan kelihatan hodoh, seperti dalam anotasi EstrategiaFeia:

final var dataLoader = getDataLoader(inputDados)
dataLoader.loadData(inputDados, workingPath);
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Bunyinya tidak begitu natural pada pendapat saya.

Baiklah, memandangkan itu, kami masih memerlukan:

  • ekstrak anotasi kelas daripada objek yang diluluskan
  • buat peta rentetan anak panah kanan pemuat data (atau rentetan anak panah kanan T)

Mengekstrak anotasi dan memasang peta

Untuk mengekstrak anotasi, saya perlu mempunyai akses kepada kelas objek:

dataLoaderFacade.loadData(inputDados, workingPath);
Salin selepas log masuk
Salin selepas log masuk

Selain itu, bolehkah saya bertanya sama ada kelas ini diberi anotasi dengan anotasi seperti Strategi:

@Service // para o Spring gerenciar esse componente como um serviço
public class DataLoaderFacade implements DataLoader {

    public DataLoaderFacade(DataLoader primaryDataLoader,
                            List<DataLoader> dataLoaderWithStrategies) {
        // armazena de algum modo
    }

    @Override
    public CompletableFuture<Void> loadData(Map<String, Object> input, Path workingPath) {
        return getDataLoader(input).loadData(input, workingPath);
    }

    private DataLoader getDataLoader(Map<String, Object> input) {
        final var strategy = input.get("$strategy");
        // magia...
    }
}
Salin selepas log masuk
Salin selepas log masuk

Adakah anda ingat bahawa ia mempunyai medan nilai? Nah, medan ini mengembalikan vektor rentetan:

@Service
@Primary
@Estrategia("athena-query")
public class AthenaQueryDataLoader implements DataLoader {
    // ...
}
Salin selepas log masuk

Tunjukkan! Tetapi saya mempunyai cabaran, kerana sebelum ini saya mempunyai objek jenis T dan sekarang saya mahu memetakan objek yang sama ke dalam, baik, (T, String)[]. Dalam strim, operasi klasik yang melakukan ini ialah flatMap. Dan Java juga tidak membenarkan saya memulangkan tupel seperti itu entah dari mana, tetapi saya boleh mencipta rekod dengannya.

Ia akan kelihatan seperti ini:

{
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "$id": "https://json-schema.org/draft/2020-12/schema",
    "$vocabulary": {
        //...
    }
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Bagaimana jika terdapat objek yang tidak dianotasi dengan strategi? Adakah ia akan memberi NPE? Lebih baik tidak, mari kita menapisnya sebelum NPE:

{
    "dados": {
        "$strategy": "sample",
        "copias": 15000
    },
    //...
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Memandangkan itu, saya masih perlu menyusun peta. Dan, lihatlah: Java sudah menyediakan pengumpul untuk ini! Collector.toMap(keyMapper, valueMapper)

{
    "dados": {
        "$strategy": "athena-query",
        "limit": 15000,
        "inicio": "2024-11-25",
        "fim": "2024-11-26",
        "cliente": "Abóbora"
    },
    //...
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Setakat ini, ok. Tetapi flatMap sangat mengganggu saya. Terdapat API Java baharu yang dipanggil mapMulti, yang mempunyai potensi untuk membiak:

{
    "dados": {
        "$strategy": "localpath",
        "cwd": "/home/jeffque/random-project-file",
        "dir": "../payloads/esses-daqui/top10-hard/"
    },
    //...
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Kecantikan. Saya mendapatkannya untuk DataLoader, tetapi saya juga perlu melakukan perkara yang sama untuk RuleLoader. Atau mungkin tidak? Jika anda perasan, tiada apa-apa dalam kod ini yang khusus untuk DataLoader. Kita boleh abstrak kod ini!!

public DataLoader getDataLoader(Map<String, Object> inputDados) {
    final var strategy = (String) inputDados.get("$strategy");
    return switch (strategy) {
        case "localpath" -> new LocalpathDataLoader();
        case "sample" -> new SampleDataLoader(resourcePatternResolver_spring);
        case "athena-query" -> new AthenaQueryDataLoader(athenaClient, s3Client);
        default -> new AthenaQueryDataLoader(athenaClient, s3Client);
    }
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Di bawah fasad

Atas sebab utilitarian semata-mata, saya meletakkan algoritma ini dalam anotasi:

final var dataLoader = getDataLoader(inputDados)
dataLoader.loadData(inputDados, workingPath);
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Dan untuk fasad? Nah, kerja yang baik untuk mengatakan perkara yang sama. Saya memutuskan untuk mengabstrak ini:

dataLoaderFacade.loadData(inputDados, workingPath);
Salin selepas log masuk
Salin selepas log masuk

Dan fasad kelihatan seperti ini:

@Service // para o Spring gerenciar esse componente como um serviço
public class DataLoaderFacade implements DataLoader {

    public DataLoaderFacade(DataLoader primaryDataLoader,
                            List<DataLoader> dataLoaderWithStrategies) {
        // armazena de algum modo
    }

    @Override
    public CompletableFuture<Void> loadData(Map<String, Object> input, Path workingPath) {
        return getDataLoader(input).loadData(input, workingPath);
    }

    private DataLoader getDataLoader(Map<String, Object> input) {
        final var strategy = input.get("$strategy");
        // magia...
    }
}
Salin selepas log masuk
Salin selepas log masuk

Atas ialah kandungan terperinci Menggunakan anotasi dalam Java untuk membuat strategi. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:dev.to
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel terbaru oleh pengarang
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan