精华内容
下载资源
问答
  • Spring注解实现原理

    2020-05-06 16:45:52
  • Spring 注解实现原理

    2021-03-01 19:08:57
    Spring 注解实现原理 转自https://www.iteye.com/blog/zxf-noimp-1071765 Spring注解@Resource和@Autowired区别对比: @Autowired @Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory....

    Spring 注解实现原理

    转自https://www.iteye.com/blog/zxf-noimp-1071765

    Spring注解@Resource和@Autowired区别对比:

    1. @Autowired @Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired;只按照byType注入。
    2. @Resource
      @Resource默认按照ByName自动注入,由J2EE提供,需要导入包javax.annotation.Resource。@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。

    注:最好是将@Resource放在setter方法上,因为这样更符合面向对象的思想,通过set、get去操作属性,而不是直接去操作属性。

    spring注解原理:
    本例实现了在set方法上和在字段属性上注解的处理解析:
    1.首先自定义注解:

    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 })
    public @interface ZxfResource {
    	//注解属性name
    	public String name() default "";
    }
    

    2.使用注解的服务类

    public class UserServiceImpl {
    
    	public UserDaoImpl userDao;
    	public User1DaoImpl user1Dao;
    
    	// 字段上的注解,可以配置name属性
    	@ZxfResource
    	public User2DaoImpl user2Dao;
    
    	// set方法上的注解,带有name属性
    	@ZxfResource(name = "userDao")
    	public void setUserDao(UserDaoImpl userDao) {
    		this.userDao = userDao;
    	}
    
    	// set方法上的注解,没有配置name属性
    	@ZxfResource
    	public void setUser1Dao(User1DaoImpl user1Dao) {
    		this.user1Dao = user1Dao;
    	}
    
    	public void show() {
    		userDao.show();
    		user1Dao.show1();
    		user2Dao.show2();
    		System.out.println("这里是Service方法........");
    	}
    
    	@Override
    	public String toString() {
    		return "UserServiceImpl{" +
    				"userDao=" + userDao +
    				", user1Dao=" + user1Dao +
    				", user2Dao=" + user2Dao +
    				'}';
    	}
    }
    
    

    3.要注入到UserServiceImpl的DAO

    public class UserDaoImpl {
    	
    	String name ;
    	
    	public void show(){
    		System.out.println("这里是dao方法........");
    	}
    }
    // User1DaoImpl User2DaoImpl代码相同输出语句不同
    

    4.要解析的xml文件代码

    <?xml version="1.0" encoding="UTF-8"?>
    <beans>
        <bean id = "userDao" class="UserDaoImpl" />
        <bean id = "user1Dao" class="User1DaoImpl" />
        <bean id = "user2Dao" class="User2DaoImpl" />
        <bean id = "userService" class = "UserServiceImpl" />
    </beans>
    

    5.临时存储class信息的类

    public class BeanDefine {
    	public String id;
    	public String className;
    	public BeanDefine(String id, String className) {
    		this.id = id;
    		this.className = className;
    	}
    	public String getId() {
    		return id;
    	}
    	public void setId(String id) {
    		this.id = id;
    	}
    	public String getClassName() {
    		return className;
    	}
    	public void setClassName(String className) {
    		this.className = className;
    	}
    
    }
    

    6.注解解析类

    import java.beans.Introspector;
    import java.beans.PropertyDescriptor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Map;
    import org.apache.log4j.Logger;
    import org.dom4j.Document;
    import org.dom4j.DocumentException;
    import org.dom4j.Element;
    import org.dom4j.io.SAXReader;
    
    public class ClassPathXMLApplicationContext {
    
    	Logger log = Logger.getLogger(ClassPathXMLApplicationContext.class);
    
    	List<BeanDefine> beanList = new ArrayList<BeanDefine>();
    	Map<String, Object> sigletions = new HashMap<String, Object>();
    
    	public ClassPathXMLApplicationContext(String fileName) {
    		//读取配置文件中管理的bean
    		this.readXML(fileName);
    		//实例化xml中定义的bean
    		this.instancesBean();
    		//注解处理器 为带注解的bean注入实例
    		this.annotationInject();
    	}
    
    	/**
    	 * 读取Bean配置文件
    	 * @param fileName
    	 * @return
    	 */
    	@SuppressWarnings("unchecked")
    	public void readXML(String fileName) {
    		Document document = null;
    		SAXReader saxReader = new SAXReader();
    		try {
    			ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    			document = saxReader.read(classLoader.getResourceAsStream(fileName));
    			Element beans = document.getRootElement();
    			for (Iterator<Element> beansList = beans.elementIterator(); beansList.hasNext();) {
    				Element element = beansList.next();
    				BeanDefine bean = new BeanDefine(
    						element.attributeValue("id"),
    						element.attributeValue("class"));
    				beanList.add(bean);
    				//将xml文件中的类信息以BeanDefine的形式存储在beanList中
    			}
    		} catch (DocumentException e) {
    			log.info("读取配置文件出错....");
    		}
    	}
    	
    	/**
    	 * 实例化Bean
    	 */
    	public void instancesBean() {
    		for (BeanDefine bean : beanList) {
    			try {
    				sigletions.put(bean.getId(), Class.forName(bean.getClassName()).newInstance());
    				// 循环beanList中的元素根据其信息实例化对象
    			} catch (Exception e) {
    				log.info("实例化Bean出错...");
    			}
    		}
    	}
    	
    	/**
    	 * 注解处理器
    	 * 如果注解ZxfResource配置了name属性,则根据name所指定的名称获取要注入的实例引用,如果注解ZxfResource
    	 * 没有配置name属性,则根据属性所属类型来扫描配置文件获取要注入的实例引用
    	 * 
    	 */
    	public void annotationInject(){
    		for(String beanName:sigletions.keySet()){
    			//循环sigletions集合中的实例扫描其注解 依赖注入
    			Object bean = sigletions.get(beanName);
    			if(bean!=null){
    				this.propertyAnnotation(bean);
    				this.fieldAnnotation(bean);
    			}
    		}
    	}
    	
    	/**
    	 * 处理在set方法加入的注解
    	 * @param bean 处理的bean
    	 */
    	public void propertyAnnotation(Object bean){
    		try {
    			//获取其属性的描述
    			PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
    			for(PropertyDescriptor proderdesc : ps){
    				//获取所有set方法
    				Method setter = proderdesc.getWriteMethod();
    				//判断set方法是否定义了注解
    				if(setter!=null && setter.isAnnotationPresent(ZxfResource.class)){
    					//获取当前注解,并判断name属性是否为空
    					ZxfResource resource = setter.getAnnotation(ZxfResource.class);
    					String name ="";
    					Object value = null;
    					if(resource.name()!=null&&!"".equals(resource.name())){
    						//获取注解的name属性的内容
    						name = resource.name();
    						value = sigletions.get(name);
    					}else{ //如果当前注解没有指定name属性,则根据类型进行匹配
    						for(String key : sigletions.keySet()){
    							//判断当前属性所属的类型是否在配置文件中存在
    							if(proderdesc.getPropertyType().isAssignableFrom(sigletions.get(key).getClass())){
    								//获取类型匹配的实例对象
    								value = sigletions.get(key);
    								break;
    							}
    						}
    					}
    					//允许访问private方法
    					setter.setAccessible(true);
    					//把引用对象注入属性
    					setter.invoke(bean, value); 
    				}
    			}
    		} catch (Exception e) {
    			log.info("set方法注解解析异常..........");
    		}
    	}
    	
    	/**
    	 * 处理在字段上的注解
    	 * @param bean 处理的bean
    	 */
    	public void fieldAnnotation(Object bean){
    		try {
    			//获取其全部的字段描述
    			Field[] fields = bean.getClass().getFields();
    			for(Field f : fields){
    				if(f!=null && f.isAnnotationPresent(ZxfResource.class)){
    					ZxfResource resource = f.getAnnotation(ZxfResource.class);
    					String name ="";
    					Object value = null;
    					if(resource.name()!=null&&!"".equals(resource.name())){
    						name = resource.name();
    						value = sigletions.get(name);
    					}else{
    						for(String key : sigletions.keySet()){
    							//判断当前属性所属的类型是否在配置文件中存在
    							if(f.getType().isAssignableFrom(sigletions.get(key).getClass())){
    								//获取类型匹配的实例对象
    								value = sigletions.get(key);
    								break;
    							}
    						}
    					}
    					//允许访问private字段
    					f.setAccessible(true);
    					//把引用对象注入属性
    					f.set(bean, value);
    				}
    			}
    		} catch (Exception e) {
    			log.info("字段注解解析异常..........");
    		}
    	}
    	
    	/**
    	 * 获取Map中的对应的bean实例
    	 * @param beanId
    	 * @return
    	 */
    	public Object getBean(String beanId) {
    		return sigletions.get(beanId);
    	}
    
    
    	public static void main(String[] args) {
    		ClassPathXMLApplicationContext path = new ClassPathXMLApplicationContext(
    				"configAnnotation.xml");
    		UserServiceImpl userService =(UserServiceImpl)path.getBean("userService");
    		userService.show();
    	}
    }
    
    

    项目的完整目录:
    在这里插入图片描述
    运行过程:
    新建ClassPathXMLApplicationContext类的示例path对象传入参数xml文件路径的字符串
    调用path的getBean(“userService”)方法从Map集合sigletions中取得UserServiceImpl对象

    往上看什么时候向sigletions中加入值
    首先在ClassPathXMLApplicationContext的对象创建时其构造方法执行
    ,其构造方法中调用了三个方法分别为

    		//读取配置文件中管理的bean
    		this.readXML(fileName);
    		//实例化xml中定义的bean
    		this.instancesBean();
    		//注解处理器 为带注解的bean注入实例
    		this.annotationInject();
    

    第一个方法readXML(fileName)读取xml(将读取到的类名和CLass信息存储在List beanList中)

    	public void readXML(String fileName) {
    		Document document = null;
    		SAXReader saxReader = new SAXReader();
    		try {
    			ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    			document = saxReader.read(classLoader.getResourceAsStream(fileName));
    			Element beans = document.getRootElement();
    			for (Iterator<Element> beansList = beans.elementIterator(); beansList.hasNext();) {
    				Element element = beansList.next();
    				BeanDefine bean = new BeanDefine(
    						element.attributeValue("id"),
    						element.attributeValue("class"));
    				beanList.add(bean);
    				//将xml文件中的类信息以BeanDefine的形式存储在beanList中
    			}
    		} catch (DocumentException e) {
    			log.info("读取配置文件出错....");
    		}
    	}
    

    第二个方法instancesBean()(通过反射循环实例化xml中定义的bean(已存储到beanList中),)

    public void instancesBean() {
    		for (BeanDefine bean : beanList) {
    			try {
    				sigletions.put(bean.getId(), Class.forName(bean.getClassName()).newInstance());
    				// 循环beanList中的元素根据其信息实例化对象
    			} catch (Exception e) {
    				log.info("实例化Bean出错...");
    			}
    		}
    	}
    

    第三个方法annotationInject()(为带注解的bean注入实例)
    这个方法又调用了两个方法分别处理在set方法加入的注解和在字段上的注解

    	public void annotationInject(){
    		for(String beanName:sigletions.keySet()){
    			//循环sigletions集合中的实例扫描其注解 依赖注入
    			Object bean = sigletions.get(beanName);
    			if(bean!=null){
    				this.propertyAnnotation(bean);
    				this.fieldAnnotation(bean);
    			}
    		}
    	}
    

    set方法上的

    public void propertyAnnotation(Object bean){
    		try {
    			//获取其属性的描述
    			PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
    			for(PropertyDescriptor proderdesc : ps){
    				//获取所有set方法
    				Method setter = proderdesc.getWriteMethod();
    				//判断set方法是否定义了注解
    				if(setter!=null && setter.isAnnotationPresent(ZxfResource.class)){
    					//获取当前注解,并判断name属性是否为空
    					ZxfResource resource = setter.getAnnotation(ZxfResource.class);
    					String name ="";
    					Object value = null;
    					if(resource.name()!=null&&!"".equals(resource.name())){
    						//获取注解的name属性的内容
    						name = resource.name();
    						value = sigletions.get(name);
    					}else{ //如果当前注解没有指定name属性,则根据类型进行匹配
    						for(String key : sigletions.keySet()){
    							//判断当前属性所属的类型是否在配置文件中存在
    							if(proderdesc.getPropertyType().isAssignableFrom(sigletions.get(key).getClass())){
    								//获取类型匹配的实例对象
    								value = sigletions.get(key);
    								break;
    							}
    						}
    					}
    					//允许访问private方法
    					setter.setAccessible(true);
    					//注意这里通过反射调用set方法 把引用对象注入属性
    					setter.invoke(bean, value); 
    				}
    			}
    		} catch (Exception e) {
    			log.info("set方法注解解析异常..........");
    		}
    	}
    

    字段上的

    public void fieldAnnotation(Object bean){
    		try {
    			//获取其全部的字段描述
    			Field[] fields = bean.getClass().getFields();
    			for(Field f : fields){
    				if(f!=null && f.isAnnotationPresent(ZxfResource.class)){
    					ZxfResource resource = f.getAnnotation(ZxfResource.class);
    				//获取字段上的注解
    					String name ="";
    					Object value = null;
    					if(resource.name()!=null&&!"".equals(resource.name())){
    						name = resource.name();
    						value = sigletions.get(name);
    						//获取注解中的参数
    					}else{
    						for(String key : sigletions.keySet()){
    							//判断当前属性所属的类型是否在配置文件中存在
    							if(f.getType().isAssignableFrom(sigletions.get(key).getClass())){
    								//获取类型匹配的实例对象
    								value = sigletions.get(key);
    								break;
    							}
    						}
    					}
    					//允许访问private字段
    					f.setAccessible(true);
    					//通过反射field.set方法把引用对象注入属性
    					f.set(bean, value);
    				}
    			}
    		} catch (Exception e) {
    			log.info("字段注解解析异常..........");
    		}
    	}
    

    运行ClassPathXMLApplicationContext的main方法结果:
    在这里插入图片描述
    原作者源码已不可下载(除非有Iteye账号)不过被我放到了github上:
    源码Github地址
    此外要运行需导入demo4j的jar包以解析xml文件请自行导入我提供的资源
    demo4jJAR包
    在这里插入图片描述

    展开全文
  • spring注解实现原理

    2019-06-11 11:27:17
    spring注解实现原理 转载:https://zxf-noimp.iteye.com/blog/1071765 注解处理器ClassPathXMLApplicationContext主要方法有三个: public ClassPathXMLApplicationContext(String fileName) { //读取配置文件中管理...

    spring注解实现原理

    转载:https://zxf-noimp.iteye.com/blog/1071765
    注解处理器ClassPathXMLApplicationContext主要方法有三个:

    public ClassPathXMLApplicationContext(String fileName) {  
            //读取配置文件中管理的bean  
            this.readXML(fileName);  
            //实例化bean  
            this.instancesBean();  
            //注解处理器  
            this.annotationInject();  
        }  
    一. readXML(String  fileName);  
    1.  new ClassPathXMLApplicationContext( "configAnnotation.xml")方式获取配置文件;
    
    1. 然后遍历文件中的标签元素,得到beanList.;
    2. 通过中属性"id","class"获取关键信息,定义BeanDefine 类;
      二. instancesBean();
      遍历beaList实例化Bean
      三. annotationInject();
    • 处理在set方法加入的注解 :propertyAnnotation(Object bean)方法
    public void propertyAnnotation(Object bean){  
            try {  
                //获取其属性的描述  
                PropertyDescriptor[] ps =   
                    Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();  
                for(PropertyDescriptor proderdesc : ps){  
                    //获取所有set方法  
                    Method setter = proderdesc.getWriteMethod();  
                    //判断set方法是否定义了注解  
                    if(setter!=null && setter.isAnnotationPresent(ZxfResource.class)){  
                        //获取当前注解,并判断name属性是否为空  
                        ZxfResource resource = setter.getAnnotation(ZxfResource.class);  
                        String name ="";  
                        Object value = null;  
                        if(resource.name()!=null&&!"".equals(resource.name())){  
                            //获取注解的name属性的内容  
                            name = resource.name();  
                            value = sigletions.get(name);  
                        }else{ //如果当前注解没有指定name属性,则根据类型进行匹配  
                            for(String key : sigletions.keySet()){  
                                //判断当前属性所属的类型是否在配置文件中存在  
                                if(proderdesc.getPropertyType().isAssignableFrom(sigletions.get(key).getClass())){
    
                                    //获取类型匹配的实例对象  
                                    value = sigletions.get(key);  
                                    break;  
                                }  
                            }  在这里插入代码片
                        }  
                        //允许访问private方法  
                        setter.setAccessible(true);  
                        //把引用对象注入属性  
                        setter.invoke(bean, value);   
                    }  
                }  
            } catch (Exception e) {  
                log.info("set方法注解解析异常..........");  
            }  
        }
    
    • 处理字段上的注解:fieldAnnotation(Object bean)方法
     public void fieldAnnotation(Object bean){  
            try {  
                //获取其全部的字段描述  
                Field[] fields = bean.getClass().getFields();  
                for(Field f : fields){  
                    if(f!=null && f.isAnnotationPresent(ZxfResource.class)){  
                        ZxfResource resource = f.getAnnotation(ZxfResource.class);  
                        String name ="";  
                        Object value = null;  
                        if(resource.name()!=null&&!"".equals(resource.name())){  
                            name = resource.name();  
                            value = sigletions.get(name);  
                        }else{  
                            for(String key : sigletions.keySet()){  
                                //判断当前属性所属的类型是否在配置文件中存在  
                                if(f.getType().isAssignableFrom(sigletions.get(key).getClass())){  
                                    //获取类型匹配的实例对象  
                                    value = sigletions.get(key);  
                                    break;  
                                }  
                            }  
                        }  
                        //允许访问private字段  
                        f.setAccessible(true);  
                        //把引用对象注入属性  
                        f.set(bean, value);  
                    }  
                }  
            } catch (Exception e) {  
                log.info("字段注解解析异常..........");  
            }  
        }
    
    展开全文
  • Spring注解实现原理

    千次阅读 2020-05-10 13:25:40
    Spring注解实现原理 Spring中有哪些注解 Spring中的注解主要分为两类: 类级别的注解:如@Component、@Repository、@Controller、@Service以及JavaEE6的@ManagedBean和@Named注解,都是添加在类上面的类级别注解...

    Spring中注解实现原理

    Spring中有哪些注解

    Spring中的注解主要分为两类:

    • 类级别的注解:如@Component、@Repository、@Controller、@Service以及JavaEE6的@ManagedBean和@Named注解,都是添加在类上面的类级别注解。

    • 类内部的注解:如@Autowire、@Value、@Resource以及EJB和WebService相关的注解等,都是添加在类内部的字段或者方法上的类内部注解。

    具体的来说又有以下几种类型的注解方式:

    1.声明bean的注解

    @Component 组件,通用的注解方式

    @Service 在业务逻辑层使用(service层)

    @Repository 在数据访问层使用(dao层)

    @Controller 在表现层使用,控制器的声明(C)

    2.注入bean的注解

    @Autowired:由Spring提供

    @Inject:由JSR-330提供

    @Resource:由JSR-250提供

    都可以注解在set方法和属性上,推荐注解在属性上(一目了然,少写代码)。

    3.java配置类相关注解

    @Configuration 声明当前类为配置类,相当于xml形式的Spring配置(类上)

    @Bean 注解在方法上,声明当前方法的返回值为一个bean,替代xml中的方式(方法上)

    @Configuration 声明当前类为配置类,其中内部组合了@Component注解,表明这个类是一个bean(类上)

    @ComponentScan 用于对Component进行扫描,相当于xml中的(类上)

    @WishlyConfiguration 为@Configuration与@ComponentScan的组合注解,可以替代这两个注解

    4.切面(AOP)相关注解

    Spring支持AspectJ的注解式切面编程。

    @Aspect 声明一个切面(类上)

    使用@After、@Before、@Around定义建言(advice),可直接将拦截规则(切点)作为参数。

    @After 在方法执行之后执行(方法上) @Before 在方法执行之前执行(方法上) @Around 在方法执行之前与之后执行(方法上)

    @PointCut 声明切点

    在java配置类中使用@EnableAspectJAutoProxy注解开启Spring对AspectJ代理的支持(类上)

    5.@Bean的属性支持

    @Scope 设置Spring容器如何新建Bean实例(方法上,得有@Bean)

    其设置类型包括:

    · Singleton (单例,一个Spring容器中只有一个bean实例,默认模式),

    · Protetype (每次调用新建一个bean),

    · Request (web项目中,给每个http request新建一个bean),

    · Session (web项目中,给每个http session新建一个bean),

    · GlobalSession(给每一个 global http session新建一个Bean实例)

    @StepScope 在Spring Batch中还有涉及

    @PostConstruct 由JSR-250提供,在构造函数执行完之后执行,等价于xml配置文件中bean的initMethod

    @PreDestory 由JSR-250提供,在Bean销毁之前执行,等价于xml配置文件中bean的destroyMethod

    Spring中的注解的实现原理

    Spring中注解的实现原理需要分为类级别注解和类内部级别注解分开讨论。

    (1)类级别的注解

    Spring容器根据注解的过滤规则扫描读取注解Bean定义类,并将其注册到Spring IoC容器中。

    下面拿spring的controller来当做示例:

    ​ Controller类使用继承@Component注解的方法,将其以单例的形式放入spring容器,如果仔细看的话会发现每个注解里面都有一个默认的value()方法,它的作用是为当前的注解声明一个名字,一般默认为类名,然后spring会通过配置文件中的context:component-scan的配置,进行如下操作:

    1、使用asm技术扫描.class文件,并将包含@Component及元注解为@Component的注解@Controller、@Service、@Repository或者其他自定义的的bean注册到beanFactory中,

    2、然后spring在注册处理器

    3、实例化处理器,然后将其放到beanPostFactory中,然后我们就可以在类中进行使用了。

    4、创建bean时,会自动调用相应的处理器进行处理。

    (2)类内部级别的注解

    注解 Autowired 的简单使用

    既然要研究多个 Bean 之间的依赖注入,那么就先创建两个 Bean,分别为 ServiceA 和 ServiceB。

    ServiceA 代码如下:

    public class ServiceA {
        public String getServiceName() {
            return serviceName;
        }
        public void setServiceName(String serviceName) {
            this.serviceName = serviceName;
        }
        private String serviceName;
        public void sayHello() {
            System.out.println("serviceA sayHello " + serviceName);
        }
    } 
    

    ServiceB,代码如下:

    import org.springframework.beans.factory.annotation.Autowired;
    
    public class ServiceB {
    
        @Autowired
        private ServiceA serviceA;
    
        public ServiceA getServiceA() {
            return serviceA;
        }
        public void setServiceA(ServiceA serviceA) {
            this.serviceA = serviceA;
        }
    
        public void sayHello() {
            serviceA.sayHello();
        }
    }
    

    如上代码可知 ServiceB 内部使用注解 @Autowired 注入了 ServiceA 的实例。

    然后使用下面 bean-test.xml 文件配置两个 Bean 的实例:

    <?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"
        xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    
        <bean id="serviceA" class="com.jiaduo.test.ServiceA"></bean>
        <bean id="serviceB" class="com.jiaduo.test.ServiceB"></bean>
    
    </beans>
    

    最后测试类代码如下:

    public class TestAuowired {
        public static void main(String[] arg) {
            ClassPathXmlApplicationContext cpxa = new ClassPathXmlApplicationContext("bean-test.xml");
            cpxa.getBean("serviceB", ServiceB.class).sayHello();
        }
    }
    

    运行测试代码,我们期望输出 serviceA sayHello null,但是结果却是如下:

    log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
    Exception in thread "main" java.lang.NullPointerException
        at com.jiaduo.test.ServiceB.sayHello(ServiceB.java:11)
        at com.jiaduo.test.TestAuowired.main(TestAuowired.java:14)
    

    从异常结果知道,ServiceB 里面的 serviceA 对象为 null,也就是 XML 里面配置的 serviceA 对象并没有被注入到 ServiceB 实例内。其实要想使用 @Autowired 需要显示的注册对应的注解的处理器到 Spring 容器,具体是需要在 bean-test.xml 里面添加 ``,添加后在运行代码就会输出 serviceA sayHello null 了。

    需要注意的是 @Autowired 除了可以标注在变量上,还可以标注在变量对应的 set 访问器上,比如下面代码1和代码2效果是一样。

    代码1:

    @Autowired
    public void setServiceA(ServiceA serviceA) {
            this.serviceA = serviceA;
    }
    

    代码2:

    @Autowired
    private ServiceA serviceA;
    

    AutowiredAnnotationBeanPostProcessor 原理剖析

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5jn7llVS-1589088268941)(C:\Users\18221\AppData\Roaming\Typora\typora-user-images\image-20200510131454115.png)]

    AutowiredAnnotationBeanPostProcessor 直接或者间接实现了 Spring 框架的好多扩展接口:

    • 实现了 BeanFactoryAware 接口,可以让 AutowiredAnnotationBeanPostProcessor 获取到当前 Spring 应用程序上下文管理的 BeanFactory,从而可以获取到 BeanFactory 里面所有的 Bean。
    • 实现了 MergedBeanDefinitionPostProcessor 接口,可以让 AutowiredAnnotationBeanPostProcessor 对 BeanFactory 里面的 Bean 在被实例化前对 Bean 定义进行修改。
    • 继承了 InstantiationAwareBeanPostProcessorAdapter,可以让 AutowiredAnnotationBeanPostProcessor 在 Bean 实例化后执行属性设置。

    Spring 框架会在创建 AutowiredAnnotationBeanPostProcessor 实例过程中调用 setBeanFactory 方法注入 Spring 应用程序上下文管理的 BeanFactory 到 AutowiredAnnotationBeanPostProcessor 中,所以 AutowiredAnnotationBeanPostProcessor 就可以操作 BeanFactory 里面的所有的 Bean 了。

    在 Spring 中每个 Bean 实例化前,Spring 框架都会调用 AutowiredAnnotationBeanPostProcessor 的 postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) 方法,用来对当前 Bean 的定义(beanDefinition)进行修改,这里主要通过 findAutowiringMetadata 方法找到当前 Bean 中标注 @Autowired 注解的属性变量和方法。

    Spring实现@Autowire解析和注入的核心的类是通过AutowiredAnnotationBeanPostProcessor来实现的。

    我们可以通过其方法列表看出,其中对字段的注入,对属性的注入,还有选择相应的构造方法来注入。

    1,从构造方法的缓存中查询其构造方法

    2,若缓存中不存在,则根据反射获取所有构造方法

    3,遍历所有构造方法,查询构造器是否含有@Autowired属性

    4,判断Autowired注解中指定了required属性 (required属性就是判断是否强依依赖)若存在required就使用默认构造方法。

    5,返回指定的构造方法

    注入的时候则是通过inject方法来实现。

    Spring对注解的支持主要都是通过反射来获取相应的注解

    使用@Autowired注解来自动装配指定的bean。

    在使用@Autowired注解之前需要在Spring配置文件进行配置,<context:annotation-config />。在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean:

    如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;

    如果查询的结果不止一个,那么@Autowired会根据名称来查找;

    如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。

    @Autowired可用于:构造函数、成员变量、Setter方法

    注:@Autowired和@Resource之间的区别

    (1) @Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。

    bean装配给@Autowired指定的数据;

    如果查询的结果不止一个,那么@Autowired会根据名称来查找;

    如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。

    @Autowired可用于:构造函数、成员变量、Setter方法

    注:@Autowired和@Resource之间的区别

    (1) @Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。

    (2) @Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。

    参考原文链接:https://www.cnblogs.com/kaleidoscope/p/9766720.html

    展开全文
  • 上次聊了retryable的使用方法后没有继续研究其原理,所以使用起来还是有些心虚的,比如:重试如何实现的?有没有使用线程池?带着问题学习源码吧 启用重试功能的EnableRetry注解导入了重试所用的配置:...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,114
精华内容 845
关键字:

spring注解实现原理

spring 订阅