انتقل إلى المحتوى الرئيسي

اختبار: Beans وسياق Spring

اختبر معرفتك بالـ beans وسياق التطبيق.

30 سؤالاً - انقر على كل سؤال لكشف الإجابة.


الأسئلة 1-15: Beans​

السؤال 1: ما هو bean في Spring؟

الإجابة: الـ bean هو كائن يُدار بواسطة حاوية Spring IoC:

  • يتم إنشاؤه وتكوينه بواسطة Spring
  • حقن التبعيات تلقائي
  • دورة حياة مُدارة
  • Singleton افتراضيًا

السؤال 2: كيف تعلن عن bean باستخدام تعليق توضيحي؟

الإجابة:

// باستخدام الستيريوتايب (مسح تلقائي)
@Component
public class MyService { }

@Service
public class UserService { }

@Repository
public class UserRepository { }

@Controller
public class UserController { }

السؤال 3: كيف تعلن عن bean باستخدام @Bean؟

الإجابة:

@Configuration
public class AppConfig {

@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}

@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper()
.registerModule(new JavaTimeModule());
}
}

السؤال 4: ما الفرق بين @Component و @Bean؟

الإجابة:

@Component@Bean
على فئةعلى طريقة
يتم اكتشافه تلقائيًا بواسطة المسحيُعلن عنه صراحة
الفئات التي تتحكم بهافئات الطرف الثالث
تكوين محدودتحكم كامل

السؤال 5: ما هو الاسم الافتراضي لـ bean؟

الإجابة: اسم الفئة بصيغة camelCase:

@Component
public class UserService { }
// الاسم: userService

@Bean
public RestTemplate myRestTemplate() { }
// الاسم: myRestTemplate

تخصيص:

@Component("customName")
@Bean("customName")

السؤال 6: كيف تسترجع bean باسمه؟

الإجابة:

@Autowired
private ApplicationContext context;

public void example() {
UserService service = context.getBean("userService", UserService.class);

// أو بالنوع
UserService service = context.getBean(UserService.class);
}

السؤال 7: ما هي دورة حياة bean؟

الإجابة:

  1. الإنشاء: إنشاء الكائن
  2. الحقن: حقن التبعيات
  3. التهيئة: طرق @PostConstruct
  4. الاستخدام: bean جاهز
  5. التدمير: طرق @PreDestroy

السؤال 8: كيف تنفذ كود بعد التهيئة؟

الإجابة:

@Component
public class DataLoader {

@PostConstruct
public void init() {
log.info("تم تهيئة bean");
loadData();
}
}

// أو مع @Bean
@Bean(initMethod = "init")
public MyService myService() { }

السؤال 9: كيف تنفذ كود قبل التدمير؟

الإجابة:

@Component
public class ResourceManager {

@PreDestroy
public void cleanup() {
log.info("تنظيف الموارد");
closeConnections();
}
}

// أو مع @Bean
@Bean(destroyMethod = "close")
public DataSource dataSource() { }

السؤال 10: ما هو InitializingBean و DisposableBean؟

الإجابة: واجهات بديلة لدورة الحياة:

@Component
public class MyBean implements InitializingBean, DisposableBean {

@Override
public void afterPropertiesSet() {
// بعد الحقن
}

@Override
public void destroy() {
// قبل التدمير
}
}

يفضل استخدام @PostConstruct/@PreDestroy لتقليل التداخل.


السؤال 11: كيف تنفذ ApplicationRunner؟

الإجابة:

@Component
public class StartupRunner implements ApplicationRunner {

@Override
public void run(ApplicationArguments args) {
log.info("تم بدء التطبيق");
// الوسائط: args.getOptionValues("config")
}
}

يتم تنفيذه بعد بدء السياق بالكامل.


السؤال 12: كيف تنفذ CommandLineRunner؟

الإجابة:

@Component
public class DataInitializer implements CommandLineRunner {

private final UserRepository repository;

@Override
public void run(String... args) {
repository.save(new User("Admin"));
log.info("تم تهيئة البيانات");
}
}

السؤال 13: كيف ترتب تنفيذ الـ runners؟

الإجابة:

@Component
@Order(1)
public class FirstRunner implements CommandLineRunner { }

@Component
@Order(2)
public class SecondRunner implements CommandLineRunner { }

السؤال 14: كيف تنشئ bean مشروط؟

الإجابة:

@Bean
@ConditionalOnProperty(name = "cache.enabled", havingValue = "true")
public CacheManager cacheManager() { }

@Bean
@ConditionalOnMissingBean(DataSource.class)
public DataSource defaultDataSource() { }

@Bean
@Profile("dev")
public DevService devService() { }

السؤال 15: ما هو @Lazy؟

الإجابة: يؤخر إنشاء bean حتى الوصول الأول:

@Component
@Lazy
public class HeavyService {
public HeavyService() {
// يُستدعى فقط عند الوصول الأول
}
}

// أو عند الحقن
public MyController(@Lazy HeavyService service) { }

الأسئلة 16-30: السياق والنطاقات​

السؤال 16: ما هو ApplicationContext؟

الإجابة: الحاوية المركزية لـ Spring التي:

  • تدير جميع الـ beans
  • توفر حقن التبعيات
  • تنشر الأحداث
  • تحمل الموارد
  • تدير التدويل

السؤال 17: ما هي النطاقات المتاحة للـ beans؟

الإجابة:

