说说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'}