DEV Community

yang yaru
yang yaru

Posted on

Spring Architecture Series-7.Implementing Annotation-Driven Development Support

Introduction

Annotation-driven development has revolutionized Java development by providing a declarative way to configure and manage application components. In this article, I'll explore how to implement annotation support in a Spring-like framework, based on my miniSpring project's implementation.

Core Components

The annotation support implementation consists of several key components:

src/com/yaruyng/ ├── beans/factory/annotation/ │ ├── AutowiredAnnotationBeanPostProcessor.java │ └── Autowired.java └── web/ ├── RequestMapping.java └── method/ 
Enter fullscreen mode Exit fullscreen mode

Annotation Processing Infrastructure

1. The Autowired Annotation

The @Autowired annotation is the foundation for dependency injection:

@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Autowired { } 
Enter fullscreen mode Exit fullscreen mode

Key features:

  1. Field-level annotation
  2. Runtime retention
  3. Simple and focused purpose

2. The AutowiredAnnotationBeanPostProcessor

The processor handle @Autowired annotation processing

public class AutowiredAnnotationBeanPostProcessor implements BeanPostProcessor { private BeanFactory beanFactory; @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { Object result = bean; Class<?> clazz = bean.getClass(); Field[] fields = clazz.getDeclaredFields(); if(fields != null) { for (Field field : fields) { boolean isAutowired = field.isAnnotationPresent(Autowired.class); if(isAutowired) { String fieldName = field.getName(); Object autowiredObj = this.getBeanFactory().getBean(fieldName); try { field.setAccessible(true); field.set(bean, autowiredObj); System.out.println("autowire " + fieldName + " for bean " + beanName); } catch (IllegalArgumentException | IllegalAccessException e) { e.printStackTrace(); } } } } return result; } } 
Enter fullscreen mode Exit fullscreen mode

Features:

  1. Field-level dependency injection
  2. Reflection-based processing
  3. Integration with bean lifecycle

Web Annotations

1. RequestMapping Annotation

The @RequestMapping annotation handles URL mapping:

@Target(value = {ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface RequestMapping { String value() default ""; } 
Enter fullscreen mode Exit fullscreen mode

Usage example

@Controller public class UserController { @RequestMapping("/users") public List<User> getUsers() { // Implementation } } 
Enter fullscreen mode Exit fullscreen mode

Annotation Processing Flow

1.Bean Post-Processing

The annotation processing happens during bean initialization:

public Object postProcessBeforeInitialization(Object bean, String beanName) { // 1. Get bean class Class<?> clazz = bean.getClass(); // 2. Get declared fields Field[] fields = clazz.getDeclaredFields(); // 3. Process each field for (Field field : fields) { // 4. Check for annotations if (field.isAnnotationPresent(Autowired.class)) { // 5. Get dependency Object dependency = getBeanFactory().getBean(field.getName()); // 6. Inject dependency field.setAccessible(true); field.set(bean, dependency); } } return bean; } 
Enter fullscreen mode Exit fullscreen mode

2.Request Mapping Processing

The request mapping processing happens during request handling:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { // 1. Get handler method HandlerMethod handlerMethod = handlerMapping.getHandler(request); // 2. Execute handler ModelAndView mv = handlerAdapter.handle(request, response, handlerMethod); // 3. Render view render(request, response, mv); } 
Enter fullscreen mode Exit fullscreen mode

Implementation Details

1.Field Injection

private void injectDependency(Field field, Object bean, String beanName) { try { // 1. Get dependency name String fieldName = field.getName(); // 2. Get dependency from container Object dependency = getBeanFactory().getBean(fieldName); // 3. Make field accessible field.setAccessible(true); // 4. Set dependency field.set(bean, dependency); System.out.println("autowire " + fieldName + " for bean " + beanName); } catch (Exception e) { e.printStackTrace(); } } 
Enter fullscreen mode Exit fullscreen mode

2. Request Mapping Resolution

public HandlerMethod getHandler(HttpServletRequest request) { String requestURI = request.getRequestURI(); String method = request.getMethod(); // Find matching handler method for (HandlerMethod handler : handlerMethods) { RequestMapping mapping = handler.getMethodAnnotation(RequestMapping.class); if (mapping != null && mapping.value().equals(requestURI)) { return handler; } } return null; } 
Enter fullscreen mode Exit fullscreen mode

Usage Example

1.Dependency Injection

@Service public class UserService { @Autowired private UserRepository userRepository; @Autowired private EmailService emailService; public void createUser(User user) { userRepository.save(user); emailService.sendWelcomeEmail(user); } } 
Enter fullscreen mode Exit fullscreen mode

2.Request Mapping

@Controller @RequestMapping("/api/users") public class UserController { @Autowired private UserService userService; @RequestMapping("/{id}") public User getUser(@PathVariable Long id) { return userService.findById(id); } } 
Enter fullscreen mode Exit fullscreen mode

Key Features

1.Dependency Injection

  • Field-level injection
  • Constructor injection support
  • Circular dependency handling ### 2.Request Mapping
  • URL pattern matching
  • HTTP method support
  • Path variable handling ### 3. Annotation Processing
  • Runtime processing
  • Reflection-based implementation
  • Extensible design

Best Practices

1.Annotation Design

  • Clear and focused purpose
  • Runtime retention when needed
  • Proper target specification ### 2.Processing Implementation
  • Efficient reflection usage
  • Proper exception handling
  • Resource cleanup ### 3.Integration
  • Clean integration with IoC
  • Proper lifecycle management
  • Performance optimization

Common Challenges and Solutions

1.Circular Dependencies

  • Lazy initialization
  • Constructor injection
  • Dependency resolution ### 2.Performance
  • Annotation caching
  • Reflection optimization
  • Resource management ### 3.Error Handling
  • Clear error messages
  • Proper exception propagation
  • Recovery mechanisms

Conclusion

Implementing annotation support provides:

  • Declarative configuration
  • Clean and maintainable code
  • Flexible dependency management
  • Simplified request handling

Key takeaways:

  1. Understanding annotation processing
  2. Dependency injection patterns
  3. Request mapping mechanisms
  4. Performance optimization techniques

This implementation demonstrates how to create a robust annotation-driven framework while maintaining simplicity and flexibility.

Top comments (0)