第四章 Spring常用注解

target

掌握注解方式将bean纳入到ioc容器
掌握@ComponentScan 注解的使用
了解使用@ComponentScan 注解定义扫描规则
掌握使用 @import注解将bean纳入到IOC容器
掌握使用 @Value注解给属性赋值
掌握使用 @Scope给bean设置作用域
掌握 生命周期相关的注解

Spring IOC容器往简单了说就是存放各种类型的bean的容器。在程序中最直观的体现方式就是xml配置文件,在配置文件中,我们可以通过bean标签存入各种类型的bean。下面我们通过xml配置文件的方式来操作一组bean:

Student类:

package com.lee.spring.bean;

public class Student {
    private int no;
    private String name;
    //getter、setter略
}
  • 将bean加入到IOC容器:

在src 下新建配置文件 applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="student" class="com.lee.spring.bean.Student">
        <property name="no" value="1001"></property>
        <property name="name" value="zs"></property>
    </bean>
</beans>
  • 从IOC容器取bean操作:
@Test
public void test01() {
  //1.获取到IOC容器
  ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  //2.从容器中获取bean
  Student student = (Student) context.getBean("student");
}

其实上面的操作同样用注解来完成

1. @Configuration + @Bean

一个普通的类加一个注解@Configuration就变成了配置类,配置类就相当于xml配置文件。

package com.lee.spring.config;

@Configuration
public class MyConfig {

}

相当于:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>

1.1 将bean纳入到IOC容器

定义一个方法,在方法上加注解@Bean。这个方法就变成了bean组件。

方法的返回值类型 = bean标签的的class

方法的名字 = bean标签的id

package com.lee.spring.config;

@Configuration
public class MyConfig {
     /**
     * 方法的返回值类型:相当于bean标签的class
     * 方法的名字:相当于bean标签的id
     */
    @Bean
    public Student student() {
        Student student = new Student(2002,"ls");
        return student;
    }
}

默认情况方法名就是bean的id值,可以修改:@Bean("stu")

1.2 从IOC容器取bean

用注解方式获取IOC容器需要用到ApplicationContext的一个实现类AnnotationConfigApplicationContext

在AnnotationConfigApplicationContext类中,可以按住 command + O (或者outline视图)看一下构造方法

AnnotationConfigContext注解.png

一般情况选择传入 class类型参数的构造方法。

编写测试方法进行测试:

@Test
public void test02() {
  ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
  Student student = (Student) context.getBean("student");
  System.out.println(student);
}

2. @ComponentScan

有一个Person类,如何其纳入到IOC容器?

package com.lee.spring.bean;

public class Person {

}

2.1 @Component注解方式

第一步:将 Person 类标上组件注解 @Component

package com.lee.spring.bean;

import org.springframework.stereotype.Component;

@Component
public class Person {

}

在配置文件applicationContext.xml 中开启包扫描:

<context:component-scan base-package="com.lee.spring"></context:component-scan>

base-package 包含 com.lee.spring 和 com.lee.spring的所有子包。

编写测试方法进行测试:

@Test
public void test03() {
  ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  //getBeanDefinitionNames()方法可以获取所有IOC容器中的bean
  String[] names = context.getBeanDefinitionNames();
  for (String name : names){
    System.out.println(name);
  }
}

输出:

person
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
...

注意:

  • IOC容器中会包含一些spring自带的bean

2.2 @ComponentScan注解方式

@ComponentScan 注解也能实现将组件扫描到IOC容器中。

实现方法:

直接在配置类上加@ComponentScan注解,然后括号里写上需要扫描的包即可。

package com.lee.spring.config;

@Configuration
@ComponentScan("com.lee.spring")
public class MyConfig {
  ...
}

编写测试方法,看一下容器中都有什么组件:

@Test
public void testAnnotation() {
  ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
  String[] names = context.getBeanDefinitionNames();
  for (String name : names){
    System.out.println(name);
  }
}

输出:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
myConfig
person
...

2.3 设置扫描规则

点开@ComponetScan注解里面,我们会发现有这样两个方法:

@ComponetScan注解

这两个方法是包含过滤器排除过滤器,我们可以用这两个方法设置扫描规则。这里的扫描规则就是规定扫描哪些包,不扫描哪些包。

① excludeFilters

excludeFilters 是排除过滤器。

ComponentScan.Filter[] excludeFilters() default {};

观察这行代码:

方法名excludeFilters表示可以排除很多过滤器。

返回值类型是ComponentScan.Filter类型的数组。

default{}表示默认什么都没有排除,可以自定义排除。

既然排除的是一个数组,所以用{}包裹起来:

@ComponentScan(value = "com.lee.spring",excludeFilters = { })

{}里面可以放好多ComponentScan.Filter,这里我们暂时写进来一个:

@ComponentScan(value = "com.lee.spring",excludeFilters = {@ComponentScan.Filter})

继续点击@ComponentScan.Filter,看一下里面都有什么:

Filter注解

发现Filter注解里面有一个FilterType类型的type,有一个Class数组类型的value,所以我们将这两个值写上。

@ComponentScan(value = "com.lee",excludeFilters = {@ComponentScan.Filter(type = ,value = )})

下面步骤配置type:

继续点进FilterType,看一下:

FilterType枚举

发现是FilterType是一个枚举,默认是:FilterType.ANNOTATION

@ComponentScan(value = "com.lee",excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = )})

下面步骤配置value:

value是一个Class数组类型,把需要排除的Class写进去就行。

@ComponentScan(value = "com.lee",excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value =Person.class )})

