## الدليل الشامل لإطار عمل Spring

Home PDF

تمت كتابة هذه المدونة بمساعدة ChatGPT-4o.


جدول المحتويات


مقدمة

Spring هو أحد أكثر الأطر شيوعًا لبناء تطبيقات على مستوى المؤسسات باستخدام لغة Java. يوفر دعمًا بنيويًا شاملاً لتطوير تطبيقات Java. في هذه المدونة، سنغطي جوانب مختلفة من نظام Spring البيئي، بما في ذلك Spring Boot، وإدارة البيانات، وبناء خدمات RESTful، والجدولة، والاختبار، والميزات المتقدمة مثل Spring Advice API.


إطار عمل Spring Boot

Spring Boot هو إطار عمل مفتوح المصدر يعتمد على لغة Java، مصمم لتبسيط عملية تطوير تطبيقات Spring. يوفر Spring Boot بيئة عمل سريعة وسهلة التكوين، مما يسمح للمطورين بإنشاء تطبيقات قائمة بذاتها بسرعة وسهولة. يتميز Spring Boot بدمج العديد من المكتبات والأدوات التي تسهل عملية التطوير، مثل Tomcat المدمج، وإدارة التبعيات عبر Maven أو Gradle، والتكوين التلقائي للتطبيقات.

المميزات الرئيسية لـ Spring Boot:

  1. سهولة التكوين: يوفر Spring Boot تكوينًا تلقائيًا للعديد من الإعدادات، مما يقلل من الحاجة إلى تكوين يدوي.
  2. تطبيقات قائمة بذاتها: يمكنك تشغيل تطبيقات Spring Boot كتطبيقات مستقلة دون الحاجة إلى خوادم خارجية.
  3. إدارة التبعيات: يدعم Spring Boot إدارة التبعيات عبر Maven وGradle، مما يسهل عملية إضافة المكتبات المطلوبة.
  4. مراقبة وصيانة: يوفر Spring Boot أدوات لمراقبة وصيانة التطبيقات، مثل Actuator.
  5. تكامل مع Spring Ecosystem: يتكامل Spring Boot بسلاسة مع باقي إطار عمل Spring، مثل Spring MVC وSpring Data وSpring Security.

مثال بسيط لتطبيق Spring Boot:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class HelloWorldApplication {

    public static void main(String[] args) {
        SpringApplication.run(HelloWorldApplication.class, args);
    }

    @GetMapping("/")
    public String hello() {
        return "Hello, World!";
    }
}

في هذا المثال، يتم إنشاء تطبيق Spring Boot بسيط يعرض رسالة “Hello, World!” عند الوصول إلى المسار الرئيسي (/).

كيفية تشغيل التطبيق:

  1. قم بتنزيل المشروع أو إنشائه باستخدام Spring Initializr.
  2. افتح المشروع في بيئة التطوير الخاصة بك (مثل IntelliJ IDEA أو Eclipse).
  3. قم بتشغيل التطبيق عن طريق تنفيذ الأمر mvn spring-boot:run في سطر الأوامر.
  4. افتح المتصفح واذهب إلى http://localhost:8080 لرؤية الرسالة “Hello, World!”.

Spring Boot يعد خيارًا مثاليًا لتطوير تطبيقات الويب والخدمات المصغرة (Microservices) بسرعة وكفاءة.

البدء مع Spring Boot

Spring Boot يجعل من السهل إنشاء تطبيقات Spring قائمة بذاتها وجاهزة للإنتاج. يأخذ وجهة نظر محددة حول منصة Spring والمكتبات الخارجية، مما يسمح لك بالبدء بأقل قدر من التهيئة.

حقن التبعيات (Dependency Injection)

حقن التبعيات (Dependency Injection أو DI) هو مبدأ أساسي في إطار عمل Spring. يسمح بإنشاء مكونات ذات اقتران ضعيف، مما يجعل الكود الخاص بك أكثر نمطية وأسهل في الاختبار.

مثال على حقن الحقول:

  @Component
  public class UserService {
      
      @Autowired
      private UserRepository userRepository;
      
      // طرق الأعمال
  }

مثال على حقن المُنشئ:

  @Component
  public class UserService {
      
      private final UserRepository userRepository;
      @Autowired
      public UserService(UserRepository userRepository) {
          this.userRepository = userRepository;
      }
      
      // طرق الأعمال
  }

مثال على حقن الأسلوب (Method Injection):

  @Component
  public class UserService {
      
      private UserRepository userRepository;
      @Autowired
      public void setUserRepository(UserRepository userRepository) {
          this.userRepository = userRepository;
      }
      
      // طرق الأعمال
  }

مثال:

@Component
public class EmailValidator {
    
    public boolean isValid(String email) {
        // منطق التحقق
        return true;
    }
}

مثال:

@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
public User findUserById(Long id) {
    return userRepository.findById(id).orElse(null);
}
}

ترجمة الكود إلى العربية:

