深刻探索spring技術內幕(四): 剖析@Resource註解實現原理與註解注入

1、@Resource註解原理java

@Resource能夠標註在字段或屬性的setter方法上spring

1.  若是指定了name屬性, 那麼就按name屬性的名稱裝配; apache

2. 若是沒有指定name屬性, 那就按照默認的名稱查找依賴對象;dom

3. 若是按默認名稱查找不到依賴對象, 那麼@Resource註解就會回退到按類型裝配;測試

① 先寫一個本身的@MyResource:ui

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;

@Retention(RetentionPolicy.RUNTIME) // 指定註解保留的範圍 (運行期)
@Target({ ElementType.FIELD, ElementType.METHOD }) // 容許註解標註的位置 (屬性, 方法)
public @interface MyResource {
	public String name() default ""; // 提供name屬性
}

② Spring Bean Factory: ClassPathXMLApplicationContextthis

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.beanutils.ConvertUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;

/**
 * Spring Bean Factory
 */
public class ClassPathXMLApplicationContext {
	private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();
	private Map<String, Object> sigletons = new HashMap<String, Object>();

	public ClassPathXMLApplicationContext(String filename) {
		this.readXML(filename);
		this.instanceBeans();
		this.annotationInject();
		this.injectObject();
	}

	/**
	 * 經過註解實現注入依賴對象
	 */
	private void annotationInject() {
		for (String beanName : sigletons.keySet()) { // 循環全部的Bean對象
			Object bean = sigletons.get(beanName);
			if (bean != null) {
				try {
					// 查找屬性的setter上是否有註解
					PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors(); 
					for (PropertyDescriptor properdesc : ps) { // 循環全部屬性
						Method setter = properdesc.getWriteMethod();// 獲取屬性的setter方法
						if (setter != null && setter.isAnnotationPresent(MyResource.class)) { // 判斷MyResource註解是否存在
							MyResource resource = setter.getAnnotation(MyResource.class);
							Object injectBean = null;
							if (resource.name() != null && !"".equals(resource.name())) {
								injectBean = sigletons.get(resource.name()); // 經過MyResource註解的name屬性獲取Bean
							} else { 
								injectBean = sigletons.get(properdesc.getName());
								if (injectBean == null) { // 沒有指定name屬性, 根據屬性名稱進行尋找
									for (String key : sigletons.keySet()) {
										// 根據屬性類型進行尋找
										if (properdesc.getPropertyType().isAssignableFrom(sigletons.get(key).getClass())) { 
											injectBean = sigletons.get(key);
											break;
										}
									}
								}
							}
							setter.setAccessible(true); 
							setter.invoke(bean, injectBean);// 把引用對象注入到屬性
						}
					}
					
					// 查找字段上是否有註解
					Field[] fields = bean.getClass().getDeclaredFields(); // 取得聲明的全部字段
					for (Field field : fields) {
						if (field.isAnnotationPresent(MyResource.class)) { // 判斷字段上是否存在MyResource註解
							MyResource resource = field.getAnnotation(MyResource.class);
							Object value = null;
							if (resource.name() != null && !"".equals(resource.name())) { // 判斷是否指定name屬性
								value = sigletons.get(resource.name());
							} else {
								value = sigletons.get(field.getName()); // 沒有指定name屬性,那麼根據字段名稱尋找
								if (value == null) {
									for (String key : sigletons.keySet()) {
										// 根據字段類型進行尋找
										if (field.getType().isAssignableFrom(sigletons.get(key).getClass())) { 
											value = sigletons.get(key);
											break;
										}
									}
								}
							}
							field.setAccessible(true);// 容許訪問private字段
							field.set(bean, value);
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 爲bean對象的屬性注入值
	 */
	private void injectObject() {
		for (BeanDefinition beanDefinition : beanDefines) {
			Object bean = sigletons.get(beanDefinition.getId());
			if (bean != null) {
				try {
					PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
					for (PropertyDefinition propertyDefinition : beanDefinition.getPropertys()) {
						for (PropertyDescriptor properdesc : ps) {
							if (propertyDefinition.getName().equals(properdesc.getName())) {
								Method setter = properdesc.getWriteMethod(); // 獲取屬性的setter方法
								if (setter != null) {
									Object injectBean = null;
									if (propertyDefinition.getRef() != null && !"".equals(propertyDefinition.getRef().trim())) {
										injectBean = sigletons.get(propertyDefinition.getRef());
									} else {
										injectBean = ConvertUtils.convert(propertyDefinition.getValue(), properdesc.getPropertyType());
									}
									setter.setAccessible(true); // private method
									setter.invoke(bean, injectBean); // 把引用對象注入到屬性
								}
								break;
							}
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 完成bean的實例化
	 */
	private void instanceBeans() {
		for (BeanDefinition beanDefinition : beanDefines) {
			try {
				if (beanDefinition.getClassName() != null && !"".equals(beanDefinition.getClassName().trim()))
					sigletons.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).newInstance());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

	}

	/**
	 * 讀取xml配置文件
	 * 
	 * @param filename
	 */
	private void readXML(String filename) {
		SAXReader saxReader = new SAXReader();
		Document document = null;
		try {
			URL xmlpath = this.getClass().getClassLoader().getResource(filename);
			document = saxReader.read(xmlpath);
			Map<String, String> nsMap = new HashMap<String, String>();
			nsMap.put("ns", "http://www.springframework.org/schema/beans");// 加入命名空間
			XPath xsub = document.createXPath("//ns:beans/ns:bean");// 建立beans/bean查詢路徑
			xsub.setNamespaceURIs(nsMap);// 設置命名空間
			List<Element> beans = xsub.selectNodes(document);// 獲取文檔下全部bean節點
			for (Element element : beans) {
				String id = element.attributeValue("id");// 獲取id屬性值
				String clazz = element.attributeValue("class"); // 獲取class屬性值
				BeanDefinition beanDefine = new BeanDefinition(id, clazz);
				XPath propertysub = element.createXPath("ns:property");
				propertysub.setNamespaceURIs(nsMap);// 設置命名空間
				List<Element> propertys = propertysub.selectNodes(element);
				for (Element property : propertys) {
					String propertyName = property.attributeValue("name");
					String propertyRef = property.attributeValue("ref");
					String propertyValue = property.attributeValue("value");
					PropertyDefinition propertyDefinition = new PropertyDefinition(propertyName, propertyRef, propertyValue);
					beanDefine.getPropertys().add(propertyDefinition);
				}
				beanDefines.add(beanDefine);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 獲取bean實例
	 * 
	 * @param beanName
	 * @return
	 */
	public Object getBean(String beanName) {
		return this.sigletons.get(beanName);
	}
}

③ beans.xmlspa

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd">
	<context:annotation-config />
	<bean id="personDao" class="com.zdp.dao.impl.PersonDaoImpl" />
	<bean id="personService" class="com.zdp.service.impl.PersonServiceImpl" />
</beans>

④ PersonServiceImpl.net

import com.zdp.dao.PersonDao;
import com.zdp.myspring.MyResource;
import com.zdp.service.PersonService;

public class PersonServiceImpl implements PersonService {
	private PersonDao personDao;
	
	@MyResource(name="personDao") 
	public void setPersonDao(PersonDao personDao) {
		this.personDao = personDao;
	}

	public void save() {
		personDao.save();
	}
}

⑤ 測試一下code

import org.junit.Test;
import com.zdp.service.PersonService;
import com.zdp.myspring.ClassPathXMLApplicationContext;
public class PersonServiceImplTest {
	@Test
	public void testSave() {
		ClassPathXMLApplicationContext ctx = new ClassPathXMLApplicationContext("beans.xml");
		PersonService personService = (PersonService)ctx.getBean("personService");
		personService.save();
	}
}

2、spring註解注入

① 引入common-annotations.jar

② 在xml中作以下配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd">
	<context:annotation-config />
</beans>

③ 在Java代碼中使用@Autowired或@Resource註解方式進行裝配

兩者區別: @Autowired默認按類型裝配, @Resource默認按名稱裝配, 當找不到與名稱匹配的Bean纔會按類型匹配.

@Resource // 配置在屬性上
private PersonDao personDao;
@Resource(name="personDao") // 名稱經過@Resource的name屬性指定
private PersonDao personDao;
@Resource // 配置在setter方法上
public void setPersonDao(PersonDao personDao) {
	this.personDao = personDao; 
}

@Autowired註解是按類型裝配依賴對象, 默認狀況下它要求依賴對象必須存在, 

若是容許null值, 能夠設置required=false

若是想使用按名稱裝配, 能夠結合@Qualifier註解一塊兒使用

@Autowired @Qualifier("personDao")
private PersonDao personDao

3、spring自動掃描和管理Bean

前面的例子都是使用xml的bean定義來配置組件, 在一個稍大的項目中, 一般會有上百個組件, 若是這些組件都採用xml的bean定義來配置, 顯然會增長配置文件的體積, 查找及維護起來也不太方便. 

spring2.5爲咱們引入了組件自動掃描機制, 它能夠在類路徑下尋找標註了@Component、@Controller、@Service、@Reponsitory註解的類, 並把這些類歸入進spring容器中管理. 它的做用和在xml中使用bean節點配置組件是同樣的.

① beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd">
           
	<context:component-scan base-package="com.zdp"/> 
	<!-- base-package爲須要掃描的包(含子包) -->
</beans>

② PersonServiceImpl

@Service("personService") 
@Scope("singleton")
public class PersonServiceImpl implements PersonService {
	private PersonDao personDao;
	
	@Resource(name="personDao") 
	public void setPersonDao(PersonDao personDao) {
		this.personDao = personDao;
	}
	
	@PostConstruct
	public void init(){
		System.out.println("init..");
	}
	
	@PreDestroy
	public void destory(){
		System.out.println("destory..");
	}
	public void save() {
		personDao.save();
	}
}

@Controller一般用於標註控制層組件(如struts中的action);

@Service一般用於標註業務層組件;

@Repository一般用於標註數據訪問組件, 即DAO組件;

@Component泛指組件, 當組件很差歸類的時候, 咱們可使用這個註解進行標註;

相關文章
相關標籤/搜索