springboot原理-1-spring注解发展历程

1.springboot基本认识

随着Spring的诞生,IOC 和DI的特性,方便了我们对于Bean的使用和管理,但是对于Bean的配置来说,确显得很笨重,麻烦,引入第三方组件,不得不在xml文件中配置一堆的bean, 同时,最常用的通过SpringMVC来构建一个web项目的步骤也很繁琐:
(1)创建maven项目结构
(2)添加Spring SpringMVC Servlet API等依赖
(3)创建web.xml,配置DispatcherServlet
(4)配置spring: application-context.xml
(5)创建controller,发布http请求,然后发布到jsp/Servlet容器
即使我们想发布一个很简单的项目,也必须经历上面这些步骤,对于单体项目来说没有问题,但是对于分布式架构,SOA架构,甚至微服务架构下的项目构建,这就很要命了。于是,springboot应运而生。

  • 什么是springboot?
    springboot 框架是为了能够帮助使用 spring 框架的开发者快速高效的构建一个基于 spirng 框架以及 spring 生态体系的应用解决方案。它是“约定优于配置”这个理念下的一个最佳实践。因此它是一个服务于框架的框架,服务的范围是简化配置文件。
  • springboot 约定优于配置的体现
  1. maven的目录结构: 默认resources目录存放配置文件,默认提供application.properties配置文件;默认打包方式为jar
  2. 只要引入spring-boot-starter-web的依赖,就默认包含spring mvc 相关的依赖以及内置 tomcat 容器,使得构建一个 web 应用更加简单
  3. EnableAutoConfiguration 默认对于依赖的 starter 进行自动装载(starter 启动依赖解决了不同版本的jar包管理问题,统一管理了所有依赖的jar的版本)

2.springboot快速构建web项目

在这里插入图片描述
在这里插入图片描述
添加依赖:
在这里插入图片描述
创建controller
在这里插入图片描述
运行,与SpringMVC相比,同样是通过web项目输出一个hello,springboot整个构建过程不超过5分钟,极大的简化了开发
在这里插入图片描述

3. Spring注解的发展

springboot 的奥秘,在于@SpringBootApplication 这个注解,打开 SpringBootApplication 这个注解,可以看到它实际上是一个复合注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

单纯看这些注解,你会一头雾水,为了能够知道这些注解背后到底做了什么,我们有必要知道spring各种注解的发展以及作用。

3.1 spring1.x的注解

SpringFramework1.x时代,bean的定义只能同过xml进行配置:
xml定义bean的缺点: 大量依赖xml配置文件,难以维护;举例:如果要使用一个bean, 首先要在xml中定义一个bean,通过applicationContext.getBean(“beanName”) 来获取并使用bean:

package com.gupaoedu.example;
public class HelloService {
}

applicationContext.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:task="http://www.springframework.org/schema/task"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/task
    http://www.springframework.org/schema/task/spring-task-3.2.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <bean name="helloService" class="com.gupaoedu.example.HelloService"/>
</beans>

使用bean:

package com.gupaoedu.example;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class DemoMain {
    public static void main(String[] args) {
        ApplicationContext context=new  FileSystemXmlApplicationContext("classpath:applicationContext.xml");             
        System.out.println(context.getBean(HelloService.class));
    }
}

3.2 spring2.x的注解

Spring Framework2.x时代,2.0版本在Annotation中添加了@Required、@Repository以及AOP相关的@Aspect等注解,同时也提升了XML配置能力,也就是可扩展的XML,比如Dubbo这样的开源框架就是基于Spring XML的扩展来完美的集成Spring,从而降低了Dubbo使用的门槛。
spring2.5 版本引入:
@Autowired 依赖注入
@Qualifier 依赖查找
@Component @Service
@Controller (控制层的注解)
@RequestMapping

spring2.x如何使用@Sercie/@Component注解:
(1)类上面加上@Sercie,声明这是一个service类
(2)加上注解后,如何被spring识别到并加载:
在spring的配置文件中加上包扫描:<context:component-scan base-package=“com.gupaoedu.demo02”/>
就可以让spring扫描 classpath下指定的包路径下加了@Servcie @Repository @Controller @Component 等这些注解的类,然后将他们注册成spring的bean,加载到spring容器中
代码示例:

package com.gupaoedu.demo02;
import org.springframework.stereotype.Service;
@Service   // bean的生声明
public class Demo02Service {
}

applicationContext.xml配置包扫描:

 <context:component-scan base-package="com.gupaoedu.demo02"/>

使用bean:

package com.gupaoedu.demo02;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class Demo02Main {
    public static void main(String[] args) {
        // 修改application-context.xml扫描包路径为demo02  <context:component-scan base-package="com.gupaoedu.demo02"/>
        ApplicationContext context=new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
        System.out.println(context.getBean(Demo02Service.class));
    }
}

3.3 spring3.x的注解

Spring Framework3.0是一个里程碑式的时代,它的功能特性开始出现了非常大的扩展,比如全面拥抱Java5、以及Spring Annotation。更重要的是,它提供了配置类注解@Configuration取代XML配置方式, spring3.x主要注解有:

  • @Configuration 去xml化
  • @ComponentScan 实现包的扫描,取代了applicationContext.xml配置文件中的 <context: component-scan>标签
  • @Import 导入外部配置
  • @EnableXXX 自动装配(启动一个模块)
    代码示例1:@Configuration + @ComponentScan 实现bean的定义,扫描与加载
package com.gupaoedu.demo03;
public class Demo03Service {
}

上面定义了一个普通的java类Demo03Service ,下面定义一个配置类,用来加载Demo03Service

package com.gupaoedu.demo03;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@ComponentScan("com.gupaoedu.demo03")
@Configuration  //相当于applicationContext.xml
public class SpringConfiguration {

    @Bean    // 定义bean, 相当于applicationContext.xml中的<bean>标签
    public Demo03Service demo03Service(){
        return new Demo03Service();
    }
}

基于注解配置类加载上下文,并获取bean:

package com.gupaoedu.demo03;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Demo03Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        System.out.println(context.getBean(Demo03Service.class));
    }
}

在这里插入图片描述
上面Demo03Service 还可以这样写, 加上@Service注解,那么配置类中就不需要重复定义bean了:

package com.gupaoedu.demo03;
import org.springframework.stereotype.Service;
@Service
public class Demo03Service {
}
package com.gupaoedu.demo03;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@ComponentScan("com.gupaoedu.demo03")  // 扫描com.gupaoedu.demo03包下的注解
@Configuration  //<applicationContext.xml
public class SpringConfiguration {
    /*@Bean
    public Demo03Service demo03Service(){
        return new Demo03Service();
    }*/
}

代码示例2:实现依赖注入,使用加了@Service注解的Demo03Service类,然后创建一个Demo04Service,在配置类SpringConfiguration 中定义Demo04Service,Demo04Service这个bean的定义依赖Demo03Service:

package com.gupaoedu.demo03;

public class Demo04Service {
    private Demo03Service demo03Service;

    public void setDemo03Service(Demo03Service demo03Service) {
        this.demo03Service = demo03Service;
    }

    public Demo03Service getDemo03Service() {
        return demo03Service;
    }
}
package com.gupaoedu.demo03;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@ComponentScan("com.gupaoedu.demo03")
@Configuration  //<applicationContext.xml
public class SpringConfiguration {

    @Bean
    public Demo04Service demo04Service(Demo03Service demo03Service){
        Demo04Service demo04Service = new Demo04Service();
        demo04Service.setDemo03Service(demo03Service);
        return demo04Service;
    }
}
package com.gupaoedu.demo03;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Demo04Main {

    public static void main(String[] args) {
        ApplicationContext applicationContext=new
                AnnotationConfigApplicationContext(SpringConfiguration.class);
        System.out.println(applicationContext.getBean(Demo04Service.class));
    }
}

代码示例3: @Import注解导入外部配置

package com.gupaoedu.demo04;
public class ImportService {
}

定义配置类:

package com.gupaoedu.demo04;
import org.springframework.context.annotation.Bean;
@Configuration
public class ImportConfiguration {

    @Bean
    public ImportService importService(){
        return new ImportService();
    }
}

接下来,我们在demo03的配置类中导入这个新增的配置类:

package com.gupaoedu.demo03;

import com.gupaoedu.demo04.ImportConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@ComponentScan("com.gupaoedu.demo03")
@Configuration  //<applicationContext.xml
@Import(ImportConfiguration.class)   // 导入外部配置类
public class SpringConfiguration {

    @Bean
    public Demo04Service demo04Service(Demo03Service demo03Service){
        Demo04Service demo04Service = new Demo04Service();
        // 依赖注入
        demo04Service.setDemo03Service(demo03Service);
        return demo04Service;
    }

}

获取导入的配置类中定义的bean:

package com.gupaoedu.demo03;

import com.gupaoedu.demo04.ImportConfiguration;
import com.gupaoedu.demo04.ImportService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Demo04Main {

    public static void main(String[] args) {
        ApplicationContext applicationContext=new
                AnnotationConfigApplicationContext(SpringConfiguration.class);
        System.out.println(applicationContext.getBean(ImportService.class));
        System.out.println(applicationContext.getBean(Demo04Service.class));
    }
}