public User findUserById(Long id) {
    return userRepository.findById(id).orElse(null);
}
}

ملاحظة: الكود يبقى كما هو لأنه مكتوب بلغة برمجية (Java) ولا يتم ترجمته. الترجمة تكون فقط للنصوص التوضيحية أو الشروحات المرافقة للكود.

هذه التعليقات التوضيحية تجعل تكوين Spring الخاص بك أكثر قابلية للقراءة وإيجازًا، وتساعد إطار عمل Spring في إدارة وربط التبعيات بين الحبوب (beans) المختلفة.

الأحداث في Spring

في إطار عمل Spring، تُعتبر الأحداث (Events) وسيلة للتواصل بين مكونات التطبيق بطريقة غير مباشرة. تسمح هذه الآلية للمكونات بإرسال إشعارات حول حدوث شيء ما، بينما يمكن لمكونات أخرى الاستماع إلى هذه الإشعارات والاستجابة لها. يتم تنفيذ هذا النموذج باستخدام ApplicationEvent و ApplicationListener.

كيفية عمل الأحداث في Spring
  1. إنشاء حدث مخصص: يمكنك إنشاء حدث مخصص عن طريق توسيع الفئة ApplicationEvent.

    public class CustomEvent extends ApplicationEvent {
        private String message;
    
        public CustomEvent(Object source, String message) {
            super(source);
            this.message = message;
        }
    
        public String getMessage() {
            return message;
        }
    }
    
  2. نشر الحدث: يمكن نشر الحدث باستخدام ApplicationEventPublisher.

    @Autowired
    private ApplicationEventPublisher publisher;
    
    public void publishCustomEvent(final String message) {
        CustomEvent customEvent = new CustomEvent(this, message);
        publisher.publishEvent(customEvent);
    }
    
  3. الاستماع إلى الحدث: يمكنك الاستماع إلى الحدث عن طريق تنفيذ واجهة ApplicationListener.

    @Component
    public class CustomEventListener implements ApplicationListener<CustomEvent> {
        @Override
        public void onApplicationEvent(CustomEvent event) {
            System.out.println("Received custom event - " + event.getMessage());
        }
    }
    
استخدام الأحداث في Spring

تُستخدم الأحداث في Spring لتحقيق فصل المهام (Separation of Concerns) وتحسين قابلية الصيانة. على سبيل المثال، يمكن استخدامها لإرسال إشعارات عبر البريد الإلكتروني بعد تسجيل مستخدم جديد، أو لتحديث ذاكرة التخزين المؤقت بعد تغيير البيانات.

مثال عملي

لنفترض أن لديك تطبيقًا لإدارة المستخدمين، وتريد إرسال بريد إلكتروني ترحيبي عند تسجيل مستخدم جديد.

  1. إنشاء حدث تسجيل المستخدم:

    public class UserRegisteredEvent extends ApplicationEvent {
        private String email;
    
        public UserRegisteredEvent(Object source, String email) {
            super(source);
            this.email = email;
        }
    
        public String getEmail() {
            return email;
        }
    }
    
  2. نشر الحدث عند تسجيل المستخدم:

    @Service
    public class UserService {
        @Autowired
        private ApplicationEventPublisher publisher;
    
        public void registerUser(String email) {
            // Logic to register user
            publisher.publishEvent(new UserRegisteredEvent(this, email));
        }
    }
    
  3. الاستماع إلى الحدث وإرسال البريد الإلكتروني:

    @Component
    public class EmailService implements ApplicationListener<UserRegisteredEvent> {
        @Override
        public void onApplicationEvent(UserRegisteredEvent event) {
            sendWelcomeEmail(event.getEmail());
        }
    
        private void sendWelcomeEmail(String email) {
            // Logic to send email
            System.out.println("Sending welcome email to " + email);
        }
    }
    

بهذه الطريقة، يتم فصل منطق إرسال البريد الإلكتروني عن منطق تسجيل المستخدم، مما يجعل الكود أكثر تنظيماً وسهولة في الصيانة.

تتيح لك آلية الأحداث في Spring إنشاء الأحداث في التطبيق والاستماع إليها.

public MyCustomEvent(Object source, String message) {
    super(source);
    this.message = message;
}

تمت ترجمة الكود أعلاه إلى:

public MyCustomEvent(Object source, String message) {
    super(source);
    this.message = message;
}

في هذا الكود، يتم تعريف مُنشئ (constructor) لفئة MyCustomEvent يأخذ كمعاملات Object source و String message. يتم استدعاء المُنشئ الأساسي (super constructor) باستخدام super(source)، ثم يتم تعيين القيمة الممررة للمعامل message إلى المتغير this.message.

      public String getMessage() {
          return message;
      }
  }
@EventListener
public void handleMyCustomEvent(MyCustomEvent event) {
    System.out.println("تم استقبال حدث Spring مخصص - " + event.getMessage());
}
public void publishCustomEvent(final String message) {
    System.out.println("جاري نشر حدث مخصص. ");
    MyCustomEvent customEvent = new MyCustomEvent(this, message);
    applicationEventPublisher.publishEvent(customEvent);
}
}

