上一篇文章介绍了如何基于XML文件去实现一个简单的IOC容器。但Spring不仅支持XML文件配置方式,还支持通过注解进行配置,本篇重点介绍如何通过注解来实现一个简单的IOC容器。
使用
首先,看看如何基于注解来进行依赖注入,代码如下:
import com.ricky.framework.ioc.bind.annotation.Inject;
import com.ricky.ioc.sample.model.Student;
import com.ricky.ioc.sample.service.StudentService;
@Bean(id="studentController")
public class StudentController {
@Inject
// @Inject(name="studentService")
private StudentService studentService;
public String find(long id){
Student stu = studentService.find(id);
System.out.println("stu:"+stu);
return "fin
}
}
StudentService接口
import com.ricky.ioc.sample.model.Student;
public interface StudentService {
public Student find(long id);
}
StudentService接口实现类
import com.ricky.framework.ioc.bind.annotation.Bean;
import com.ricky.framework.ioc.bind.annotation.Inject;
import com.ricky.ioc.sample.dao.StudentDao;
import com.ricky.ioc.sample.model.Student;
import com.ricky.ioc.sample.service.StudentService;
@Bean(id="studentService")
public class StudentServiceImpl implements StudentService {
@Inject
private StudentDao studentDao;
@Override
public Student find(long id) {
return studentDao.find(id);
}
}
以上使用了两个自定义注解:@Bean 与 @Inject。在 StudentServiceImpl 类上标注了 @Bean 注解,表示该类会交给“容器”处理,IOC容器生成的bean的id为@Bean 注解的id值。在 studentService字段上标注了 @Inject 注解,表示该字段将会被注入进来,而无需 new StudentServiceImpl (),@Inject 注解默认根据字段的名称来查找bean,同时@Inject 注解也支持通过name属性指定bean。
然后,我们需要在beans.xml配置文件中配置 自动注解 属性:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<!-- 要扫描的包 -->
<context:component-scan base-package="com.ricky.ioc.sample" />
<bean id="userDao" class="com.ricky.framework.ioc.dao.UserDaoImpl"></bean>
<bean id="userService" class="com.ricky.framework.ioc.service.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
<bean id="userController" class="com.ricky.framework.ioc.controller.UserController">
<property name="userService" ref="userService"></property>
</bean>
</beans>
到此,基于注解的IOC容器已经配置完毕了,跟Spring IOC的使用完全一致,接下来看看这个功能到底是怎样实现的吧!
实现
@Bean注解
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
@Documented
public @interface Bean { // 类似spring配置文件中的bean
String id();
}
@Inject注解
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD })
@Documented
public @interface Inject {
public String name() default "";
}
最后,我们扫描当前classpath下所有带@Bean注解的类,然后利用反射构造Bean实例对象并完成依赖注入。整个代码如下:
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.ricky.framework.ioc.bind.annotation.Bean;
import com.ricky.framework.ioc.bind.annotation.Inject;
import com.ricky.framework.ioc.util.ClassDetector;
public class AnnotationIocContainer {
protected Map<String, Object> beanInstanceMap = new HashMap<String, Object>();
public void bind(String packageName) {
initializeBean(packageName);
inject();
}
/**
* 实例化Bean
*/
private void initializeBean(String packageName) {
try {
List<Class<?>> list = new ClassDetector(packageName).detect(Bean.class);;
for (Class<?> clazz : list) {
Bean beanAnnotation = clazz.getAnnotation(Bean.class);
System.out.println("class="+clazz.getName()+",bean_id="+beanAnnotation.id());
Object bean = clazz.newInstance(); //如需通过构造函数注入,需要在此处理
beanInstanceMap.put(beanAnnotation.id(), bean);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
private void inject(){
for(String beanName : beanInstanceMap.keySet()){
Object bean = beanInstanceMap.get(beanName);
if(bean!=null){
processSetterAnnotation(bean);
processFieldAnnotation(bean);
}
}
}
/**
* 处理field上的注解
* @param bean
*/
protected void processFieldAnnotation(Object bean){
try {
Field[] fields = bean.getClass().getDeclaredFields();
for(Field field : fields){
if(field!=null && field.isAnnotationPresent(Inject.class)){
Inject resource = field.getAnnotation(Inject.class);
String name = resource.name();
Object injectBean = null;
if(name!=null&&!"".equals(name)){
injectBean = beanInstanceMap.get(name);
}else{
for(String key : beanInstanceMap.keySet()){
//判断当前属性所属的类型是否在配置文件中存在
if(field.getType().isAssignableFrom(beanInstanceMap.get(key).getClass())){
//获取类型匹配的实例对象
injectBean = beanInstanceMap.get(key);
break;
}
}
}
if(injectBean!=null){
//允许访问private字段
field.setAccessible(true);
//把引用对象注入属性
field.set(bean, injectBean);
}else{
System.out.println("field inject failed,name="+name);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 处理set方法上的注解
* @param bean
*/
protected void processSetterAnnotation(Object bean){
try {
//获取bean的属性描述器
PropertyDescriptor[] ps =
Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
for(PropertyDescriptor pd : ps){
Method setter = pd.getWriteMethod();
if(setter!=null && setter.isAnnotationPresent(Inject.class)){
//获取当前注解,并判断name属性是否为空
Inject resource = setter.getAnnotation(Inject.class);
String name = resource.name();
Object injectBean = null;
if(name!=null&&!"".equals(name)){
injectBean = beanInstanceMap.get(name);
}else{ //如果当前注解没有指定name属性,则根据类型进行匹配
for(String key : beanInstanceMap.keySet()){
if(pd.getPropertyType().isAssignableFrom(beanInstanceMap.get(key).getClass())){
injectBean = beanInstanceMap.get(key);
break;
}
}
}
if(injectBean!=null){
//允许访问private方法
setter.setAccessible(true);
//把引用对象注入属性
setter.invoke(bean, injectBean);
}else{
System.out.println("setter inject failed,name="+name);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}