当一个项目很大的时候,xml 文件就会有很多,配置起来就稍微有点麻烦,所以,可以使用注解来代替在xml的相关配置。
用在类上,指明这个类是配置类,相当于是一个只有名称空间的xml文件,有了配置类,就可以不用写xml文件了。
用在方法上,给IOC容器中注册一个bean,id 默认为方法名,也可以使用value属性指定,类型是方法的返回值类型。相当于在xml文件中写
。如果方法中有参数,参数默认从容器中获取。
`
@//表示这个类是配置类,代替xml文件
@Configuration
public class MainConfig {
@Bean //在IOC容器中注册bean,类型是返回值类型,id默认是方法名
public Person person(){
return new Person("张三",20);
}
}
用在配置类上,开启包扫描,开启之后,类只要标注了@Controller、@Service、@repository、@Componmente
这四个注解中的任何一个,都会在容器中注册,相当于在xml文件中使用
- value:指定要扫描的包
- excludeFilters :指定扫描的时候按照什么规则排除哪些组件,这个属性的类型是一个Filter注解类型的数组,Filter注解中,有type(按照什么类型进行排除)、classes(排除type类型的类)等属性
@ComponentScan(value="ewen",
excludeFilters = {
排除@Repository注解
@Filter(type=FilterType.ANNOTATION,classes={Repository.class})
})
- includeFilters:指定扫描的时候只包含那些类型的组件,使用这个属性的时候,需要让
useDefaultFilters=false
(@CoponentScan的属性),来禁用默认的过滤规则,默认使用的是不扫描哪些些组件
@ComponentScan(value="ewen",
includeFilters ={
//只要@Service注解
@Filter(type=FilterType.ANNOTATION, classes={Service.class, Repository.class})
}, useDefaultFilters = false)
@Filter这个注解中,type的类型是FilterType枚举类,枚举类中有以下几个值
- FilterType.ANNOTATION:按照注解(常用),classes是注解.class
- FilterType.ASSIGNABLE_TYPE:按照给定的类型(常用),classes是类名.class
- FilterType.ASPECTJ:使用ASPECTJ表达式
- FilterType.REGEX:使用正则指定
- FilterType.CUSTOM:使用自定义规则(自定义类,实现FilterType接口),classes为自定义类.class
自定义过滤规则,就是写一个类,实现FilterType接口,实现match方法(过滤规则),当扫描一个类时,会执行match方法,如果返回true,表示,符合规则,将这个类在IOC容器中注册,返回false,不在容器中注册。
@ComponentScan(value="ewen",
includeFilters = {
@Filter(type=FilterType.CUSTOM, classes={MyTypeFilter.class})
} , useDefaultFilters = false
)
自定义规则(只要含有 “er” 的类)
public class MyTypeFilter implements TypeFilter {
/**
* @param metadataReader:读取到的当前正在扫描的类信息
* @param metadataReaderFactory:可以获取到其他类的任何信息
* @return 当进行包扫描时,使用自定义过滤规则,扫描到的类来与这个方法中定义的规则匹配
* 如果返回true,代表符合规则,将这个类注入到容器中,返回false,代表不符合规则
* @throws IOException
*/
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//获得当前正在扫描类的注解信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取当前正在扫描的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//获取当前正在扫描类的路径
Resource resource = metadataReader.getResource();
String name = classMetadata.getClassName();
if(name.contains("er")){
return true;
}
return false;
}
}
指定bean的作用范围,有如下四个属性:
懒加载,容器启动时,不创建对象,获取时才会创建,只有当是bean是单例的(singleton)时才有用
用在方法上,按照一定条件进行判断,满足条件给容器注册bean,注意需要和@Bean一起使用。也可以把这个注解放在类上,类中的bean满足条件才可以在容器中注册
需要定义一个条件类,实现Condition接口,实现match方法,如果这个方法返回true,则符合条件,注册bean
//如果是Windows操作系统,给容器中注册bill
@Conditional(WindowsCondition.class)
@Bean
public Person person01(){
return new Person("bill", 60);
}
自定义条件类
//判断当前操作系统是不是Windows
public class WindowsCondition implements Condition {
/**
*
* @param conditionContext:判断条件能使用的上下文环境
* @param annotatedTypeMetadata:注释信息
* @return 如果返回true,代表符合条件,返回false,表示不符合条件
*/
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
/*通过conditionContext可以获得很多有用的信息, 可以使用这些信息,来设置很多条件
//1.获取IOC使用的工厂BeanFactory
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
//2.获取类加载器
ClassLoader classLoader = conditionContext.getClassLoader();
//4.获取bean定义的注册类,通过这个类可以注册、查询、移除IOC容器中的bean
BeanDefinitionRegistry registry = conditionContext.getRegistry();
*/
//3. 获取运行时的环境信息
Environment environment = conditionContext.getEnvironment();
//获取当前的操作系统名字
String osName = environment.getProperty("os.name");
//判断当前操作系统是不是Windows 10
if(osName.contains("Windows")){
return true;
}
return false;
}
}
给容器中注册组件(bean),前面已经写了两种:
@Import(类名.class) 快速给容器中导入一个组件,id为类的全路径名,用在类上,属性为class类型的数组
比如,在容器中注册Color、Red类
@Configuration
//@Import:快速在容器中注册组件,id为类的全路径名
@Import({Color.class, Red.class})
public class MainConfig02 {
。。。}
@Configuration
/* @Import:快速在容器中注册组件,id为类的全路径名
类名.class
选择器类.class
*/
@Import({Color.class, Red.class, MyImportSelector.class})
public class MainConfig02 {
。。。}
自定义选择器类
//选择器类
public class MyImportSelector implements ImportSelector {
/**
* @param importingClassMetadata:可以获得@Import标注的类的所有注解信息
* @return 返回一个String数组,里面要注册的bean的全路径名,id是全路径名
*/
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"ewen.bean.Blue"};
}
}
@Configuration
/* @Import:快速在容器中注册组件,id为类的全路径名
类名.class
选择器类.class:实现ImportSelector接口的类
注册类.class:实现ImportBeanDefinitionRegistrar接口的类
*/
@Import({Color.class, Red.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
public class MainConfig02 {
。。。}
注册类
//注册类
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
AnnotationMetadata:@Import标注类的注解信息
BeanDefinitionRegistry:bean的注册类,通过这个类,调用registerBeanDefinition方法
可以手动的在容器中注册bean。也可以用这个类中的方法进行查询、移除bean
*/
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//先判断IOC容器中是否有蓝色和红色,如果都有,就注册一个RainBow
boolean b1 = registry.containsBeanDefinition("ewen.bean.Blue");
boolean b2 = registry.containsBeanDefinition("ewen.bean.Red");
if(b1 && b2){
//指定bean的定义信息(bean的类型、作用域等等)
RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
//指定bean的id
registry.registerBeanDefinition("rainBow",beanDefinition);
}
}
}
使用spring提供的FactoryBean(工厂bean)也可以给容器注册bean。FactoryBean 是一个接口,里面有三个方法,getObject,获取对象,getObjectType,获取对象类型,isSingleton,是否是单例模式。定义一个类实现FactoryBean接口,实现上面三个方法,然后在配置类中,使用@Bean注解来注册这个类,需要注意的是,通过getBean方法获取的是在getObject中生成的对象,而不是FactoryBean对象,可以在name前面加一个“&”获取FactoryBean 对象。
//自定义的FactoryBean,通过这个FactoryBean来给容器注册Bean
public class ColorFactoryBean implements FactoryBean<Color> {
public Color getObject() throws Exception {
return new Color();
}
public Class<?> getObjectType() {
return Color.class;
}
//返回true,是单例,返回false不是单例
public boolean isSingleton() {
return true;
}
}
配置类
@Bean
public ColorFactoryBean colorFactoryBean(){
return new ColorFactoryBean();
}
bean创建—初始化—销毁的过程
我们可以自定义初始化和销毁方法,容器在bean进行到当前生命周期的时候来调用自定义的初始化和销毁方法。
指定bean的初始化和销毁方法
BeanPostProcessor 原理
- 调用populateBean方法给bean赋好值,执行initializeBean方法
- nitializeBean方法中主要执行applyBeanPostProcessorsBeforeInitialization 遍历得到容器中所有的BeanPostProcessor;挨个执行beforeInitialization,一但返回null,跳出for循环,不会执行后面的postProcessBeforeInitialization方法
- 执行完applyBeanPostProcessorsBeforeInitialization ,执行初始化方法,再执行applyBeanPostProcessorsAfterInitialization方法
Spring底层对 BeanPostProcessor 的使用
bean赋值,注入其他组件,@Autowired,生命周期注解功能,@Async等都使用的是xxx BeanPostProcessor。
xml中是在标签下使用进行属性注入的,可以使用@Value注解代替,@Value中,可以写:
自动注入,默认优先根据类型在容器中找对应的组件,将组件赋给@Autowired下的属性,如果容器中有多个相同类型的组件,会将属性名作为id在容器中查找组件。
如果容器中没有相同类型的bean或者指定id的bean,会报错,可以修改required属性为false(默认为true),就不报错了。
@Autowired 还可以用在构造器、方法、参数上,参数默认从容器中获取。
@Resouce 和 @Inject 都可以自动注入,@Resource(name="") 是根据bean的id自动注入的,不支持@Primary来指定首选bean。使用@Inject 要导入javax.inject包,注解里没有属性。这两个注解式java规范的,@Autowired是spring定义的。
自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx),自定义组件类可以实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware,把Spring底层一些组件注入到自定义的Bean中;
xxxAware:底层使用xxxAwareProcessor, 如ApplicationContextAware ==》ApplicationContextAwareProcessor。在bean中获取applicationContext。
//实现ApplicationContextAware接口,可以在Color类中获得applicationContext
//实现BeanNameAware接口,可以获得bean的id
//实现EmbeddedValueResolverAware接口,可以解析字符串
@Component
public class Color implements ApplicationContextAware, BeanNameAware,EmbeddedValueResolverAware {
private ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void setBeanName(String name) {
System.out.println("bean 的name:" + name);
}
//StringValueResolver解析字符串
public void setEmbeddedValueResolver(StringValueResolver resolver) {
String s = resolver.resolveStringValue("你好${os.name} #{18 * 20}");
System.out.println("解析的字符串:" + s);
}
指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件
比如,在tes环境下,将dataSourceTest注册到容器,开发dev环境下,将dataSourceDev注册到容器
参考:
Spring注解驱动教程