إدارة البيانات باستخدام Spring

Spring Data JDBC

Spring Data JDBC هي جزء من مشروع Spring Data الذي يهدف إلى تبسيط الوصول إلى قواعد البيانات العلائقية باستخدام JDBC. يوفر Spring Data JDBC طبقة تجريدية فوق JDBC التقليدي، مما يجعل من السهل تنفيذ عمليات CRUD (إنشاء، قراءة، تحديث، حذف) دون الحاجة إلى كتابة الكثير من الكود المتكرر.

المميزات الرئيسية:
  1. تبسيط الكود: يقلل من الحاجة إلى كتابة كود JDBC تقليدي متكرر.
  2. دعم الكيانات البسيطة: يدعم الكيانات البسيطة التي تعكس جداول قاعدة البيانات.
  3. استعلامات مخصصة: يسمح بكتابة استعلامات SQL مخصصة بسهولة.
  4. تكامل مع Spring: يتكامل بسلاسة مع إطار عمل Spring، مما يسهل إدارة الكيانات والتبعيات.
مثال بسيط:
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;

@Table("users")
public class User {
    @Id
    private Long id;
    private String name;
    private String email;

    // Getters and Setters
}

import org.springframework.data.repository.CrudRepository;

public interface UserRepository extends CrudRepository<User, Long> {
    User findByEmail(String email);
}

في هذا المثال، يتم تعريف كيان User الذي يعكس جدول users في قاعدة البيانات. يتم استخدام UserRepository للوصول إلى البيانات وتنفيذ عمليات CRUD بسهولة.

الخلاصة:

Spring Data JDBC هو أداة قوية لتبسيط الوصول إلى قواعد البيانات العلائقية باستخدام JDBC، مع الحفاظ على البساطة والمرونة.

يوفر Spring Data JDBC وصولًا بسيطًا وفعالًا إلى JDBC.

Spring Data JPA

Spring Data JPA هو جزء من مشروع Spring Data الذي يهدف إلى تبسيط تطوير تطبيقات Java التي تعتمد على تقنيات الوصول إلى البيانات. يوفر Spring Data JPA طبقة تجريدية فوق JPA (Java Persistence API)، مما يسهل عملية تنفيذ عمليات الوصول إلى البيانات دون الحاجة إلى كتابة الكثير من الكود المتكرر.

الميزات الرئيسية لـ Spring Data JPA

  1. تقليل الكود المتكرر: يوفر Spring Data JPA واجهات برمجية جاهزة للاستخدام للتعامل مع الكيانات (Entities) وعمليات CRUD (إنشاء، قراءة، تحديث، حذف) دون الحاجة إلى كتابة الكود يدويًا.

  2. الاستعلامات المخصصة: يمكنك كتابة استعلامات مخصصة باستخدام لغة JPQL (Java Persistence Query Language) أو عن طريق استخدام أسماء الطرق في الواجهات البرمجية.

  3. التكامل مع Spring: Spring Data JPA يتكامل بسلاسة مع إطار عمل Spring، مما يسمح باستخدام ميزات مثل إدارة المعاملات (Transaction Management) وحقن التبعيات (Dependency Injection).

  4. الدعم للاستعلامات الديناميكية: يمكنك إنشاء استعلامات ديناميكية باستخدام Specification و Criteria API.

  5. التقسيم (Pagination) والفرز (Sorting): يوفر Spring Data JPA دعمًا مدمجًا للتقسيم والفرز، مما يسهل التعامل مع مجموعات كبيرة من البيانات.

مثال بسيط

لنفترض أن لديك كيان User وتريد تنفيذ عمليات CRUD عليه. يمكنك إنشاء واجهة برمجية تمتد JpaRepository كما يلي:

import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
    // يمكنك إضافة طرق مخصصة هنا
}

بعد ذلك، يمكنك استخدام هذه الواجهة في خدمة (Service) أو وحدة تحكم (Controller) للتعامل مع البيانات:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public List<User> getAllUsers() {
        return userRepository.findAll();
    }

    public User getUserById(Long id) {
        return userRepository.findById(id).orElse(null);
    }

    public User saveUser(User user) {
        return userRepository.save(user);
    }

    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }
}

الخلاصة

Spring Data JPA يوفر طريقة فعالة وسهلة للتعامل مع قواعد البيانات في تطبيقات Java باستخدام Spring. بفضل طبقة التجريد التي يوفرها، يمكنك التركيز على كتابة منطق الأعمال بدلاً من القلق بشأن تفاصيل تنفيذ الوصول إلى البيانات.

Spring Data JPA يجعل من السهل تنفيذ مستودعات تعتمد على JPA.

Spring Data Redis

