SpringBoot入门到超神-Spring的常用注解(一)

本文介绍了SpringBoot入门,包括Spring的IOC基础、注解编程的详细用法,如@Bean、@ComponentScan、@Conditional和@Import,以及如何通过注解控制Bean的创建、依赖和生命周期。
摘要由CSDN通过智能技术生成

系列文章目录

第一章 SpringBoot入门到超神-Spring的常用注解(一)



前言

SpringBoot并不是一个新技术了,如果你现在都还不是很明白SpringBoot的日常使用和了解,那么你一定不是一个合格的程序员,甚至可能是随时被公司优化的那一批人。该系列是可以帮助您快速入门SpringBoot,对公司实际开发中SpringBoot需要用到的内容。

一、SpringBoot基础概念

1.SpringBoot是什么玩意?

在SpringBoot出现之前的开发项目大多数都是SSM框架,即Spring、SpringMVC、MyBatis进行开发,配置文件都是通过XML进行配置的,都号称SSM的配置为配置地狱,直到SpringBoot3.0的出现,整加了注解编程(Java Config)来简化XML配置,并且Spring框架本身也是大量使用到注解的方式进行配置。

2.什么是JavaConfig

所谓的JavaConfig就是指的是配置文件,不是说使用了注解就不需要进行配置了,这个观点是错误的,以前是在XML里面进行配置,现在是将XML里面的配置进行拿出来,搬到Java类进行配置。

二、SpringBoot注解编程

1.Spring的IOC案例

首先提出的几个问题就是什么是IOC?为什么要使用IOC?用与不用有什么区别?

什么IOC?

IOC是Spring的两大核心之一(另外的一个是AOP),IOC是控制反转,就是把创建对象的事情交给Spring去做,这些事情分别是对象的创建,属性设置,初始化,销毁(Bean的生命周期),大概流程是 创建Bean注入到Spring容器中去,使用到时候直接从容器中获取出来。接下来会将基于XML的IOC实现和使用注解编程实现进行对比。重点来喽!!!!

1.1基于XML的IOC

首先创建一个普通的Java的项目(不会的同学请自行百度),导入一个测试+Spring的基础依赖,开始搞事。

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>4.3.0.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.3.0.RELEASE</version>
    </dependency>
</dependencies>

第二步:接着我们在resource目录创建一个xml作为Spring配置文件,内容如下

<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
      ">
    <bean id="user" class="cn.whale._01_xml_ioc.Student">
        <property name="id" value="1"/>
        <property name="username" value="zs" />
    </bean>
</beans>

第三步:编写实体类 Student

public class Student {
    private Long id;
    private String username;

    public Student() {
    }

    public Student(Long id, String username) {
        this.id = id;
        this.username = username;
    }

    public Long getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", username='" + username + '\'' +
                '}';
    }
}

第四步 : 编写测试类

//XML版本 IOC测试
public class StudentTest {

    @Test
    public void test() {
        //创建IOC容器工厂类 , 加载配置 , 得到容器对象
        ClassPathXmlApplicationContext applicationContext = 
                                 new ClassPathXmlApplicationContext("applicationContext.xml");
        //从容器中获取Bean
        Student bean = applicationContext.getBean(Student.class);
        //打印
        System.out.println(bean);
    }
}

这里使用了一个 ClassPathXmlApplicationContext 来加载Spring的配置文件。ClassPathXmlApplicationContext 是IOC容器工厂类,可以默认从classpath加载指定配置。

根据上面案例我们可以总结出IOC的步骤:

  1. 首先需要有个xml配置文件,配置好Student
  2. 然后需要有个IOC工厂类去加载xml配置,Spring会自动把xml中配置的Bean注册到容器中
  3. 最后从容器中直接获取到Bean即可

1.2基于Java Config的IOC

第一步:创建一个配置类作为Spring的配置文件,用来代替xml配置文件,同时贴上@Configuration标记该类为Spring的配置类

//Spring配置注解,标记该类是Spring的配置类,该类的作用等同于applicationContext.xml
@Configuration
public class JavaConfig {

}

第二步:在配置类中注册Bean

//Spring配置注解,标记该类是Spring的配置类,该类的作用等同于applicationContext.xml
@Configuration
public class JavaConfig {

	//注册一个Student到Spring容器中
    @Bean
    public Student student(){
        return new Student();
    }
}

