Spring Architecture Series-7.Implementing Annotation-Driven Development Support
Table of Contents
Implementing Annotation-Driven Development Support in Spring
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/
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 {
}
Key features:
- Field-level annotation
- Runtime retention
- 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;
}
}
Features:
- Field-level dependency injection
- Reflection-based processing
- 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 "";
}
Usage example
@Controller
public class UserController {
@RequestMapping("/users")
public List<User> getUsers() {
// Implementation
}
}
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;
}
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);
}
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();
}
}
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;
}
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);
}
}
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);
}
}
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:
- Understanding annotation processing
- Dependency injection patterns
- Request mapping mechanisms
- Performance optimization techniques
This implementation demonstrates how to create a robust annotation-driven framework while maintaining simplicity and flexibility.