Spring Data Redis هو جزء من مشروع Spring Data الذي يهدف إلى تبسيط الوصول إلى قواعد البيانات NoSQL، وتحديدًا Redis. Redis هو مخزن بيانات مفتوح المصدر يعتمد على الذاكرة، ويستخدم كقاعدة بيانات، وذاكرة تخزين مؤقت، ووسيط للرسائل. يوفر Spring Data Redis طبقة تجريدية تسمح للمطورين بالتفاعل مع Redis بسهولة باستخدام واجهات برمجية مألوفة في Spring.

الميزات الرئيسية

  1. التكامل السلس مع Spring: يمكن دمج Spring Data Redis بسهولة مع تطبيقات Spring الأخرى، مما يوفر تجربة تطوير متسقة.
  2. دعم للأنواع المختلفة من البيانات: يدعم Spring Data Redis أنواعًا مختلفة من البيانات مثل Strings, Hashes, Lists, Sets, و Sorted Sets.
  3. التعامل مع الاتصالات: يوفر إدارة اتصالات Redis عبر RedisConnectionFactory، مما يسهل تكوين وإدارة الاتصالات.
  4. دعم للتعامل مع الكيانات: يمكن تعيين الكيانات (Entities) إلى هياكل بيانات Redis باستخدام RedisTemplate أو Repository interfaces.
  5. دعم للاستعلامات: يوفر دعمًا للاستعلامات باستخدام معايير بسيطة، مما يسمح بالبحث عن البيانات المخزنة في Redis.

مثال بسيط

فيما يلي مثال بسيط يوضح كيفية تكوين واستخدام Spring Data Redis في تطبيق Spring Boot:

@SpringBootApplication
public class RedisExampleApplication {

    public static void main(String[] args) {
        SpringApplication.run(RedisExampleApplication.class, args);
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        return template;
    }
}

@Service
public class RedisService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public void setValue(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    public Object getValue(String key) {
        return redisTemplate.opsForValue().get(key);
    }
}

في هذا المثال، نقوم بتكوين RedisTemplate واستخدامه لتخزين واسترجاع البيانات من Redis.

الخلاصة

Spring Data Redis يوفر طريقة قوية وسهلة للتفاعل مع Redis في تطبيقات Spring. سواء كنت تقوم ببناء تطبيق جديد أو تحسين تطبيق موجود، فإن Spring Data Redis يمكن أن يساعدك على تحقيق أهدافك بفعالية.

يوفر Spring Data Redis البنية التحتية للوصول إلى البيانات المعتمدة على Redis.

public void save(String key, Object value) {
    redisTemplate.opsForValue().set(key, value);
}

الترجمة:

public void save(String key, Object value) {
    redisTemplate.opsForValue().set(key, value);
}

في هذه الدالة، يتم حفظ قيمة معينة (value) في قاعدة بيانات Redis باستخدام مفتاح محدد (key). يتم استخدام redisTemplate للوصول إلى عمليات Redis وحفظ القيمة.

  public Object find(String key) {
      return redisTemplate.opsForValue().get(key);
  }

تمت ترجمة الكود أعلاه إلى:

  public Object find(String key) {
      return redisTemplate.opsForValue().get(key);
  }

ملاحظة: الكود لم يتغير لأنه مكتوب بلغة برمجية (Java) ولا يتم ترجمته.

المعاملات ودعم DAO

يُسهل Spring إدارة المعاملات (Transactions) ودعم DAO (كائن الوصول إلى البيانات).

public User findById(Long id) {
    return jdbcTemplate.queryForObject("SELECT * FROM users WHERE id = ?", new Object[]{id}, new UserRowMapper());     
}
}

ترجمة الكود إلى العربية:

public User findById(Long id) {
    return jdbcTemplate.queryForObject("SELECT * FROM users WHERE id = ?", new Object[]{id}, new UserRowMapper());     
}
}

ملاحظة: الكود يبقى كما هو لأنه مكتوب بلغة برمجية (Java) ولا يتم ترجمته.

JDBC و ORM

JDBC (Java Database Connectivity) هي واجهة برمجة تطبيقات (API) تُستخدم في لغة Java للتفاعل مع قواعد البيانات. تسمح JDBC للتطبيقات بتنفيذ استعلامات SQL وإدارة البيانات في قواعد البيانات العلائقية. توفر JDBC مجموعة من الواجهات والفئات التي تمكن المطورين من الاتصال بقاعدة البيانات، وإرسال استعلامات SQL، ومعالجة النتائج.

ORM (Object-Relational Mapping) هي تقنية تُستخدم لتحويل البيانات بين نظام أنواع غير متوافق في لغات البرمجة الموجهة للكائنات (مثل Java) وقواعد البيانات العلائقية. تُسهل ORM على المطورين العمل مع قواعد البيانات باستخدام الكائنات بدلاً من كتابة استعلامات SQL مباشرة. من أشهر أدوات ORM في Java هي Hibernate و JPA (Java Persistence API).

الفرق بين JDBC و ORM:

