目录
一、前言
本文章是在学习尚硅谷雷丰阳老师的Spring注解驱动的教程视频中,所记录的简单笔记以及代码实现!
雷丰阳老师的视频很赞,建议大家可以去学习一下!
小伙伴在学习Spring框架的时候,有没有被各种xml配置文件所烦扰呢?比如:组件的注册,依赖的注入,组件的生命周期,属性赋值等等…
相关博客:
二、注解的基本知识与代码示例
(一)@Configuration
用于定义配置类,即配置类相当于xml配置文件,只是一个是通过xml标签来写配置,一个是通过Java代码的形式来定义配置,被注解的类的内部可以包含一个或多个@Bean注解的方法,这些方法将会被
AnnotationConfigApplicationContext
或AnnotationConfigWebApplicationContext
类进行扫描,并用于构建bean
定义,初始化Spring
容器。
1. 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">
<!--注册一个类型为Person,id为person的组件-->
<bean class="com.study.bean.Person" id="person">
<property name="name" value="zhangsan"/>
<property name="age" value="20"/>
</bean>
</beans>
2. @Configuration注解注册组件
@Configuration
public class ContextConfig {
@Bean
public Person person(){
return new Person("zhangsan", 20);
}
}
@Bean的注解相当于xml文件中的bean标签。在方法上使用@Bean注解注册Bean对象,Bean类型是方法类型,Bean的id默认为方法名;若修改则在@Bean注解加上value属性,@Bean(value=“xxx”)。
(二)@ComponentScan
用于包扫描,只要标注了@Controller、@Service、@Repository、@Component的组件,都会自动扫描加入到容器中。作用在配置类上。
value:指定要扫描的package;
includeFilters=Filter[]:指定只包含的组件
excludeFilters=Filter[]:指定需要排除的组件;
useDefaultFilters=true/false:指定是否需要使用Spring默认的扫描规则:被@Component, @Repository, @Service, @Controller或者已经声明过@Component自定义注解标记的组件;
在过滤规则Filter中:
- FilterType:指定过滤规则,支持的过滤规则有
- ANNOTATION:按照注解规则,过滤被指定注解标记的类;
- ASSIGNABLE_TYPE:按照给定的类型;
- ASPECTJ:按照ASPECTJ表达式;
- REGEX:按照正则表达式
- CUSTOM:自定义规则;
value:指定在该规则下过滤的表达式;
1. 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"
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">
<!--自动包扫描-->
<context:component-scan base-package="com.study"></context:component-scan>
</beans>
2. @ComponetScan自动包扫描
@Configuration
@ComponentScan(value = "com.study")
public class ContextConfig {
@Bean
public Person person(){
return new Person("lisi", 20);
}
}
=================================================
//可以定义一些controller、service、dao来测试一下是否真的能够扫描到容器中
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ContextConfig.class);
//获取容器中定义的组件
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name: beanDefinitionNames) {
System.out.println(name);
}
}
}
结果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
contextConfig
personController
personDao
personService
person //说明:person在容器中,并不是自动扫描到容器中的,而是在配置类中使用了@Bean注解,注册到了容器中。
分析:在前五个是ioc容器中需要使用的组件,后五个则是自定义的组件,可以看到contextConfig配置类也被自动扫描进入到容器中,显然说明其也使用了@Component注解。
源码:
(三)@Scope
用于定义Bean的作用域,默认是单例的,在 Spring IoC 容器是指其创建的 Bean 对象相对于其他 Bean 对象的请求可见范围。在 Spring IoC 容器中具有以下几种作用域:基本作用域(singleton、prototype),Web 作用域(reqeust、session、globalsession),自定义作用域。
各个作用域的解释:
scope | 说明 |
---|---|
singleton单例模式 | 全局有且仅有一个实例,容器被创建时bean就会创建并被放入到容器中。以后每次获取该bean时,都直接在容器中获取到该bean。 |
prototype原型模式 | 每次获取bean的时候会有一个新的实例,每次获取该bean时才会被创建并将该bean放入到容器中。 |
request | 每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效 |
session | 每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效 |
global session | global session作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个 portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portlet Session的生命周期范围内。如果你在web中使用global session作用域来标识bean,那么web会自动当成session类型来使用。 |
1. 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"
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">
<!--注册一个类型为Person,id为person的组件,定义组件的作用域为多例的-->
<bean class="com.study.bean.Person" id="person" scope="prototype">
<property name="name" value="zhangsan"/>
<property name="age" value="20"/>
</bean>
</beans>
2. @Scope注解设置组件的作用域
@Configuration
@ComponentScan(value = "com.study")
public class ContextConfig {
@Bean
@Scope("prototype")
public Person person(){
return new Person("lisi", 20);
}
}
(四)@Lazy
懒加载,作用于单例的Bean,在创建容器的时候,不立刻创建单例的Bean并加入到容器中。而是在其获取时才创建Bean并加入到容器中。
@Bean
@Scope("singleton")
@Lazy
public Person person(){
return new Person("lisi", 20);
}
(五)@Conditional
按照一定的条件给容器中注册组件bean!作用是根据某个条件创建特定的Bean,通过实现Condition接口,并重写matches接口方法来构造判断条件。总的来说,就是根据特定条件来控制Bean的创建行为,这样我们可以利用这个特性进行一些自动的配置。可以作用于类和方法上!@Conditional注解放在类上时,对类中的所有的bean可以进行一个统一的管理,是否统一注册到容器中!
@Conditional注解需要一个Condition的类来作为参数!一般这个条件类是要自己创建的!
源码:
Condition是一个接口,接口中有个匹配条件的方法,参数var1为条件上下文,参数var2为当前标注了@Conditional注解的类的所有的注释信息!
例子:以不同的操作系统为条件,通过实现Condition接口,并重写其matches方法来构造判断条件。若在Windows系统下运行程序,则输出列表命令为dir;若在Linux系统下运行程序,则输出列表命令为ls。
//window判断条件
public class WindowsCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty("os.name").contains("Windows");
}
}
//Linux判断条件
public class LinuxCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty("os.name").contains("Linux");
}
}
//按照条件进行Bean注册
@Bean
@Conditional(WindowsCondition.class)
public String window() {
return "dir";
}
@Bean
@Conditional(LinuxCondition.class)
public String linux() {
return "ls";
}
(六)@Import
快速导入一个组件加入到IoC容器中,而不需要在配置类中声明一个方法使用@Bean注解进行初始化操作来加入到容器中。其id默认为全类名,容器会自动注册!@Import(value=“xxx.class”),其value也可以是一个数组的形式!
源码:
还可以进行多个组件的快速加入,@Import({xxx.class, aaa.class})。
1. ImportSelector
导入自定义的选择器,进行一个组件的批量选择导入!返回值为:导入容器中的组件的全类名数组!
源码:
示例:测试容器中是否存在使用选择器返回全类名返回的方式的bean
//定义一个Color类
public class Color {
}
//定义一个选择器,实现了ImportSelector接口
public class MySelector implements ImportSelector {
/**
*
* @param importingClassMetadata 当前标注@Imoprt注解的类的全部注解数据
* @return 返回加入到容器中的bean的全类名
*/
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//返回了Color的全类名
return new String[]{"com.study.bean.Color"};
}
}
//配置类中使用@Import注解,但value并不是Color.class而是MySelector.class
@Configuration
@ComponentScan(value = "com.study")
@Import(MySelector.class)
public class ContextConfig {
@Bean
@Scope("singleton")
@Lazy
public Person person(){
return new Person("lisi", 20);
}
}
//打印中容器中的bean
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ContextConfig.class);
//获取容器中定义的组件
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name: beanDefinitionNames) {
System.out.println(name);
}
}
}
结果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
contextConfig
personController
personDao
personService
com.study.bean.Color //存在color组件
person
2. ImportBeanDefinitionRegistrar
手动注册bean加入到容器中,自定义一个类实现ImportBeanDefinitionRegistrar接口,来进行相应的逻辑注册!
//定义一个bean
public class Dog {
}
// 自定义一个ImportBeanDefinitionRegistrar实现类
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//判断容器中是否包含person字段的bean
boolean flag = registry.containsBeanDefinition("person");
//如果存在,则注册一个dog的组件
if (flag) {
//当我想用BeanDefinition创建对象时,发现并没有有参构造,所以选用了其子类
//BeanDefinition beanDefinition = new BeanDefinition();
RootBeanDefinition beanDefinition = new RootBeanDefinition(Dog.class);
//注册需要两个参数{s:bean的名称,beanDefinition:bean的信息--类型,作用域...}
registry.registerBeanDefinition("dog", beanDefinition);
}
}
}
//在配置类注解@Import参数中加入MyImportBeanDefinitionRegistrar.class
@Configuration
@ComponentScan(value = "com.study")
@Import({MySelector.class, MyImportBeanDefinitionRegistrar.class})
public class ContextConfig {
@Bean
@Scope("singleton")
@Lazy
public Person person(){
return new Person("lisi", 20);
}
}
//测试容器中是否存在dog组件
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ContextConfig.class);
//获取容器中定义的组件
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name: beanDefinitionNames) {
System.out.println(name);
}
}
}
结果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
contextConfig
personController
personDao
personService
com.study.bean.Color
person
dog //容器中存在dog组件,手动注册成功!
(七)工厂模式注册组件
使用Spring提供的FactoryBean(工厂方法)来创建对象!将工厂加入到IoC容器中时,bean的名称时beanFactory但是其类型是bean的实体类型!
(八)@Profile
- 指定组件在哪个环境的情况下才能被注册到容器中。不指定的情况下,任何环境下都能注册这个组件到容器中。
- 添加了@Profile注解的组件,只有在指定环境被激活的时候才能注册到容器中。默认是default环境。
- @Profile作用在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效。
- 没有标注@Profile注解的组件,在任何环境都是加载到容器中的。
//代码解释@Profile注解
//在配置类中,注册不同环境下的组件
@Configuration
public class MainConfigOfProfile {
@Bean
@Profile("test") //test环境才被注册
public Color color(){
return new Color();
}
@Bean
@Profile("dev") //dev环境下才被注册
public Dog dog() {
return new Dog();
}
}
======================================================
//运行类
public class MainTest {
public static void main(String[] args) {
//创建applicationContex容器对象
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
//设置当前激活环境值
applicationContext.getEnvironment().setActiveProfiles("dev");
//注册配置类
applicationContext.register(MainConfigOfProfile.class);
//刷新容器
applicationContext.refresh();
//获取容器中注册的组件
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name: beanDefinitionNames) {
System.out.println(name);
}
}
}
结果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfigOfProfile
dog //dog组件在容器中