这表示排除的是Person类,如果想再排除User类,直接在后面加就可以。

@ComponentScan(value = "com.lee",excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value ={Person.class, User.class})})

② includeFilters

@ComponentScan(value = "com.lee",includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value ={Service.class, Controller.class})},useDefaultFilters = false)

注意:

  • includeFilters是包含的意思,就是包含配置的Controller层和Service层,但是必须关闭默认过滤器行为才有效:useDefaultFilters = false

FilterType

FilterType是一个枚举,包含以下:

FilterType枚举

其中:

  • ANNOTATION:表示过滤某一类注解,比如Controller注解、Service注解,所以带有这种注解的类全部会被过滤。

  • ASSIGNABLE_TYPE:表示过滤某个具体的类。比如:StudentController类。

    @ComponentScan(value = "com.lee",includeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value ={StudentController.class})},useDefaultFilters = false)
    
  • CUSTOM:表示自定义过滤哪些类。

    • 配置类:MyConfig.java
    @ComponentScan(value = "com.lee",includeFilters = {
            @ComponentScan.Filter(type = FilterType.CUSTOM,value ={MyFilter.class})},useDefaultFilters = false)
    
    • 自定义过滤器类:MyFilter.java,需要实现TypeFilter接口
    public class MyFilter implements TypeFilter {
    
        @Override
        public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
            ClassMetadata classMetadata = metadataReader.getClassMetadata();
            String className = classMetadata.getClassName();//获取所有三层注解的类名
            if (className.contains("User")){
                return true;
            }
            return false;
        }
    }
    

    所有包含User的三层类,全部会被过滤。例如:UserController,UserService等。

3. @import

回顾一下:把bean纳入到IOC容器的几种方式:

方式一:配置文件中配置

 <bean id="stu" class="com.lee.entity.Student" ></pre>

方式二:配置类中用@Bean注解

@Configuration
public class MyConfig {

 @Bean("stu")
 public Student student() {
   Student student = new Student(2002,"ls");
   return student;
 }
}

除了上述两种方式外,@import注解也能把bean纳入到IOC容器中:

🌰将 Apple类和 Banana类纳入到IOC容器:

苹果类:

package com.lee.spring.bean;

public class Apple {

}

香蕉类:

package com.lee.spring.bean;

public class Banana {

}

配置类:

package com.lee.spring.config;

@Configuration
@Import({Apple.class, Banana.class})
public class MyConfig {

}

测试:

@Test
public void test05() {
   ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
   String[] names = context.getBeanDefinitionNames();
   for (String name : names) {
     System.out.println(name);
   }
}

输出:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
myConfig
com.lee.entity.Apple
com.lee.entity.Banana

发现,苹果类和香蕉了已经成功纳入到IOC容器。

注意,@import注解纳入到IOC容器的bean的id是全类名。

4. @Value

通过@Value可以将外部的值动态注入到Bean中,可以为基本类型数据和String类型数据的变量注入数据

🌰为Computer类注入值:

package com.lee.spring.bean;

@Component("computer")
public class Computer {

   @Value("Apple")
   private String brand;
   @Value("100")
   private double price;

}

在配置文件 applicationContext.xml中配置包扫描:

<context:component-scan base-package="com.lee.spring"></context:component-scan>

编写测试方法进行测试:

@Test
public void test06() {
   ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
   Computer computer = (Computer)context.getBean("computer");
   System.out.println(computer);
}

控制台输出:

Computer [brand=Apple, price=100.0]

说明值已经成功注入。

上述方式是直接为属性赋值,还可以读取配置文件进行赋值:

在 src下新建 jdbcConfig.properties:

jdbc.driver = com.mysql.cj.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/test  
jdbc.username = root
jdbc.password = root

$获取配置文件的值,并且赋值给属性:

package com.lee.spring.bean;

@Component("conn")
@PropertySource("classpath:jdbcConfig.properties")
public class Connection {
   @Value("${jdbc.driver}")
   private String driver;

   @Value("${jdbc.url}")
   private String url;

   @Value("${jdbc.username}")
   private String username;

   @Value("${jdbc.password}")
   private String password;
}

5. @Scope

作用:

用于指定bean的作用范围。

属性:

singleton:
prototype:
request: 
session: 

🌰

package com.lee.spring.config;

@Configuration
public class MyConfig {

   @Bean("pc")
   @Scope("singleton")
   public Computer computer() {
     Computer pc = new Computer();
   return pc;
   }
}

6. 生命周期相关的注解

@PostConstruct注解标注的方法是初始化方法。

@PreDestroy注解标注的方法是销毁方法。

🌰演示User的生命周期:

package com.lee.spring.bean;

@Component("user")
public class User {

    public User() {
        System.out.println("User的无参构造");
    }
    
    @PostConstruct
    public void myInit() {
        System.out.println("User的初始化方法执行");
    }
    
    @PreDestroy
    public void myDestroy() {
        System.out.println("User的销毁方法执行");
    }
}

在配置文件 applicationContext.xml 中配置扫描:

<context:component-scan base-package="com.lee.spring.bean"></context:component-scan>

测试方法:

@Test
public void test08() {
  ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  User user  = (User)context.getBean("user");
  ((ClassPathXmlApplicationContext)context).close();
}

控制台打印:

User的无参构造
User的初始化方法执行
User的销毁方法执行
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,607评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,239评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,960评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,750评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,764评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,604评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,347评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,253评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,702评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,893评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,015评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,734评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,352评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,934评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,052评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,216评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,969评论 2 355

推荐阅读更多精彩内容