@Bean贴在方法上,该方法的返回值会自动注册到Spring容器中。

第三步:编写测试类,使用 AnnotationConfigApplicationContext 来加载配置类

//XML版本 IOC测试
public class StudentTest {

    @Test
    public void test() {
        //创建IOC容器工厂类 , 加载配置 , 得到容器对象
        AnnotationConfigApplicationContext applicationContext = 
            								new AnnotationConfigApplicationContext(JavaConfig.class);
        //从容器中获取Bean
        Student bean = applicationContext.getBean(Student.class);
        //打印
        System.out.println(bean);
    }
}

ClassPathXmlApplicationContext是针对xml的容器工厂,而AnnotationConfigApplicationContext是针对配置类的容器工厂。

总结一下:

  • @Configuration : Spring的配置注解,标记某个类成为Spring配置类。
  • @Bean:用来在配置类中,注册Bean的注解,贴方法上方法的返回实例会被识别为Spring的Bean交给Spring管理。
  • AnnotationConfigApplicationContext :是针对配置类的容器工厂,SpringBoot中使用的也是这个工厂。

2.@Bean的详解

2.1Bean的属性

下面我们来介绍一下@Bean来做一个全方位的认识,下面是它的结构

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
    //Bean的名称,如果未指定就使用方法名作为Bean的名称
    String[] name() default {};
    //自动装配
    Autowire autowire() default Autowire.NO;
    //Bean的初始方法
    String initMethod() default "";
    //Bean的销毁方法
    String destroyMethod() default "(inferred)";
}

解释:

  • name : Bean的名称,如果未指定就使用方法名作为Bean的名称
  • autowire依赖项是否通过基于约定的自动装配按名称或类型注入
  • initMethod :初始化方法
  • destroyMethod : 销毁方法

2.2 Bean的生命周期

根据上面的解释我们给之前的代码做些改造

  //注册一个Student到Spring容器中
@Bean(name = "student" ,    //bean的名字
      initMethod = "init",  //初始化方法,需要给Student提供init方法
      destroyMethod = "destory")  //销毁方法,需要给Student提供destory方法
public Student student(){
        return new Student(1L,"zs");
}

然后给Student提供两个方法 , 将我们再次执行测试,就可以看到init方法被执行。

public class Student {
    //省略...
    
    public void init(){
        System.out.println("Bean的init被执行...");
    }

    public void destory(){
        System.out.println("Bean的destory被执行...");
    }
}

2.3destory不执行问题

Bean的Destory方法没被执行的原因是Spring容器没被正常关闭,因为我们使用的Junit的测试,测试类是没有交给Spring管理的,当测试方法执行完毕,整个程序也就终止,Spring容器没办法正常关闭,解决方案有两个:一是使用JDK1.7的自动关闭资源的新特性;二是使用Spring的测试,把测试类也交给Spring容器管理。
方式一:使用JDK1.7的自动关闭资源的新特性

//IOC测试
public class StudentTestBF {

    @Test
    public void test() {
        try(
            //创建IOC容器工厂类 , 加载配置 , 得到容器对象
            AnnotationConfigApplicationContext applicationContext = 
            								new AnnotationConfigApplicationContext(JavaConfig.class)
        ){
            //从容器中获取Bean
            Student bean = applicationContext.getBean(Student.class);
            //打印
            System.out.println(bean);
        }
    }
}

方式二:整合Spring的测试,使用@RunWith+@ContextConfiguration注解来完成,要注意的是现在的配置文件是一个配置类,需要通过ContextConfiguration的classes属性来指定。如下:

//IOC测试
@RunWith(SpringJUnit4ClassRunner.class)
//
@ContextConfiguration(classes = JavaConfig.class)
public class StudentTest {

    //不再需要创建AnnotationConfigApplicationContext,直接注入即可
    //@Autowired
    //private ApplicationContext applicationContext;

    @Autowired
    private Student student;

    @Test
    public void test() {
        //从容器中获取Bean
        //Student student = applicationContext.getBean(Student.class);
        //打印
        System.out.println(student);
    }
}

集成Spring的测试之后,我们可以通过注入 ApplicationContext 容器来获取Bean, 甚至直接注入需要的Bean,效果如下:
在这里插入图片描述
这里我再介绍一种实现Spring的Bean的生命周期的方式,就是 通过@PostConstract+@PreDestory注解来完成,如下:

public class Student {
    ...省略...
	//PostConstruct : 该注解用于需要在依赖注入完成后执行任何初始化的方法
    @PostConstruct
    public void init2(){

    }
    //PreDestroy : 注释在方法上用作回调通知,以表明实例正在被容器移除
    @PreDestroy
    public void destory2(){

    }

这里解释一下这2个注解

  • @PostConstruct :标记了该注解的方法会在:对象的依赖注入完成后执行,可以用作对象的初始化
  • @PreDestroy:当Spring容器移除该对象之前会触发标记了该注解的方法的调用,可以用来做一些扫尾工作

当然还有其他的手段,比如通过实现 InitializingBean 和 DisposableBean 来完成,你可以自己去了解一下

2.3Bean的Scope

Spring中的Bean默认情况下scope为singleton , 在Spring注解编程中提供了@Scope注解来指定,如下

//注册一个Student到Spring容器中
@Bean
@Scope("singleton") //单利,指定为  : prototype 就是多利
public Student student(){
    return new Student(1L,"zs");
}

2.4Bean的懒加载

在Spring中Bean默认是lazy=false , 在Spring启动的时候,会对 scope为singleton也就是单利的Bean,且没有配置lazy = true的普通Bean进行实例化。也就是说一个普通的Bean在Spring启动的过程中就会为该Bean进行实例化,然后把实例化后的Bean存储到单利Bean的容器中(一个ConcurrentHashMap中)。

在Spring的注解式编程中提供了@Lazy注解来修改是否要懒初始化,如下

@Bean
@Lazy(true) //懒初始化,使用到Bean的时候才会初始化
public Student student(){
    return new Student(1L,"zs");
}

当然没有特殊情况此注解也不建议去加,默认就是迫切初始化的。

3.@ComponentScan的使用

3.1.认识ConmponentScan

在之前的案例中我们是通过@Bean贴方法上的方式来注册Bean,这本身也没有什么问题,但是当我的Bean特别多的时候,那么我们就需要写很多的方法非常的麻烦。在XML的配置方式中我们也是可以通过<context:component-scan base-package/>指定一个扫描包 的方式来开启IOC组件自动扫描,然后我们只需要在需要交给Spring管理的类上贴:@Controller ,@Service,@Component,@Repository 等注解即可,当Spring启动的时候,就会自动去扫描指定的base-package包下的标记了如上注解的类,自动注入到Spring容器中。
在注解式编程中Spring也提供了一个注解@ComponentScan 来达到如上所述的相同效果。
第一步:对实体类进行改造,增加一个@Component 注解

//标记为Spring的组件
//@Controller
//@Service
//@Repository
@Component
public class Student {
    private Long id;
    private String username;
    ...省略...
}

第二步:配置类增加@ComponentScan

//Spring配置注解,标记该类是Spring的配置类,该类的作用等同于applicationContext.xml
@Configuration
//Spring的IOC自动扫描,可以自动多个扫描包
@ComponentScan(basePackages = {"cn.whale._03_ioc_scan"})
public class JavaConfig {

}

重点说一下 @ComponentScan注解,上面给该注解指定了一个basePackages扫描包路径,那么该注解就会扫描到该表下的贴了@Controller ,@Service,@Component,@Repository 的类,自动完成Bean的注解。如果没有指定basePackages该注解也会默认扫描当前包及其子包中的相关类。

需要注意:如果使用了IOC自动扫描的方式,就可以不使用@Bean的方式注册Bean了,切记。

第三步:执行测试代码,这里省略。

3.2.ComponentScan的属性

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
    //配置扫描的包路径
    @AliasFor("basePackages")
	String[] value() default {};
    //配置扫描的包路径
    @AliasFor("value")
	String[] basePackages() default {};
    //包含的资源过滤器
    Filter[] includeFilters() default {};
    //排除的资源过滤器
    Filter[] excludeFilters() default {};
    //指定是否延迟初始化Bean。默认迫切初始化
    boolean lazyInit() default false;
}

3.3.排除资源excludeFilters

该注解中还提供了 lazyInit 来指定是否要懒初始化,还提供了 excludeFilters 来排除某些类不需要交给Spring管理,比如我同一个包下有Student和Teacher类都注解了@Component,但是我想排除Teacher不交给Spring管理,如下:

@Component
public class Student {
}

@Component
public class Teacher {
}

配置类通过excludeFilters来排除,如下