مثال بسيط باستخدام JDBC:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class JdbcExample {
    public static void main(String[] args) {
        try {
            Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "password");
            Statement statement = connection.createStatement();
            ResultSet resultSet = statement.executeQuery("SELECT * FROM users");

            while (resultSet.next()) {
                System.out.println(resultSet.getString("username"));
            }

            resultSet.close();
            statement.close();
            connection.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

مثال بسيط باستخدام ORM (Hibernate):

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateExample {
    public static void main(String[] args) {
        SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
        Session session = sessionFactory.openSession();
        session.beginTransaction();

        User user = new User();
        user.setUsername("john_doe");
        session.save(user);

        session.getTransaction().commit();
        session.close();
        sessionFactory.close();
    }
}

في المثالين أعلاه، يمكنك أن ترى كيف أن ORM يُبسط عملية التفاعل مع قاعدة البيانات مقارنة بـ JDBC.

يوفر Spring دعمًا شاملاً لـ JDBC و ORM (تعيين الكائنات إلى العلاقات).

  public List<User> findAll() {
      return jdbcTemplate.query("SELECT * FROM users", new UserRowMapper());
  }

ترجمة الكود أعلاه إلى العربية:

  public List<User> findAll() {
      return jdbcTemplate.query("SELECT * FROM users", new UserRowMapper());
  }

في هذا الكود، يتم تعريف دالة findAll التي تقوم بإرجاع قائمة من الكائنات من نوع User. يتم استخدام jdbcTemplate.query لتنفيذ استعلام SQL يقوم بجلب جميع السجلات من جدول users، ويتم تحويل النتائج إلى كائنات User باستخدام UserRowMapper.


بناء خدمات RESTful

عملاء REST في Spring

يوفر Spring عدة خيارات لإنشاء عملاء REST للتفاعل مع خدمات الويب. فيما يلي بعض الأدوات الرئيسية التي يمكن استخدامها:

  1. RestTemplate:
    RestTemplate هو أداة كلاسيكية في Spring لتنفيذ طلبات HTTP. إنه متزامن (synchronous) ويوفر واجهة برمجية بسيطة لإرسال الطلبات واستقبال الاستجابات.

    RestTemplate restTemplate = new RestTemplate();
    String url = "https://api.example.com/resource";
    String response = restTemplate.getForObject(url, String.class);
    System.out.println(response);
    
  2. WebClient:
    WebClient هو بديل غير متزامن (asynchronous) لـ RestTemplate ويعتمد على Reactive Programming. إنه مناسب للتطبيقات التي تحتاج إلى التعامل مع طلبات غير متزامنة.

    WebClient webClient = WebClient.create("https://api.example.com");
    Mono<String> response = webClient.get()
        .uri("/resource")
        .retrieve()
        .bodyToMono(String.class);
    response.subscribe(System.out::println);
    
  3. RestClient:
    RestClient هو واجهة برمجية جديدة في Spring 6 تهدف إلى تبسيط إنشاء عملاء REST. إنه يوفر واجهة برمجية أكثر حداثة ومرونة.

    RestClient restClient = RestClient.create();
    String response = restClient.get()
        .uri("https://api.example.com/resource")
        .retrieve()
        .body(String.class);
    System.out.println(response);
    
  4. Feign Client:
    Feign هو مكتبة تسمح بإنشاء عملاء REST بطريقة إعلانية (declarative). يمكن استخدامها مع Spring Cloud لإنشاء عملاء REST بسهولة.

    @FeignClient(name = "exampleClient", url = "https://api.example.com")
    public interface ExampleClient {
        @GetMapping("/resource")
        String getResource();
    }
    
  5. RestEasy Client:
    RestEasy هو مكتبة أخرى يمكن استخدامها لإنشاء عملاء REST. إنها توفر واجهة برمجية قوية لتنفيذ طلبات HTTP.

    ResteasyClient client = new ResteasyClientBuilder().build();
    ResteasyWebTarget target = client.target("https://api.example.com/resource");
    String response = target.request().get(String.class);
    System.out.println(response);
    

اختيار الأداة المناسبة يعتمد على احتياجات التطبيق الخاص بك. إذا كنت تعمل في بيئة متزامنة، فقد يكون RestTemplate خيارًا جيدًا. أما إذا كنت بحاجة إلى معالجة غير متزامنة، فإن WebClient هو الخيار الأفضل.

Spring يجعل من السهل بناء عملاء RESTful.

  public String getUserInfo(String userId) {
      return restTemplate.getForObject("https://api.example.com/users/" + userId, String.class);
  }

تمت ترجمة الكود أعلاه إلى:

  public String getUserInfo(String userId) {
      return restTemplate.getForObject("https://api.example.com/users/" + userId, String.class);
  }

ملاحظة: الكود لم يتغير لأنه مكتوب بلغة برمجية (Java) ولا يتم ترجمته.

  public Mono<String> getUserInfo(String userId) {
      return webClientBuilder.build()
              .get()
              .uri("https://api.example.com/users/" + userId)
              .retrieve()
              .bodyToMono(String.class);
  }

FeignClient

FeignClient هي مكتبة تُستخدم في تطبيقات Spring Cloud لتبسيط عملية استدعاء خدمات RESTful. تُوفر واجهة برمجية سهلة الاستخدام لإنشاء عميل HTTP، مما يسمح للمطورين بالتفاعل مع خدمات الويب دون الحاجة إلى كتابة كود HTTP يدويًا. باستخدام FeignClient، يمكنك تعريف واجهة برمجية مع التعليقات التوضيحية (annotations) التي تصف كيفية استدعاء خدمة معينة، وتقوم المكتبة بإنشاء التنفيذ المناسب تلقائيًا.

مثال بسيط لاستخدام FeignClient:

@FeignClient(name = "example-service", url = "https://api.example.com")
public interface ExampleServiceClient {

    @GetMapping("/example-endpoint")
    String getExampleData();
}

في هذا المثال، يتم تعريف واجهة برمجية ExampleServiceClient التي تستخدم التعليق التوضيحي @FeignClient لتحديد اسم الخدمة وعنوان URL الخاص بها. الطريقة getExampleData تستخدم التعليق التوضيحي @GetMapping لتحديد نقطة النهاية التي سيتم استدعاؤها. عند استدعاء هذه الطريقة، سيقوم FeignClient تلقائيًا بإجراء طلب HTTP GET إلى https://api.example.com/example-endpoint وإرجاع النتيجة.

FeignClient يدعم أيضًا العديد من الميزات المتقدمة مثل التوازن التلقائي للتحميل (load balancing)، والتعامل مع الأخطاء، والتشفير، وغيرها.

Feign هو عميل خدمة ويب تصريحي.


البريد الإلكتروني، المهام، والجدولة

دعم البريد الإلكتروني

يوفر Spring دعمًا لإرسال البريد الإلكتروني.

  public void sendEmail(String to, String subject, String body) {
      SimpleMailMessage message = new SimpleMailMessage();
      message.setTo(to);
      message.setSubject(subject);
      message.setText(body);
      mailSender.send(message);
  }
  public void sendRichEmail(String to, String subject, String body, File attachment) throws MessagingException {
      MimeMessage message = mailSender.createMimeMessage();
      MimeMessageHelper helper = new MimeMessageHelper(message, true);
      helper.setTo(to);
      helper.setSubject(subject);
      helper.setText(body, true);
      helper.addAttachment(attachment.getName(), attachment);
      mailSender.send(message);
  }

تنفيذ المهام والجدولة

دعم Spring لتنفيذ المهام وجدولتها يجعل من السهل تشغيل المهام.


الاختبار في Spring

عند العمل مع إطار عمل Spring، يعد الاختبار جانبًا مهمًا لضمان جودة الكود وسلوك التطبيق كما هو متوقع. يوفر Spring مجموعة من الأدوات والمكتبات التي تسهل كتابة الاختبارات للفئات والمكونات المختلفة.

أنواع الاختبارات في Spring

  1. Unit Testing (اختبار الوحدة): يتم اختبار كل وحدة (عادةً ما تكون كلاس) بشكل منفصل عن باقي التطبيق. يمكن استخدام إطار عمل مثل JUnit لكتابة هذه الاختبارات.

  2. Integration Testing (اختبار التكامل): يتم اختبار تفاعل عدة وحدات معًا للتأكد من أنها تعمل بشكل صحيح معًا. يوفر Spring إمكانية اختبار تكامل المكونات مع بعضها البعض.

  3. End-to-End Testing (اختبار من البداية إلى النهاية): يتم اختبار التطبيق بأكمله من البداية إلى النهاية، عادةً باستخدام أدوات مثل Selenium أو Spring Boot Test.

أدوات الاختبار في Spring

مثال على اختبار وحدة باستخدام JUnit و Mockito

import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

public class MyServiceTest {

    @Mock
    private MyRepository myRepository;

    @InjectMocks
    private MyService myService;

    @BeforeEach
    public void setUp() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    public void testGetData() {
        when(myRepository.findData()).thenReturn("Mocked Data");

        String result = myService.getData();

        assertEquals("Mocked Data", result);
        verify(myRepository, times(1)).findData();
    }
}

في هذا المثال، يتم اختبار MyService باستخدام MyRepository ككائن وهمي (Mock). يتم التحقق من أن getData() يعيد القيمة المتوقعة وأن findData() يتم استدعاؤها مرة واحدة فقط.

اختبار التكامل باستخدام Spring Boot Test

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;

@SpringBootTest
@AutoConfigureMockMvc
public class MyControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void testGetEndpoint() throws Exception {
        mockMvc.perform(get("/my-endpoint"))
               .andExpect(status().isOk())
               .andExpect(content().string("Expected Response"));
    }
}

في هذا المثال، يتم اختبار نقطة نهاية (endpoint) في MyController باستخدام MockMvc للتحقق من أن الاستجابة هي كما هو متوقع.

الخلاصة

الاختبار في Spring هو عملية أساسية لضمان جودة التطبيق وسلوكه الصحيح. باستخدام الأدوات المناسبة مثل JUnit و Mockito و Spring Test، يمكنك كتابة اختبارات فعالة تغطي مختلف جوانب التطبيق.

الاختبار باستخدام Mockito

Mockito هي مكتبة شائعة في عالم Java تُستخدم لإنشاء كائنات وهمية (mocks) في اختبارات الوحدة. تُسهل Mockito عملية اختبار الكود عن طريق السماح لك بمحاكاة سلوك الكائنات المعقدة أو الخارجية، مما يجعل الاختبارات أكثر تركيزًا وسرعة.

لماذا نستخدم Mockito؟
  1. عزل الكود: تسمح لك Mockito باختبار الكود بشكل معزول عن التبعيات الخارجية مثل قواعد البيانات أو خدمات الويب.
  2. التحكم في السلوك: يمكنك تحديد السلوك المتوقع للكائنات الوهمية، مما يسمح لك باختبار سيناريوهات مختلفة بسهولة.
  3. التحقق من التفاعلات: يمكنك التحقق من أن الكود الذي تقوم باختباره يتفاعل مع الكائنات الوهمية بالطريقة المتوقعة.
مثال بسيط

لنفترض أن لديك فئة UserService تعتمد على UserRepository لاسترداد بيانات المستخدم. يمكنك استخدام Mockito لإنشاء كائن وهمي لـ UserRepository واختبار UserService دون الحاجة إلى قاعدة بيانات حقيقية.

import static org.mockito.Mockito.*;
import static org.junit.Assert.*;

public class UserServiceTest {

    @Test
    public void testGetUser() {
        // إنشاء كائن وهمي لـ UserRepository
        UserRepository userRepository = mock(UserRepository.class);

        // تعريف السلوك المتوقع للكائن الوهمي
        when(userRepository.findUserById(1)).thenReturn(new User(1, "John Doe"));

        // إنشاء كائن UserService مع الكائن الوهمي
        UserService userService = new UserService(userRepository);

        // اختبار طريقة getUser
        User user = userService.getUser(1);

        // التحقق من النتيجة
        assertEquals("John Doe", user.getName());
    }
}

في هذا المثال:

الخلاصة

Mockito هي أداة قوية لاختبار الوحدة في Java، حيث تسمح لك بإنشاء كائنات وهمية والتحكم في سلوكها بسهولة. باستخدام Mockito، يمكنك عزل الكود الذي تريد اختباره والتحقق من تفاعلاته مع التبعيات الأخرى بشكل فعال.

Mockito هي مكتبة قوية لإنشاء الكائنات الوهمية (mocks) لأغراض الاختبار.

@Test
public void testFindUserById() {
    User user = new User();
    user.setId(1L);
    Mockito.when(userRepository.findById(1L)).thenReturn(Optional.of(user));
}

ترجمة:

@Test
public void testFindUserById() {
    User user = new User();
    user.setId(1L);
    Mockito.when(userRepository.findById(1L)).thenReturn(Optional.of(user));
}

ملاحظة: الكود الموجود في الكتلة البرمجية لم يتم ترجمته لأنه يحتوي على أسماء دوال ومتغيرات بالإنجليزية، وهي أمور لا يتم ترجمتها عادةً في البرمجة.

          User result = userService.findUserById(1L);
          assertNotNull(result);
          assertEquals(1L, result.getId().longValue());
      }
  }

