Spring注解的原理与自定义注解的实现

3 篇文章 0 订阅
2 篇文章 0 订阅
本文只是用于记录个人在学习annotation过程中的心德,所以知识面不太完善。
1、注解的基本概念
    Java 的annotation提供的是一种类似于注释的机制,注解本身不做任何事,好比一个配置或者说一个标记。用于 包、类型、构造方法、方法、成员变量、参数及本地变量的标记。
    程序后续可以利用java的反射机制来了解各种元素是否有何标记,针对不同的标记,作出相应的操作。

总而言之,annotation并没有那么玄乎,并不是打个标记就会帮你做什么事情,这个要做的事情还是要在后续配套的反射函数来自己实现。

2、spring注解的原理
以前看到spring的各种注解、xml配置就可以实现很多的东西,对新手而言是很难理解的,但其实只要懂了java的注解原理,这些都是很简单的。就好像spring 在xml里面配置了

以下便以一个读取xml中配置的注解例子来理解一下原理(这个例子我是照搬zxf的,这个原博主我找不到了,因为看的都是别人转载的,我觉得这个例子很好,所以就照搬过来了)
1、定义注解
package com.yulam.myannotation.annotation ;

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

/**
* @author: 5yl
* time : 2018/1/24 下午10:49
*/

// 在运行时执行
@Retention (RetentionPolicy. RUNTIME )
// 注解适用地方(字段和方法)
@Target ({ ElementType. FIELD , ElementType. METHOD })
public @ interface ZxfResource {

    //注解的name属性
    public String name () default "" ;
}

2、 带有注解的服务类
    
ublic 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方法........" ) ;
    }
 
3、要注入的类
其中2、3类我就不一一列出来了,但是会放上源码
package com.yulam.myannotation.dao ;

/**
* @author: 5yl
* time : 2018/1/24 下午10:53
*/

public class UserDaoImpl {

    String name ;

    public void show(){
        System. out.println( "这里是dao方法........") ;
    }
}
4、xml文件
<? xml version ="1.0" encoding ="UTF-8" ?>
<beans>
    <!--这里的class记得改,不然后面反射找不到 -->
    <bean id = "userDao" class ="com.yulam.myannotation.dao.UserDaoImpl" />
    <bean id = "user1Dao" class ="com.yulam.myannotation.dao.User1DaoImpl" />
    <bean id = "user2Dao" class ="com.yulam.myannotation.dao.User2DaoImpl" />
    <bean id = "userService" class = "com.yulam.myannotation.service.UserServiceImpl" />
</beans>
5、与注解配套的处理器
其中有定义一个BeanDefine,这个只是简单的pojo,只有id、className两个属性用于装载xml解析出来的bean
package com.yulam.myannotation.service ;

import com.yulam.myannotation.annotation. ZxfResource ;
import com.yulam.myannotation.pojo.BeanDefine ;
import lombok.extern.slf4j. Slf4j ;
import org.dom4j.Document ;
import org.dom4j.DocumentException ;
import org.dom4j.Element ;
import org.dom4j.io.SAXReader ;

import java.beans.Introspector ;
import java.beans.PropertyDescriptor ;
import java.lang.reflect.Field ;
import java.lang.reflect.Method ;
import java.util.* ;

/**
* @author: 5yl
* time : 2018/1/25 上午9:29
*/

@Slf4j
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) ;
        //实例化bean
        this.instancesBean() ;
        //注解处理器
        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) ;
            }
        } catch (DocumentException e) {
            log.info( "读取配置文件出错....") ;
        }
    }

    /**
     * 实例化Bean
     */
    public void instancesBean() {
        for (BeanDefine bean : beanList) {
            try {
                sigletions.put(bean.getId() ,
                        Class. forName(bean.getClassName()).newInstance()) ;
            } catch (Exception e) {
                log.info( "实例化Bean出错...") ;
            }
        }
    }

    /**
     * 注解处理器
     * 如果注解ZxfResource配置了name属性,则根据name所指定的名称获取要注入的实例引用,
     * 如果注解ZxfResource;没有配置name属性,则根据属性所属类型来扫描配置文件获取要
     * 注入的实例引用
     *
     */
    public void annotationInject(){
        for(String beanName: sigletions.keySet()){
            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) ;
    }


}

6、运行函数
package com.yulam.myannotation.service ;

import static org.junit.Assert.* ;

public class ClassPathXMLApplicationContextTest {

    public static void main(String[] args) {
        ClassPathXMLApplicationContext path = new ClassPathXMLApplicationContext(
                "configAnnotation.xml") ;
        UserServiceImpl userService =(UserServiceImpl)path.getBean( "userService") ;
        userService.show() ;
    }

}
结果:
这里是dao方法........
这里是dao1111方法........
这里是dao22222方法........
这里是Service方法........

Process finished with exit code 0

其中的原理代码中ZXF已经说的很明白了。放上源代码吧,原转载博主那里是没有可以直接运行的,对于新手来说,又一个可以直接运行的可以省很多时间。 

https://github.com/Wooyulin/annotation-test

  • 5
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值