//Spring配置注解,标记该类是Spring的配置类,该类的作用等同于applicationContext.xml
@Configuration
//Spring的IOC自动扫描
@ComponentScan(basePackages = "cn.whale._03_ioc_scan",
        //根据类型,排除Teacher这个类不要交给Spring管理 
        excludeFilters = { @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE , value = Teacher.class)}
)
public class JavaConfig {

}

type = FilterType.ASSIGNABLE_TYPE : 是通过类型来排除,也可以支持通过注解来排除,通过REGEX正则来排除等。

3.4.ComponentScans

ComponentScans是ComponentScan的复数形式,可以把多个ComponentScan进行组合,如下

//Spring配置注解,标记该类是Spring的配置类,该类的作用等同于applicationContext.xml
@Configuration
@ComponentScans({
        @ComponentScan(basePackages = "cn.whale._03_ioc_scan",
                //根据类型,排除Teacher这个类不要交给Spring管理
                excludeFilters = { @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE , value = Teacher.class)}
        ),
        @ComponentScan(basePackages = "cn.whale._04_ioc_scan",
                //根据类型,排除Teacher这个类不要交给Spring管理
                excludeFilters = { @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE , value = Teacher.class)}
        ),
})
public class JavaConfig {

}

这种用法比较复杂,更多的还是使用 @ComponentScan 就能满足我们的需求了。

3.5.Bean的依赖关系

我们使用组件自动扫描,那么应该如何进行依赖注入的,实际上非常简单,使用@Autowired 注解自动注入即可:

@Component
public class Student {
    //自动注入
    @Autowired
    private Teacher teacher;
    ...省略...
}

4.@Conditional

@Conditional注解的作用是标记组件满足某种条件才会被创建,该注解通常贴在定义Bean的方法上,或者配置类上,它提供了一个 Class<? extends Condition>[] value() 属性来指定条件规则。也就是说我们需要

  1. 做两个事情 在定义Bean的方法上贴@Conditional(value=XxxCondition.cass)。
  2. 定义一个条件类实现Condition接口,编写条件规则。

4.1标记Bean的创建条件

第一步:定义Bean,并通过 @Conditional(BeanCreateCondition.class) 注解指定创建Bean的条件

//Spring配置注解,标记该类是Spring的配置类,该类的作用等同于applicationContext.xml
@Configuration
public class JavaConfig{

        //如果环境是window,就注册该Bean , 指向一个条件类
        @Conditional(BeanCreateCondition.class)
        @Bean
        public Student windowStudent(){
                return new Student(1L,"window");
        }
}

4.2定义Bean的创建条件

第二步:创建条件类,需要实现 Condition 接口,复写其matches方法,该方法的返回值决定是否满足条件

//Bean被创建的条件,如果系统环境是window,就创建
public class BeanCreateCondition implements Condition {

    //方法返回的boolean值代表是否满足条件
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //从Environment环境中拿到操作系统的名字
        String osName = context.getEnvironment().getProperty("os.name");
        //如果是window,就返回true
        return osName.startsWith("Windows");
    }
}

如果满足:osName.startsWith(“Windows”) , 也就是说系统是window,那么该条件类的matches方法返回的true。当Spring在创建Bean的时候,因为方法上标记了 @Conditional(BeanCreateCondition.class) ,就会去调用BeanCreateCondition#matches方法,根据返回值来确定要不要创建Bean。

注意:如果把@Conditional(BeanCreateCondition.class)贴在配置类上,那么配置类中的所有组件都会受到该条件限制。

5.@Import

提供与 Spring XML 中的元素等效的功能。 允许导入@Configuration类、 ImportSelector和ImportBeanDefinitionRegistrar实现。该功能是通过@Import实现,

下面是@Import注解的结构

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

	/**
	 可以导入 Configuration配置类 ,ImportSelector 选择器 ,ImportBeanDefinitionRegistrar Bean注册器
	 * @{@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
	 * or regular component classes to import.
	 */
	Class<?>[] value();
}

可以看到,Import支持:导入 Configuration配置类 ,ImportSelector 选择器 ,ImportBeanDefinitionRegistrar 注册器。

总结

Spring的注解就到这里,这篇文章是为了SpringBoot打下基础,为减轻后面SpringBoot的学习压力,避免一些基础的东西都搞不明白。
第一次写,有很多不足,望点评。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值