تم ترجمة الكود أعلاه إلى:

          User result = userService.findUserById(1L);
          assertNotNull(result);
          assertEquals(1L, result.getId().longValue());
      }
  }

ملاحظة: الكود لم يتم ترجمته لأنه يحتوي على أسماء متغيرات ووظائف بالإنجليزية، وهي عادةً لا تُترجم في البرمجة.

الاختبار باستخدام MockMvc

MockMvc يتيح لك اختبار وحدات تحكم Spring MVC.

@Test
public void testGetUser() throws Exception {
    mockMvc.perform(get("/users/1"))
        .andExpect(status().isOk())
        .andExpect(content().contentType(MediaType.APPLICATION_JSON))
        .andExpect(jsonPath("$.id").value(1));
}
}

تمت ترجمة الكود أعلاه إلى:

@Test
public void testGetUser() throws Exception {
    mockMvc.perform(get("/users/1"))
        .andExpect(status().isOk())
        .andExpect(content().contentType(MediaType.APPLICATION_JSON))
        .andExpect(jsonPath("$.id").value(1));
}
}

ملاحظة: الكود لم يتغير لأنه يحتوي على أسماء وظائف ومتغيرات بالإنجليزية، وهي أسماء تقنية لا يتم ترجمتها عادةً.


المراقبة والإدارة

