基于xml配置文件的Spring运用:
传统的Spring做法是使用xml文件来对bean进行注入或者是配置aop、事物,这么做有两个缺点:
1、如果所有的内容都配置在xml文件中,那么.xml文件将会十分庞大;如果按需求分开.xml文件,那么.xml文件又会非常多。总之这将导致配置文件的可读性与可维护性变得很低。
2、在开发中在.java文件和xml文件之间不断切换,是一件麻烦的事,同时这种思维上的不连贯也会降低开发的效率。
//获取配置文件生成的IOC容器
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("beans.xml");
为了解决这两个问题,Spring引入了注解,通过"@XXX"的方式,让注解与Java Bean紧密结合,既大大减少了配置文件的体积,又增加了Java Bean的可读性与内聚性。
基于注解的Spring运用:
@Configuration: 告诉Spring这是一个配置类,替代传统的xml配置文件,是配置类的最基本组件。在配置类中配合@Import、@Conditional、@Bean等等注解可以向容器中自定义的添加各类组件。
//获取配置类生成的IOC容器
ApplicationContext applicationContext =
new AnnotationConfigApplicationContext(MainConfig.class);
@Import: 快速导入组件,包括配置类。
1.Class : id默认是组件的全类名
2.ImportSelector :返回需要导入的全类名数组
3.ImportBeanDefinitionRegistrar :bean的定义注册器
代码示例:
1.定义三个将要被@Import导入的类
public class Red extends Color{
}
/**
* 自定义返回需要导入的组件
*/
public class MySelector implements ImportSelector {
//返回值就是要导入到容器中的组件全类名数组,
//AnnotationMetadata:当前标注@Import注解的配置类的所有注解信息
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{
"com.atguigu.bean.ioc.Blue", "com.atguigu.bean.ioc.Yellow"
};
}
}
/**
* 实现ImportBeanDefinitionRegistrar接口,使其拥有注册bean的功能
* AnnotationMetadata:当前标注@Import注解的配置类的所有注解信息
* BeanDefinitionRegistry:配置类对应的IOC容器注册器
*/
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
registry.registerBeanDefinition("yellow",new RootBeanDefinition(Yellow.class));
}
}
2.定义配置类
@Configuration
@Import({Red.class, MySelector.class, MyImportBeanDefinitionRegistrar.class})
public class MainConfig {
}
3.测试
@Test
public void testScope(){
ApplicationContext applicationContext =
new AnnotationConfigApplicationContext(MainConfig.class);
String[] beans = applicationContext.getBeanDefinitionNames();
for (String bean : beans) {
System.out.println(bean);
}
}
@ImportResource: 向配置类中导入xml配置文件。
@ComponentScan: 代替配置文件中的
@Bean:注入组件,默认的组件名为首字母小写的类的名称。代替配置文件中的
@Scope:
singleton:单实例,容器启动的时候直接创建单个对象放到IOC容器中。
prototype:多实例,每次获取的时候才会创建对象。
@Lazy:懒加载,针对单实例bean,容器启动不默认创建对象,第一次获取bean的时候才创建对象并初始化
@Conditional:可标注在类上或者方法上,按照一定条件进行判断,满足条件给容器中注册bean,否则不对类或者方法做处理。此注解在Springboot的自动配置中有大量应用。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
/**
* All {@link Condition}s that must {@linkplain Condition#matches match}
* in order for the component to be registered.
*/
Class extends Condition>[] value();
}
从代码中可以看到,使用@Conditional 注解,需要传入一个Class数组,并且数组内元素需要实现Condition接口。
如何运用呢?首先我们自定义两个Condition接口的实现类以及一个需要被按条件注入的bean组件。
- 定义实现类
public class WindowCondition implements Condition {
/**
* ConditionContext: 上下文环境,包含容器的环境信息、注册器、资源加载器
* AnnotatedTypeMetadata: 当前标注了@Condition注解的注释信息
*/
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
ClassLoader classLoader = context.getClassLoader();
Environment environment = context.getEnvironment();
BeanDefinitionRegistry registry = context.getRegistry();
ResourceLoader resourceLoader = context.getResourceLoader();
System.out.println(beanFactory);
System.out.println(classLoader);
System.out.println(environment);
System.out.println(registry);
System.out.println(resourceLoader);
//判断是否window系统
if (environment.getProperty("os").contains("Window")){
return true;
}
return false;
}
}
public class LinuxCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String os = environment.getProperty("os");
if (os.contains("linux")){
return true;
}
return false;
}
}
- 定义将被按条件注册的实体bean
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dept {
private Integer deptNo;
private String deptName;
}
- 然后在配置类运用它。
@Configuration
@Import({Color.class, Red.class, MySelector.class, MyImportBeanDefinitionRegistrar.class})
@Conditional(WindowCondition.class) //如果不满足条件,则配置类失效
@ComponentScan(value = "com.atguigu.ioc"
,includeFilters ={@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class,Repository.class})}
,useDefaultFilters = false)
public class MainConfig {
/**
* @Scope:单实例:容器启动的时候直接创建单个对象放到IOC容器中。多实例:每次获取的时候才会创建对象。
* @Bean:给容器注册一个bean,类型为返回值类型,id默认是方法名,默认单例的。
* @Lazy:懒加载,针对单实例bean,容器启动不默认创建对象,第一次获取bean的时候才创建对象并初始化
*/
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
@Lazy
public Person person(){
return new Person("李四",30,"设计师");
}
/**
* @Conditional: 按照一定条件进行判断,满足条件给容器中注册bean
*/
@Bean("window")
public Dept dept01(){
return new Dept(1001,"设计部");
}
@Bean("linux") //@Bean标注的方法,IOC容器在创建对象的时候,参数直接从IOC容器中获取
@Conditional(LinuxCondition.class)
public Dept dept02(){
return new Dept(1002,"开发部");
}
}
@PropertySource:加载指定配置文件。一般和@Value配合使用为IOC容器中的bean组件属性赋值。@PropertySource相当于在xml文件中配置:
- 在类路径下定义一个配置文件,score.properties
score.stuId=20100514
score.score=98
- 在组件中,定义Score组件
@Data
@NoArgsConstructor
@AllArgsConstructor
@PropertySource("classpath:score.properties") //加载配置文件
public class Score {
@Value("${score.stuId}") //通过el表达式为组件赋值
private Integer stuId;
@Value("${score.score}")
private Integer score;
}
- 定义配置文件,注入Score组件
@Configuration
@Import(Score.class)
public class MainConfigOfPropertyValue {
}
- 测试
@Test
public void testValue2(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfPropertyValue.class);
Score bean = applicationContext.getBean(Score.class);
System.out.println(bean ); //Score(stuId=20100514, score=98)
ConfigurableEnvironment environment =
((AnnotationConfigApplicationContext) applicationContext).getEnvironment();
String property = environment.getProperty("score.score");
System.out.println(property); //98
}