اختبار: 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؟
الإجابة:
- الإنشاء: إنشاء الكائن
- الحقن: حقن التبعيات
- التهيئة: طرق @PostConstruct
- الاستخدام: bean جاهز
- التدمير: طرق @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 | بحاجة إلى مراجعة |