java spring注解原理_Java的注解机制——Spring自动装配的实现原理

使用注解主要是在需要使用Spring框架的时候,特别是使用SpringMVC。因为这时我们会发现它的强大之处:预处理。

注解实际上相当于一种标记,它允许你在运行时(源码、文档、类文件我们就不讨论了)动态地对拥有该标记的成员进行操作。

实现注解需要三个条件(我们讨论的是类似于Spring自动装配的高级应用):注解声明、使用注解的元素、操作使用注解元素的代码。

首先是注解声明,注解也是一种类型,我们要定义的话也需要编写代码,如下:

1 package annotation;

2

3 import java.lang.annotation.ElementType;

4 import java.lang.annotation.Retention;

5 import java.lang.annotation.RetentionPolicy;

6 import java.lang.annotation.Target;

7

8 /**

9 * 自定义注解,用来配置方法

10 *

11 * @author Johness

12 *

13 */

14 @Retention(RetentionPolicy.RUNTIME) // 表示注解在运行时依然存在

15 @Target(ElementType.METHOD) // 表示注解可以被使用于方法上

16 public @interface SayHiAnnotation {

17 String paramValue() default "johness"; // 表示我的注解需要一个参数 名为"paramValue" 默认值为"johness"

18 }

然后是使用我们注解的元素:

1 package element;

2

3 import annotation.SayHiAnnotation;

4

5 /**

6 * 要使用SayHiAnnotation的元素所在类

7 * 由于我们定义了只有方法才能使用我们的注解,我们就使用多个方法来进行测试

8 *

9 * @author Johness

10 *

11 */

12 public class SayHiEmlement {

13

14 // 普通的方法

15 public void SayHiDefault(String name){

16 System.out.println("Hi, " + name);

17 }

18

19 // 使用注解并传入参数的方法

20 @SayHiAnnotation(paramValue="Jack")

21 public void SayHiAnnotation(String name){

22 System.out.println("Hi, " + name);

23 }

24

25 // 使用注解并使用默认参数的方法

26 @SayHiAnnotation

27 public void SayHiAnnotationDefault(String name){

28 System.out.println("Hi, " + name);

29 }

30 }

最后,是我们的操作方法(值得一提的是虽然有一定的规范,但您大可不必去浪费精力,您只需要保证您的操作代码在您希望的时候执行即可):

1 package Main;

2

3 import java.lang.reflect.InvocationTargetException;

4 import java.lang.reflect.Method;

5

6 import element.SayHiEmlement;

7 import annotation.SayHiAnnotation;

8

9 public class AnnotionOperator {

10 public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException {

11 SayHiEmlement element = new SayHiEmlement(); // 初始化一个实例,用于方法调用

12 Method[] methods = SayHiEmlement.class.getDeclaredMethods(); // 获得所有方法

13

14 for (Method method : methods) {

15 SayHiAnnotation annotationTmp = null;

16 if((annotationTmp = method.getAnnotation(SayHiAnnotation.class))!=null) // 检测是否使用了我们的注解

17 method.invoke(element,annotationTmp.paramValue()); // 如果使用了我们的注解,我们就把注解里的"paramValue"参数值作为方法参数来调用方法

18 else

19 method.invoke(element, "Rose"); // 如果没有使用我们的注解,我们就需要使用普通的方式来调用方法了

20 }

21 }

22 }

结果为:Hi, Jack

Hi, johness

Hi, Rose

可以看到,注解是进行预处理的很好方式(这里的预处理和编译原理有区别)!

接下来我们看看Spring是如何使用注解机制完成自动装配的:

首先是为了让Spring为我们自动装配要进行的操作,无外乎两种:继承org.springframework.web.context.support.SpringBeanAutowiringSupport类或者添加@Component/@Controller等注解并(只是使用注解方式需要)在Spring配置文件里声明context:component-scan元素。

我说说继承方式是如何实现自动装配的,我们打开Spring源代码查看SpringBeanAutowiringSupport类。我们会发现以下语句:

1 public SpringBeanAutowiringSupport() {

2 processInjectionBasedOnCurrentContext(this);

3 }

众所周知,Java实例构造时会调用默认父类无参构造方法,Spring正是利用了这一点,让"操作元素的代码"得以执行!(我看到第一眼就震惊了!真是奇思妙想啊。果然,高手都要善于用Java来用Java)

后面的我就不就不多说了,不过还是要纠正一些人的观点:说使用注解的自动装配来完成注入也需要setter。这明显是错误的嘛!我们看Spring注解装配(继承方式)的方法调用顺序: org.springframework.web.context.support.SpringBeanAutowiringSupport#SpringBeanAutowiringSupport=>

org.springframework.web.context.support.SpringBeanAutowiringSupport#processInjectionBasedOnCurrentContext=>

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#processInjection=>

org.springframework.beans.factory.annotation.InjectionMetadata#Injection(继承,方法重写)。最后看看Injection方法的方法体:

1 /**

2 * Either this or {@link #getResourceToInject} needs to be overridden.

3 */

4 protected void inject(Object target, String requestingBeanName, PropertyValues pvs) throws Throwable {

5 if (this.isField) {

6 Field field = (Field) this.member;

7 ReflectionUtils.makeAccessible(field);

8 field.set(target, getResourceToInject(target, requestingBeanName));

9 }

10 else {

11 if (checkPropertySkipping(pvs)) {

12 return;

13 }

14 try {

15 Method method = (Method) this.member;

16 ReflectionUtils.makeAccessible(method);

17 method.invoke(target, getResourceToInject(target, requestingBeanName));

18 }

19 catch (InvocationTargetException ex) {

20 throw ex.getTargetException();

21 }

22 }

23 }

虽然不完全,但可以基本判定此种自动装配是使用了java放射机制。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值