Spring Boot Actuator

Spring Boot Actuator هو جزء من إطار عمل Spring Boot الذي يوفر ميزات لمراقبة وإدارة تطبيقاتك. يمكنك استخدامه لجمع مقاييس مختلفة، وفحص صحة التطبيق، وعرض معلومات التكوين، وغير ذلك الكثير. يتم تفعيل هذه الميزات عن طريق إضافة التبعيات المناسبة إلى ملف pom.xml أو build.gradle، ثم تكوينها حسب الحاجة.

على سبيل المثال، لإضافة Actuator إلى مشروع Spring Boot باستخدام Maven، يمكنك إضافة التبعية التالية إلى ملف pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

وبعد ذلك، يمكنك الوصول إلى نقاط النهاية المختلفة التي يوفرها Actuator عبر HTTP. على سبيل المثال، يمكنك الوصول إلى معلومات الصحة عن طريق طلب GET إلى /actuator/health.

curl http://localhost:8080/actuator/health

سيتم إرجاع استجابة JSON تحتوي على حالة صحة التطبيق.

{
    "status": "UP"
}

يمكنك أيضًا تكوين نقاط النهاية التي تريد تفعيلها وتعطيلها عبر ملف application.properties أو application.yml. على سبيل المثال:

management.endpoints.web.exposure.include=health,info
management.endpoint.health.show-details=always

