深入探索spring技术内幕: 剖析@Resource注解实现原理与注解注入

xiaoxiao2021-02-28  33

一、@Resource注解原理

@Resource可以标注在字段或属性的setter方法上

1.  如果指定了name属性, 那么就按name属性的名称装配; 

2. 如果没有指定name属性, 那就按照默认的名称查找依赖对象;

3. 如果按默认名称查找不到依赖对象, 那么@Resource注解就会回退到按类型装配;

① 先写一个自己的@MyResource:

[java]  view plain  copy 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

[java]  view plain  copy 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

[html]  view plain  copy <?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

[java]  view plain  copy 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();      }  }   ⑤ 测试一下

[java]  view plain  copy 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中做如下配置:

[html]  view plain  copy <?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才会按类型匹配.

[java]  view plain  copy @Resource // 配置在属性上  private PersonDao personDao;  

[java]  view plain  copy @Resource(name="personDao"// 名称通过@Resource的name属性指定  private PersonDao personDao;  

[java]  view plain  copy @Resource // 配置在setter方法上  public void setPersonDao(PersonDao personDao) {      this.personDao = personDao;   }  

@Autowired注解是按类型装配依赖对象, 默认情况下它要求依赖对象必须存在, 

如果允许null值, 可以设置required=false

如果想使用按名称装配, 可以结合@Qualifier注解一起使用

[java]  view plain  copy @Autowired @Qualifier("personDao")  private PersonDao personDao  

三、spring自动扫描和管理Bean

前面的例子都是使用xml的bean定义来配置组件, 在一个稍大的项目中, 通常会有上百个组件, 如果这些组件都采用xml的bean定义来配置, 显然会增加配置文件的体积, 查找及维护起来也不太方便. 

spring2.5为我们引入了组件自动扫描机制, 它可以在类路径下寻找标注了@Component、@Controller、@Service、@Reponsitory注解的类, 并把这些类纳入进spring容器中管理. 它的作用和在xml中使用bean节点配置组件是一样的.

① beans.xml

[html]  view plain  copy <?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 [java]  view plain  copy @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泛指组件, 当组件不好归类的时候, 我们可以使用这个注解进行标注;

版权声明: https://blog.csdn.net/zdp072/article/details/25558563

转载请注明原文地址: https://www.6miu.com/read-2625731.html

最新回复(0)