0%

注解编程

为什么要使用注解编程

注解编程可以使得代码更简洁,提供开发效率。  
替换xml这种配置形式,简化配置

例如 开发一个User类:

1
public class User(){....}

添加注解@Component后如下:

1
2
@Component
public class User(){....}

该形式等价于在spring配置文件中的

1
<bean id="user" class="com.study.User"></bean>

容器相关注解

组件注册

@Configuration

作用在类上,表示这个是一个配置类,相当于配置文件xml一样

@Bean

作用在方法上,和 bean 标签功能一致,返回值是class,方法名默认是id,也可以指定

@ComponentScan

作用在类上,配置扫描包,可以使用excludeFilters或includeFilters来过滤或指定包含哪个类

使用excludeFilters:过滤掉哪些类

1
2
3
@ComponentScan(value = {"com.study"}, excludeFilters={
@Filter(type=FilterType.ANNOTATION, value={Controller.class, Service.class})
}) //配置扫描包

使用includeFilters:包含哪些类

1
2
3
@ComponentScan(value = {"com.study"}, includeFilters={
@Filter(type=FilterType.ANNOTATION, value={Service.class})
}, useDefaultFilters=false) //配置扫描包

@ComponentScans

配置扫描包,可以设置多个值

1
2
3
4
5
@ComponentScans({
@ComponentScan(value = {"com.study"}, excludeFilters={
@Filter(type=FilterType.ANNOTATION, value={Controller.class, Service.class})
}) //配置扫描包
})

自动过滤规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MyTypeFilter implements TypeFilter{

public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//注解信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//路径信息
Resource resource = metadataReader.getResource();
//获取类名
String className = classMetadata.getClassName();
System.out.println(className);
if(className.contains("er")){
return true;
}
return false;
}
}
1
2
3
4
5
6
@ComponentScans({
@ComponentScan(value = {"com.study"}, excludeFilters={
@Filter(type=FilterType.ANNOTATION, value={Controller.class, Service.class}),
@Filter(type=FilterType.CUSTOM, value={MyTypeFilter.class}) //使用自定义的过滤规则
}) //配置扫描包
})

@Scope

作用在方法上,相当于 bean 中的scope属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* prototype 多实例,当要用到时才会创建实例
* singleton 单例(默认),在spring容器创建时就创建实例了
* request 同一个请求创建一个实例(很少用到)
* session 同一个session创建一个实例(很少用到)
* @return
*/
@Scope("prototype")
@Bean
public Student student(){
System.out.println("创建student对象");
return new Student("张三", 15);
}

@Lazy

懒加载,作用单实例bean中,原来的单实例bean在spring容器加载后会自动创建实例,加了此注解后,会在第一次使用到此bean时才会创建。

1
2
3
4
5
6
@Lazy
@Bean
public Student student(){
System.out.println("创建student对象");
return new Student("张三", 15);
}

@Conditional

可以作用在类或方法上。
作用在方法上: 如果条件返回true,则创建bean
作用在类上: 如果返回true,此类的注册bean才会生效。

1
2
3
4
5
6
7
8
9
10
11
   @Conditional({WindowsCondition.class})
@Bean("windows")
public Student student2(){
return new Student("my is windows", 60);
}

@Conditional({LinuxCondition.class})
@Bean("linux")
public Student student3(){
return new Student("my is linux", 50);
}
1
2
3
4
5
6
7
8
9
10
11
//判断是否是Linux系统
public class LinuxCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String name = environment.getProperty("os.name");
if(name.contains("linux")){
return true;
}
return false;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

//判断是否是windows系统
public class WindowsCondition implements Condition{

/**
* context:程序上下文
* metadata:注解信息
*/
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//bean工厂
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//程序运行环境
Environment environment = context.getEnvironment();
//bean注册信息
BeanDefinitionRegistry registry = context.getRegistry();

String name = environment.getProperty("os.name");
if(name.contains("Windows")){
return true;
}
return false;
}

}

@Import

作用:快速的给容器导入一个组件

  • 使用方法1:

    1
    2
    @Import({要导入到容器中的组件}),容器中就会自动注册这个组件,id是类的全路径
    @Import({RedisUtils.class})
  • 使用方法2:
    ImportSelector:返回需要导入的组件的全类名数组,这个在springboot中底层很常用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //实现ImportSelector接口
    public class MyImportSelector implements ImportSelector{

    //importingClassMetadata:注解信息
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
    return new String[]{"com.oyr.bean.Red", "com.oyr.bean.Yellow"};
    }

    }
    @Import({ MyImportSelector.class})
  • 使用方法3:
    ImportBeanDefinitionRegistrar:手动注册bean到容器中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //实现ImportBeanDefinitionRegistrar接口
    public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar{

    /**
    * importingClassMetadata:注解信息
    * registry:bean注册对象
    */
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    //是否有id为red Bean
    boolean b1 = registry.containsBeanDefinition("com.oyr.bean.Red");
    //是否有id为yellow bean
    boolean b2 = registry.containsBeanDefinition("com.oyr.bean.Yellow");
    if(b1 && b2){
    registry.registerBeanDefinition("blue", new RootBeanDefinition(Blue.class));
    }
    }

    }

容器注册逐渐总结:

  1. 包扫描 + 逐渐标注注解(@Controller/ @Service/ @Repository/ @Componnet)
  2. Bean 导入的第三方包里面的组件
  3. import 快速的给融资导入一个组件
    • @Import 需要导入容器中的bean,容器就会自动注解这个Bean,id是类的全路径
    • ImportSelector: 返回需要导入的组件的全类名数组
    • ImportBeanDefinitionRegistrar 手动注册bean到容器中
  4. 使用spring提供的FactoryBean(goncBean)
    • 默认获取到的是工厂bean调用getObject()创建的对象
    • 要获取到工厂bean本身,我们要在id前面加一个&

Bean 生命周期

指定初始化方法和销毁方法

  • 对象的创建:
    • 单实例: 在容器启动的时候创建对象
    • 多实例: 在每次获取对象时创建
  • 初始化:
    • 对象创建完成,并赋值好,调用初始化方法
  • 销毁:
    • 单实例在容器关闭的时候销毁
    • 多实例不会调用销毁方法,spring并不销毁,需要自己调用销毁方法。
  1. 使用@Bean指定初始化方法和销毁方法

    1
    2
    3
    4
    @Bean(initMethod="init", destroyMethod="destroy")
    public Color color(){
    return new Color();
    }
  2. 通过让bena实现InitializingBean(定义初始化逻辑), DisposableBean(定义销毁逻辑)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class Color2 implements InitializingBean, DisposableBean{

    public void destroy() throws Exception {
    // TODO Auto-generated method stub
    System.out.println("destroy");
    }

    public void afterPropertiesSet() throws Exception {
    // TODO Auto-generated method stub
    System.out.println("afterPropertiesSet");
    }
    }
  3. 使用JSR250
    @PostConstruct:在bean创建完成并且属性赋值完成:来执行初始化方法
    @PreDestroy:在容器销毁bena之前通知我们进行销毁工作

BeanPostProcessor:bean的前后置处理器
在bean初始化前后进行一些处理工作
postProcessBeforeInitialization:在初始化之前工作
postProcessAfterInitialization:在初始化之后进行工作

spring底层对BeanPostProcessor的使用:
Bean赋值,注入其他组件,@AutoWitred,生命周期注解功能,@Async,等等
都是使用BeanPostProcessor实现的。