هذا التكوين سيسمح فقط بنقاط النهاية /actuator/health و /actuator/info بالظهور، وسيعرض تفاصيل الصحة دائمًا.

Spring Boot Actuator يوفر مجموعة واسعة من الميزات التي يمكن أن تساعدك في إدارة ومراقبة تطبيقاتك بفعالية.

يوفر Spring Boot Actuator ميزات جاهزة للإنتاج لمراقبة وإدارة تطبيقك.


مواضيع متقدمة

Spring Advice API

Spring Advice API هي جزء من إطار عمل Spring AOP (Aspect-Oriented Programming) الذي يُستخدم لتطبيق السلوكيات المشتركة عبر تطبيقات Spring. الـ Advice هو الإجراء الذي يتم تنفيذه عند نقطة معينة في تنفيذ البرنامج، مثل قبل أو بعد استدعاء طريقة معينة.

هناك عدة أنواع من الـ Advice في Spring:

  1. Before Advice: يتم تنفيذه قبل استدعاء طريقة معينة.
  2. After Returning Advice: يتم تنفيذه بعد استدعاء طريقة معينة بنجاح (بدون استثناءات).
  3. After Throwing Advice: يتم تنفيذه إذا تم رمي استثناء أثناء استدعاء طريقة معينة.
  4. After (Finally) Advice: يتم تنفيذه بعد استدعاء طريقة معينة بغض النظر عن النتيجة (سواء تم بنجاح أو مع استثناء).
  5. Around Advice: يتم تنفيذه حول استدعاء طريقة معينة، مما يسمح بالتحكم في تنفيذ الطريقة بشكل كامل.

مثال على استخدام Before Advice

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {

    @Before("execution(* com.example.service.*.*(..))")
    public void logBefore() {
        System.out.println("Method is about to be called.");
    }
}

في هذا المثال، يتم تنفيذ الـ logBefore قبل استدعاء أي طريقة في الحزمة com.example.service.

مثال على استخدام Around Advice

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class TimingAspect {

    @Around("execution(* com.example.service.*.*(..))")
    public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long endTime = System.currentTimeMillis();
        System.out.println("Method execution time: " + (endTime - startTime) + "ms");
        return result;
    }
}

في هذا المثال، يتم قياس وقت تنفيذ أي طريقة في الحزمة com.example.service وإخراج الوقت المستغرق.

Spring Advice API يوفر مرونة كبيرة في إدارة السلوكيات المشتركة عبر التطبيق، مما يجعل الكود أكثر نظافة وسهولة في الصيانة.

واجهة برمجة التطبيقات (API) الخاصة بـ Spring تُوفر إمكانيات متقدمة للبرمجة الموجهة نحو الجوانب (AOP).

@After("execution(* com.example.service.*.*(..))")
public void logAfter(JoinPoint joinPoint) {
    System.out.println("بعد تنفيذ الدالة: " + joinPoint.getSignature().getName());
}

الخلاصة

Spring هو إطار عمل قوي ومتعدد الاستخدامات يمكنه تبسيط تطوير التطبيقات على مستوى المؤسسات. من خلال الاستفادة من ميزات Spring Boot وSpring Data وSpring REST وغيرها من مشاريع Spring، يمكن للمطورين بناء تطبيقات قوية وقابلة للتوسع وسهلة الصيانة بكفاءة. مع إضافة أدوات مثل Spring Boot Actuator وأطر اختبار، يمكنك التأكد من أن تطبيقاتك جاهزة للإنتاج ومختَبرة بشكل جيد.


Back 2025.01.18 Donate