النطاقالوصف
singletonنسخة واحدة لكل سياق (افتراضي)
prototypeنسخة جديدة لكل طلب
requestنسخة لكل طلب HTTP
sessionنسخة لكل جلسة HTTP
applicationنسخة لكل ServletContext

السؤال 18: كيف تحدد نطاق bean؟

الإجابة:

@Component
@Scope("prototype")
public class PrototypeBean { }

@Bean
@Scope("request")
public RequestContext requestContext() { }

// أو باستخدام الثوابت
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

السؤال 19: ما المشكلة التي تحدث عند حقن prototype في singleton؟

الإجابة: يحتفظ الـ singleton دائمًا بنفس نسخة الـ prototype:

@Component  // Singleton
public class SingletonService {

private final PrototypeBean proto; // دائمًا نفس النسخة!

public SingletonService(PrototypeBean proto) {
this.proto = proto;
}
}

الحل: استخدام ObjectProvider أو @Lookup.


السؤال 20: كيف تستخدم ObjectProvider؟

الإجابة:

@Component
public class SingletonService {

private final ObjectProvider<PrototypeBean> protoProvider;

public void doWork() {
PrototypeBean proto = protoProvider.getObject(); // نسخة جديدة
}
}

السؤال 21: كيف تستخدم @Lookup؟

الإجابة:

@Component
public abstract class SingletonService {

@Lookup
protected abstract PrototypeBean getPrototype();

public void doWork() {
PrototypeBean proto = getPrototype(); // نسخة جديدة
}
}

السؤال 22: كيف تستمع لأحداث السياق؟

الإجابة:

@Component
public class ContextListener {

@EventListener
public void onContextRefreshed(ContextRefreshedEvent event) {
log.info("تم تحديث السياق");
}

@EventListener
public void onContextClosed(ContextClosedEvent event) {
log.info("تم إغلاق السياق");
}
}

السؤال 23: كيف تنشر حدثًا مخصصًا؟

الإجابة:

// الحدث
public class UserCreatedEvent extends ApplicationEvent {
private final User user;

public UserCreatedEvent(Object source, User user) {
super(source);
this.user = user;
}
}

// النشر
@Service
public class UserService {

private final ApplicationEventPublisher publisher;

public void createUser(User user) {
userRepository.save(user);
publisher.publishEvent(new UserCreatedEvent(this, user));
}
}

// الاستماع
@Component
public class UserEventListener {

@EventListener
public void onUserCreated(UserCreatedEvent event) {
log.info("تم إنشاء المستخدم: {}", event.getUser().getName());
}
}

السؤال 24: كيف تستمع لحدث بشكل غير متزامن؟

الإجابة:

@EnableAsync
@Configuration
public class AsyncConfig { }

@Component
public class AsyncListener {

@Async
@EventListener
public void onEvent(MyEvent event) {
// يُنفذ في خيط آخر
}
}

السؤال 25: كيف تصل إلى بيئة Spring؟

الإجابة:

@Component
public class ConfigService {

private final Environment env;

public ConfigService(Environment env) {
this.env = env;
}

public void showConfig() {
String property = env.getProperty("app.name");
String[] profiles = env.getActiveProfiles();
boolean isDev = env.acceptsProfiles(Profiles.of("dev"));
}
}

السؤال 26: كيف تحمل الموارد الخارجية؟

الإجابة:

@Component
public class ResourceLoader {

@Value("classpath:data/config.json")
private Resource configResource;

public void load() throws IOException {
String content = new String(configResource.getInputStream().readAllBytes());
}
}

السؤال 27: كيف تنفذ EnvironmentPostProcessor؟

الإجابة:

public class CustomEnvProcessor implements EnvironmentPostProcessor {

@Override
public void postProcessEnvironment(ConfigurableEnvironment env, SpringApplication app) {
// تعديل البيئة قبل البدء
Map<String, Object> props = new HashMap<>();
props.put("custom.property", "value");
env.getPropertySources().addFirst(new MapPropertySource("custom", props));
}
}

تسجيل في META-INF/spring.factories.


السؤال 28: كيف تستخدم ملفات تعريف Spring؟

الإجابة:

// bean نشط فقط في dev
@Component
@Profile("dev")
public class DevOnlyService { }

// bean نشط إلا في prod
@Component
@Profile("!prod")
public class NonProdService { }

// ملفات تعريف متعددة
@Component
@Profile({"dev", "test"})
public class DevTestService { }

السؤال 29: كيف تحدد beans في تكوينات منفصلة؟

الإجابة:

@Configuration
public class DatabaseConfig {
@Bean
public DataSource dataSource() { }
}

@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain() { }
}

@Configuration
@Import({DatabaseConfig.class, SecurityConfig.class})
public class AppConfig { }

السؤال 30: كيف تختبر سياق Spring؟

الإجابة:

@SpringBootTest
class ContextTest {

@Autowired
private ApplicationContext context;

@Test
void contextLoads() {
assertThat(context).isNotNull();
assertThat(context.getBean(UserService.class)).isNotNull();
}

@Test
void beanExists() {
assertThat(context.containsBean("userService")).isTrue();
}
}

النتيجة​

النتيجةالمستوى
27-30ممتاز - إتقان beans والسياق
21-26جيد - بعض المراجعات
15-20متوسط - أعد قراءة الفصل
أقل من 15بحاجة إلى مراجعة

العودة إلى الوحدة 5