tomLuo

加油!努力!奋斗!

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

           说说spring注解

注解(annotation)其实是一种接口,通过java的反射机制相关的API来访问annotation信息。Java语言解释器会在工作时忽略这些annotation,因此在JVM中这些annotation是不会被处理的,只能通过配套的工具才能对这些annotation类型的信息进行访问和处理。 annotation的类型使用关键字@interface。它继承了java.lang.annotation.Annotation接口,而不是申明了一个interface。 Annotation类型、方法定义是独特的、受限制的。Annotation类型的方法必须申明为无参数、无异常抛出的。方法后面可以使用default和一个默认数值来申明成员的默认值,null不能作为成员的默认值。

元注解@Target,@Retention,@Documented,@Inherited

  • @Target表示该注解用于什么地方

  • @Retention表示在什么级别保存改注解信息

  • @Documented表示将此注解包含在javadoc中

  • @Inherited表示允许子类继承父类中的注解

使用注解主要是在需要使用Spring框架的时候,特别是使用SpringMVC。因为这时我们会发现它的强大之处:预处理。 注解实际上相当于一种标记,它允许你在运行时(源码、文档、类文件我们就不讨论了)动态地对拥有该标记的成员进行操作。 实现注解需要三个条件(我们讨论的是类似于Spring自动装配的高级应用):注解声明、使用注解的元素、操作使用注解元素的代码。

注解声明

src/main/java/spring_annotation/HelloAnnotation.java

package spring_annotation;

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

/**
 * User: TOM
 * Date: 2016/5/19
 * email: beauty9235@gmail.com
 * Time: 9:45
 * 注解声明
 */
@Retention(RetentionPolicy.RUNTIME) // 表示注解在运行时依然存在
@Target(ElementType.METHOD) //注释可被用于方法上
public @interface HelloAnnotation {
    String value() default "hello tomLuo!";
}

使用注解的元素

src/main/java/spring_annotation/Hello.java

package spring_annotation;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
 * User: TOM
 * Date: 2016/5/19
 * email: beauty9235@gmail.com
 * Time: 9:54
 * 注解声明
 */
public class Hello {
    // 普通的方法
    public void sayHello() {
        System.out.println("hello, kitty!");
    }

    //使用注解但没有传入参数
    @HelloAnnotation()
    public void sayHello1(String value) {
        System.out.println(value);
    }

    //使用注解并传入参数的方法
    @HelloAnnotation("hello, jim!")
    public void sayHello2(String value) {
        System.out.println(value);
    }

    /**
     * 操作使用注解元素的代码
     * 1 继承org.springframework.web.context.support.SpringBeanAutowiringSupport类
     * 2 添加@Component/@Controller等注解并(只是使用注解方式需要)
     * 在Spring配置文件里声明context:component-scan元素。
     * <p>
     * 打印结果
     * sayHello2
     * hello, jim!
     * sayHello
     * hello, kitty!
     * sayHello1
     * hello tomLuo!
     */
    public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException {
        Hello element = new Hello(); // 初始化一个实例,用于方法调用
        Method[] methods = Hello.class.getDeclaredMethods(); // 获得所有方法
        for (Method handle : methods) {

                if (handle.getName().equals("main")) continue; //这儿排除main方法
                System.out.println(handle.getName());

                HelloAnnotation annotationTmp = null;
                if ((annotationTmp = handle.getAnnotation(HelloAnnotation.class)) != null) // 检测是否使用了我们的注解
                    handle.invoke(element, annotationTmp.value()); // 如果使用了我们的注解,我们就把注解里的"paramValue"参数值作为方法参数来调用方法
                else
                    handle.invoke(element);//如果没有使用我们的注解,我们就需要使用普通的方式来调用方法了 sayHello()

        }
    }
}

操作使用注解元素的代码, 即类中的main函数

注解本身不做任何事情,只是像xml文件一样起到配置作用。注解代表的是某种业务意义,spring中@Resource注解简单解析:首先解析类的所有属性,判断属性上面是否存在这个注解,如果存在这个注解,再根据搜索规则来取得这个bean,然后通过反射注入。

spring中注解的实现原理

下面我们来实现一个service层注入dao的实例

注解声明

src/main/java/spring_annotation/TomResourceAnnotation.java

