Spring中注解实现原理

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

  • 4
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值