在这里插入图片描述
代码示例4: @EnableXXX 注解启动一个模块
以定时任务为例,springMVC中创建一个定时任务如下:
applicationContext.xml中:

<context:component-scan base-package="com.gupaoedu.demo05"/>
    <task:annotation-driven scheduler="scheduler"/>
    <task:scheduler id="scheduler" pool-size="5"/>

定时任务:

package com.gupaoedu.demo05;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.Date;

@Service
public class TaskService {
    @Scheduled(fixedRate = 3000)
    public void reportCurrentTime(){
        System.out.println("current Time:"+new Date());
    }
}
package com.gupaoedu.demo05;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class TaskMain {
    public static void main(String[] args) {
        ApplicationContext context=new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
    }
}

通过Enable注解将代码改为如下:

package com.gupaoedu.demo06;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.Date;


@Service
public class TaskService02 {
    @Scheduled(fixedRate = 3000)
    public void reportCurrentTime(){
        System.out.println("current Time:"+new Date());
    }
}

配置类:@EnableScheduling注解启动定时任务模块,

package com.gupaoedu.demo06;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
@ComponentScan("com.gupaoedu.demo06")
@EnableScheduling
@Configuration
public class TaskConfiguration {
}
package com.gupaoedu.demo06;

import com.gupaoedu.demo03.SpringConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Task02Main {
    public static void main(String[] args) {
        ApplicationContext applicationContext=new
                AnnotationConfigApplicationContext(TaskConfiguration.class);
    }
}

两者的效果是一样的,
注解驱动的bean的定义,是通过AnnotationDrivenBeanDefinitionParser这个类去解析的,
在这里插入图片描述
这里会声明一个ScheduledAnnotationBeanPostProcessor 的bean去处理@Schedule注解,xml配置中的<task:annotation-driven scheduler=“scheduler”/> 所做的事情
在这里插入图片描述

接下来,看下@EnableScheduling注解做了什么事情

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({SchedulingConfiguration.class})
@Documented
public @interface EnableScheduling {
}

这个注解核心作用就是导入了SchedulingConfiguration 这个配置类,这个配置类就是创建一个ScheduledAnnotationBeanPostProcessor 的bean注入到IOC容器中,与xml中是一样的,不同点在于,通过Enable模块驱动,不需要手动去配置ScheduledAnnotationBeanPostProcessor bean的注入,只需要通过@EnableScheduling注解来开启ScheduledAnnotationBeanPostProcessor bean的注入

package org.springframework.scheduling.annotation;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;

@Configuration
@Role(2)
public class SchedulingConfiguration {
    public SchedulingConfiguration() {
    }

    @Bean(
        name = {"org.springframework.context.annotation.internalScheduledAnnotationProcessor"}
    )
    @Role(2)
    public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
        return new ScheduledAnnotationBeanPostProcessor();
    }
}

3.4 spring4.x的注解

  • @Conditional注解
    @Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件时才向IOC容器注册bean。
    @Conditional的定义如下:
//此注解可以标注在类和方法上
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME) 
@Documented
public @interface Conditional {
    Class<? extends Condition>[] value();
}

从代码中可以看到,需要传入一个Class数组,并且需要继承Condition接口:

public interface Condition {
    boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}

Condition是个接口,需要实现matches方法,返回true则注入bean,false则不注入。代码示例如下:
(1)需要在配置类中被装载的bean

package com.gupaoedu.example.demo01;
public class DemoService {
}

(2)自定义Bean的装载条件(自定义条件注解)

package com.gupaoedu.example.demo01;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

/**
 * 自定义bean条件
 */
public class MyCondition implements Condition{

    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//        1.conditionContext 可以从上下文中获取需要使用的信息
//        2.annotatedTypeMetadata 注解的元数据
        if(1 == 1){ // 随便写个判断逻辑模拟一下
            return true;
        }
        return false;
    }
}

(3)在配置类中,加上自定义的条件注解

package com.gupaoedu.example.demo01;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SpringConfiguration {

    // 满足某个条件才装载DemoService 这个bean 
    @Conditional(MyCondition.class)
    @Bean
    public DemoService demoService(){
        return new DemoService();
    }
}

(4)获取bean :DemoService

package com.gupaoedu.example.demo01;

import javafx.application.Application;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class ConditionMain {
    public static void main(String[] args) {

        ApplicationContext context=new AnnotationConfigApplicationContext(SpringConfiguration.class);
        System.out.println(context.getBean(DemoService.class));
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值