package spring_annotation;

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

/**
 * User: TOM
 * Date: 2016/5/19
 * email: beauty9235@gmail.com
 * Time: 10:43
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD}) //可被用于方法和属性上
public @interface TomResourceAnnotation {
    String name() default "";
}

创建一个实体对象,这于用于存用户信息,在实际的应用中,常常对应数据库的一个表

src/main/java/spring_annotation/User.java

package spring_annotation;

import java.io.Serializable;

/**
 * User: TOM
 * Date: 2016/5/19
 * email: beauty9235@gmail.com
 * Time: 10:46
 */
public class User implements Serializable{
    private Long id;
    private String name;
    private String password;
    private String mail;

    public User() {

    }


    public User(String name, String password, String mail) {
        this.name = name;
        this.password = password;
        this.mail = mail;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getMail() {
        return mail;
    }

    public void setMail(String mail) {
        this.mail = mail;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                ", mail='" + mail + '\'' +
                '}';
    }
}

创建一个持久层接口,实际上可以不用创建

src/main/java/spring_annotation/UserDao.java

package spring_annotation;

/**
 * User: TOM
 * Date: 2016/5/19
 * email: beauty9235@gmail.com
 * Time: 10:40
 */
public interface UserDao {
    User queryUser();
}

实现持久层接口

实际应用中常和JPA,MyBatis,Ibatis打交道,这里只是返一个对象而己

src/main/java/spring_annotation/UserDaoImpl.java

package spring_annotation;

/**
 * User: TOM
 * Date: 2016/5/19
 * email: beauty9235@gmail.com
 * Time: 10:41
 */
public class UserDaoImpl implements UserDao {
    @Override
    public User queryUser() {
        //假設我們從持久層獲取了對象
        return new User("tomLuo", "789", "beauty9235@gmail.com");
    }
}

实现服务层逻辑

实际应用中是由MVC框架中调用服务层函数 本类中,创建了四个注解:字段上的注解、字段上的注解,配置name属性、set方法上的注解、set方法上的注解,配置name属性 然后分别从四个函数中分别取值

src/main/java/spring_annotation/UserService.java

package spring_annotation;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * User: TOM
 * Date: 2016/5/19
 * email: beauty9235@gmail.com
 * Time: 10:42
 */

public class UserService {
    Logger logger = LoggerFactory.getLogger(UserService.class);
    // 字段上的注解
    @TomResourceAnnotation
    public UserDao userDao1;

    // 字段上的注解,配置name属性
    @TomResourceAnnotation(name = "userDao")
    public UserDao userDao2;

    private UserDao userDao3;
    //set方法上的注解
     @TomResourceAnnotation
    public void setUserDao3(UserDao userDao3) {
        this.userDao3 = userDao3;
    }
    private UserDao userDao4;
    //set方法上的注解,带有name属性
    @TomResourceAnnotation(name = "userDao")
    public void setUserDao4(UserDao userDao4) {
        this.userDao4 = userDao4;
    }

    public User getUser1() {
        User user = userDao1.queryUser();
        return user;
    }
    public User getUser2() {
        User user = userDao2.queryUser();
        return user;
    }
    public User getUser3() {
        User user = userDao3.queryUser();
        return user;
    }
    public User getUser4() {
        User user = userDao4.queryUser();
        return user;
    }
}

创建配置依赖文件

实际应用中,我们不会这么做,而是通过包扫描,自动实现配置 src/main/resources/annotation.xml

<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans">
    <bean id = "userDao" class="spring_annotation.UserDaoImpl" />
    <bean id = "userService" class = "spring_annotation.UserService" />
</beans>

创建解析配置并管理BEAN的上下文件类

创建解析配置文件及利用反射机制实现对属性和setXXX注入,实际应用中,spring己为我们做好了这一切 src/main/java/spring_annotation/ClassPathXMLApplicationContext.java

package spring_annotation;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * User: TOM
 * Date: 2016/5/19
 * email: beauty9235@gmail.com
 * Time: 11:39
 */
public class ClassPathXMLApplicationContext {
    Logger logger = LoggerFactory.getLogger(UserService.class);

    Map<String, Object> sigletions = new HashMap<String, Object>();

