深入探索spring技术内幕(四): 剖析@Resource注解实现原理与注解注入
一、@Resource注解原理
@Resource可以标注在字段或属性的setter方法上
1. 如果指定了name属性, 那么就按name属性的名称装配;
2. 如果没有指定name属性, 那就按照默认的名称查找依赖对象;
3. 如果按默认名称查找不到依赖对象, 那么@Resource注解就会回退到按类型装配;
① 先写一个自己的@MyResource:
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: ClassPathXMLApplicationContext
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.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 /> <bean id="personDao" class="com.zdp.dao.impl.PersonDaoImpl" /> <bean id="personService" class="com.zdp.service.impl.PersonServiceImpl" /> </beans>④ PersonServiceImpl
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(); } }⑤ 测试一下
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(); } }
二、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
三、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泛指组件, 当组件不好归类的时候, 我们可以使用这个注解进行标注;
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。