    public ClassPathXMLApplicationContext(String fileName) {
        //读取配置文件中管理的bean
        this.readXML(fileName);
        //注解处理器
        this.annotationInject();
    }

    /**
     * 读取Bean配置文件
     *
     * @param xml
     * @return
     */
    @SuppressWarnings("unchecked")
    public void readXML(String xml) {
        SAXReader saxReader = new SAXReader();
        try {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            InputStream inputStream = classLoader.getResourceAsStream(xml);
            Document document = saxReader.read(inputStream);
            Element beans = document.getRootElement();
            for (Iterator<Element> beansList = beans.elementIterator();
                 beansList.hasNext(); ) {
                Element element = beansList.next();
                Attribute id = element.attribute("id");
                Attribute cls = element.attribute("class");
                //下面是完成對field、method的注入
                logger.info("{} {}", id.getText(), cls.getText());
                sigletions.put(id.getText(), Class.forName(cls.getText()).newInstance());

            }
        } catch (Exception e) {
            logger.info("读取配置文件出错....{}", e.getMessage());
        }
    }

    /**
     * 注解处理器
     * 如果注解TomResourceAnnotation配置了name属性,则根据name所指定的名称获取要注入的实例引用,
     * 如果注解TomResourceAnnotation;没有配置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(TomResourceAnnotation.class)) {
                    //获取当前注解,并判断name属性是否为空
                    TomResourceAnnotation resource = setter.getAnnotation(TomResourceAnnotation.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) {
            logger.info("set方法注解解析异常..........");
        }
    }

    /**
     * 处理在字段上的注解
     *
     * @param bean 处理的bean
     */
    public void fieldAnnotation(Object bean) {
        try {
            //获取其全部的字段描述
            Field[] fields = bean.getClass().getFields();
            for (Field f : fields) {
                logger.debug("{}", f.getName());
                if (f != null && f.isAnnotationPresent(TomResourceAnnotation.class)) {
                    TomResourceAnnotation resource = f.getAnnotation(TomResourceAnnotation.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) {
            logger.info("字段注解解析异常..........");
        }
    }

    /**
     * 获取Map中的对应的bean实例
     *
     * @param beanId
     * @return
     */
    public Object getBean(String beanId) {
        return sigletions.get(beanId);
    }
}

现在我们创建一个测试类,检查是不是达到我们的预期

src/test/java/AnnotationTest.java

import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import spring_annotation.ClassPathXMLApplicationContext;
import spring_annotation.UserService;
/**
 * User: TOM
 * Date: 2016/5/19
 * email: beauty9235@gmail.com
 * Time: 11:26
 */

public class AnnotationTest {
    Logger logger = LoggerFactory.getLogger(UserService.class);
   static UserService userService;
    @BeforeClass
    public static void init() {
        ClassPathXMLApplicationContext path = new ClassPathXMLApplicationContext("annotation.xml");
         userService =(UserService)path.getBean("userService");
    }
    @Test
    public void findUsers() {
        logger.debug("{}",userService.getUser1());
        logger.debug("{}",userService.getUser2());
        logger.debug("{}",userService.getUser3());
        logger.debug("{}",userService.getUser4());
    }


}

打印出结果

15:43:01.280 [main] INFO  spring_annotation.UserService - userDao spring_annotation.UserDaoImpl
15:43:01.286 [main] INFO  spring_annotation.UserService - userService spring_annotation.UserService
15:43:01.300 [main] DEBUG spring_annotation.UserService - userDao1
15:43:01.300 [main] DEBUG spring_annotation.UserService - userDao2
15:43:01.305 [main] DEBUG spring_annotation.UserService - User{id=null, name='tomLuo', password='789', mail='beauty9235@gmail.com'}
15:43:01.305 [main] DEBUG spring_annotation.UserService - User{id=null, name='tomLuo', password='789', mail='beauty9235@gmail.com'}
15:43:01.305 [main] DEBUG spring_annotation.UserService - User{id=null, name='tomLuo', password='789', mail='beauty9235@gmail.com'}
15:43:01.305 [main] DEBUG spring_annotation.UserService - User{id=null, name='tomLuo', password='789', mail='beauty9235@gmail.com'}
posted on 2016-05-19 16:20  tomLuo  阅读(648)  评论(0编辑  收藏  举报