IOC的使用 @Configuration是入口 在Spring中,在一个类的上面增加@Configuration
注解,这个就表示当前为配置类,可以代替xml
形式的配置形式。在类中可以定义各种bean的定义,这些定义会被AnnotationConfigApplicationContext
容器扫描。
1 2 3 4 5 @Configuration public class RzConfig {}
注入的@Configuration
对象可以通过configApplicationContext.getBeanDefinitionNames()
方法获得对应的集合
1 2 3 4 5 AnnotationConfigApplicationContext configApplicationContext=new AnnotationConfigApplicationContext(RzConfig.class); String[] beanDefinitionNames = configApplicationContext.getBeanDefinitionNames(); for (int i = 0 ; i < beanDefinitionNames.length; i++) { RzLogger.info("获得注入的类:" +beanDefinitionNames[i]); }
1 2 3 4 5 6 23:24:18,150 INFO [main] (RzLogger.java:12) - 获得注入的类:org.springframework.context.annotation.internalConfigurationAnnotationProcessor 23:24:18,154 INFO [main] (RzLogger.java:12) - 获得注入的类:org.springframework.context.annotation.internalAutowiredAnnotationProcessor 23:24:18,154 INFO [main] (RzLogger.java:12) - 获得注入的类:org.springframework.context.annotation.internalCommonAnnotationProcessor 23:24:18,154 INFO [main] (RzLogger.java:12) - 获得注入的类:org.springframework.context.event.internalEventListenerProcessor 23:24:18,154 INFO [main] (RzLogger.java:12) - 获得注入的类:org.springframework.context.event.internalEventListenerFactory 23:24:18,154 INFO [main] (RzLogger.java:12) - 获得注入的类:rzConfig
@Bean的使用 @Bean
代表一个类实体,可以定义一个Id
,类似代码:
1 2 3 <beans > <bean id ="xxx" class ="com.xxx.xxx" /> </beans >
可以在@Configuration
定义一个@Bean
对象:
1 2 3 4 @Bean public Person getPerson () { return new Person(); }
扫描出来的实例:
1 2 3 4 5 6 7 8 23:31:50,092 INFO [main] (RzLogger.java:12) - 获得注入的类:org.springframework.context.annotation.internalConfigurationAnnotationProcessor 23:31:50,094 INFO [main] (RzLogger.java:12) - 获得注入的类:org.springframework.context.annotation.internalAutowiredAnnotationProcessor 23:31:50,094 INFO [main] (RzLogger.java:12) - 获得注入的类:org.springframework.context.annotation.internalCommonAnnotationProcessor 23:31:50,094 INFO [main] (RzLogger.java:12) - 获得注入的类:org.springframework.context.event.internalEventListenerProcessor 23:31:50,095 INFO [main] (RzLogger.java:12) - 获得注入的类:org.springframework.context.event.internalEventListenerFactory 23:31:50,095 INFO [main] (RzLogger.java:12) - 获得注入的类:rzConfig 23:31:50,095 INFO [main] (RzLogger.java:12) - 获得注入的类:getPerson
对于Spring中的@Bean
来说,如果一般不特殊指定Id,id是获得对象的方法名。
对于容器中创建的对象,都是单例模式。在容器创建完成的时候,就已经把实例创建完成了。 可以通过getBean
获取多次实例,来证明是不是同一个对象:
1 2 3 Person person = configApplicationContext.getBean(Person.class); Person person1 = configApplicationContext.getBean(Person.class); System.out.println(person==person1);
运行结果为true
@Lazy实现懒加载 在创建Bean对象的时候,在Person
类的构造函数中增加一个打印log的日志:
1 2 3 public Person () { RzLogger.info("创建Person实例" ); }
打印结果:
1 2 3 4 5 创建Person实例 .... 23:37:13,800 INFO [main] (RzLogger.java:12) - 获得注入的类:rzConfig 23:37:13,800 INFO [main] (RzLogger.java:12) - 获得注入的类:getPerson true
可以看出,并不是在bean使用的时候才创建类的对象,而是使用已经创建好的类。
@Lazy
从字面上意思可以理解为懒加载,为了就是在使用的时候,才真正的创建实例(不推荐使用)。
1 2 3 4 5 @Lazy @Bean public Person getPerson () { return new Person(); }
1 2 3 4 5 6 ..... 23:45:17,716 INFO [main] (RzLogger.java:12) - 获得注入的类:rzConfig 23:45:17,717 INFO [main] (RzLogger.java:12) - 获得注入的类:getPerson 23:45:17,745 INFO [main] (RzLogger.java:12) - 创建Person实例 true
@Scope实现多例 @Scope
代表对象在spring容器(IOC容器)中的生命周期,也可以理解为对象在spring容器中创建方式。 实现的方式有很多:
1 2 3 4 5 6 7 8 9 10 @Scope(value=ConfigurableBeanFactory.SCOPE_PROTOTYPE) 这个是说在每次注入的时候回自动创建一个新的bean实例@Scope(value=ConfigurableBeanFactory.SCOPE_SINGLETON) 单例模式,在整个应用中只能创建一个实例@Scope(value=WebApplicationContext.SCOPE_GLOBAL_SESSION) 全局session中的一般不常用@Scope(value=WebApplicationContext.SCOPE_APPLICATION) 在一个web应用中只创建一个实例@Scope(value=WebApplicationContext.SCOPE_REQUEST) 在一个请求中创建一个实例@Scope(value=WebApplicationContext.SCOPE_SESSION) 每次创建一个会话中创建一个实例proxyMode=ScopedProxyMode.INTERFACES创建一个JDK代理模式 proxyMode=ScopedProxyMode.TARGET_CLASS基于类的代理模式 proxyMode=ScopedProxyMode.NO(默认)不进行代理
在@Bean注解上在增加一个@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
注解,可以实现多实例的注入:1 2 3 4 5 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) @Bean public Person getPerson () { return new Person(); }
执行结果:
1 2 3 4 5 6 7 ..... 23:53:21,543 INFO [main] (RzLogger.java:12) - 获得注入的类:rzConfig 23:53:21,544 INFO [main] (RzLogger.java:12) - 获得注入的类:getPerson 23:53:21,583 INFO [main] (RzLogger.java:12) - 创建Person实例 23:53:21,587 INFO [main] (RzLogger.java:12) - 创建Person实例 false
@ComponentScan @ComponentScan
注解是告诉Spring 从哪里可以找到Bean,一旦被指定了,Spring将会将在被指定的包及其下级的包 (sub packages)中寻找bean。
1 @ComponentScan(value = "learnJava.spring.*")
Filter 如果说@ComponentScan
告诉了在哪能够找到Bean
,则Filter
是告诉如何去注入@Bean
. 例如:
1 2 3 4 5 ANNOTATION:注解类型 ASSIGNABLE_TYPE:ANNOTATION:指定的类型 ASPECTJ:按照Aspectj的表达式,基本上不会用到 REGEX:按照正则表达式 CUSTOM:自定义规则
使用CUSTOM
规则的时候,需要实现TypeFilter
接口。例如, 加载Controller
结尾的类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class SpRzTypeFilters implements TypeFilter { @Override public boolean match (MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); ClassMetadata classMetadata = metadataReader.getClassMetadata(); Resource resource = metadataReader.getResource(); String clazzName=classMetadata.getClassName(); RzLogger.info("自定义Type获得当前类的信息:" ,clazzName); if (clazzName.endsWith("Controller" ))return true ; return false ; } }
1 2 3 4 5 6 7 8 @Configuration @ComponentScan(value = "learnJava.spring.*",includeFilters = { @ComponentScan.Filter(type = FilterType.CUSTOM,classes = {SpRzTypeFilters.class}) },useDefaultFilters = false) public class RzConfig02 {}
@Conditional实现条件注入 @Conditional
可以根据Condition
的返回值来决定要不要注入当前变量
Condition
是一个函数式接口,内部只有一个matches方法。Spring是否注入,根据这个函数的返回值true/false
.
一个场景 如果我们想实现一个在Linux和Window下分别加载不同的Config的功能。首先我们先实现两个Condition
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public class LinuxCondition implements Condition { @Override public boolean matches (ConditionContext context, AnnotatedTypeMetadata metadata) { ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); ClassLoader classLoader = context.getClassLoader(); Environment environment = context.getEnvironment(); BeanDefinitionRegistry registry = context.getRegistry(); RzLogger.infoObject(context); RzLogger.infoObject(metadata); String env=environment.getProperty("os.name" ); if (env.contains("Linux" )){ return true ; } return false ; } } public class WinCondition implements Condition { @Override public boolean matches (ConditionContext context, AnnotatedTypeMetadata metadata) { ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); ClassLoader classLoader = context.getClassLoader(); Environment environment = context.getEnvironment(); BeanDefinitionRegistry registry = context.getRegistry(); RzLogger.infoObject(context); RzLogger.infoObject(metadata); String env=environment.getProperty("os.name" ); if (env.contains("Windows" )){ return true ; } return false ; } }
在@Bean的注解的基础上再增加一个Condition
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Configuration public class RzConfig07 { @Bean("Windows") @Conditional({WinCondition.class}) public WinConfig getWinConfig () { return new WinConfig(); } @Bean("Linux") @Conditional(value = {LinuxCondition.class}) public LinuxConfig getLinuxConfig () { return new LinuxConfig(); } }
当执行注入的时候,容器会根据String env=environment.getProperty("os.name");
来判断是加载哪一个Bean
@Import的用法 @Import
的注解是@Bean
注解的高级用法。@Import
提供了可以直接导入和高级导入的方式,也可以很简单的实现自定义注解实现注入对象,提供了第三方组件与Spring集成的方法。
ImportSelector导入数组类 使用可以单个直接导入
1 2 3 @Import({Dog.class, Cat.class})
如果想导入的类一次性很多,需要使用 ImportSelector
接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class RzImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{ "learnJava.spring.sprz08.Fish" }; } }
导入时需要声明RzImportSelector
的类型:
1 2 3 @Import({Dog.class, Cat.class, RzImportSelector.class})
ImportBeanDefinitionRegistrar的使用 如果在导入的时候,我们需要对类对象进项一些高级操作
,比如:C类对AB类都有依赖,如果必须要等到AB加载完,再加载C
ImportBeanDefinitionRegistrar
接口提供了registerBeanDefinitions
方法,该方法有两个参数:AnnotationMetadata importingClassMetadata: 当前类的注册信息 BeanDefinitionRegistry registry:BeanDefinition:注册类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class RzImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions (AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AnnotationAttributes attributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MyImportAnnotation.class.getName())); boolean dogRegister = registry.containsBeanDefinition("learnJava.spring.sprz08.Dog" ); boolean catRegister = registry.containsBeanDefinition("learnJava.spring.sprz08.Cat" ); if (dogRegister && catRegister) { RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Pig.class); MutablePropertyValues propertyValues = new MutablePropertyValues(); rootBeanDefinition.setPropertyValues(propertyValues); registry.registerBeanDefinition("pig" , rootBeanDefinition); } } }
同样需要在入口出增加@Import({RzImportBeanDefinitionRegistrar.class})
自定义注解实现注入 自定义注解的使用,可以是第三方组件能够集成到Spring的框架中,可以基于RzImportBeanDefinitionRegistrar
简单的实现一个注解:
1 2 3 4 5 @Retention(RetentionPolicy.RUNTIME) @Import({RzImportBeanDefinitionRegistrar.class}) public @interface MyImportAnnotation { }
使用的时候直接使用@MyImportAnnotation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Configuration @MyImportAnnotation public class RzConfig08 { @Bean public RzFactoryBean getRzFactoryBean () { return new RzFactoryBean(); } }
FactoryBean的使用 在Spring创建对象的时候,一般都是都是通过反射的方式来创建,某些情况下创建实体的Bean的过程比较复杂,就需要允许调用方实现实例的创建方法。用户可以通过实现FactoryBean
接口来实现创建类实体的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class RzFactoryBean implements FactoryBean <Eal > { @Override public Eal getObject () throws Exception { RzLogger.info("创建Eal实例" ); return new Eal(); } @Override public Class<?> getObjectType() { return Eal.class; } @Override public boolean isSingleton () { return true ; } }
使用时与正常的类相同,直接用@Import
导入即可。
@Value的使用 @Value
主要是给字段赋值,主要有3中方式:
直接赋值
通过SPEL
表达式
读取配置文件中的值
1 2 3 4 5 6 7 8 @Value("Rz") private String userName;@Value("付威") private String userRealName;@Value("#{20-2}") private Integer age;@Value("${Author.version}") private String version;
几种注入的方式优先级 和不同 在注入的注解中,有几种方式:@Autowired
,@Resource
,@Inject
,对于他们需要了解一些使用上的区别:
@Autowired
:@Autowired
按byType自动注入(可以通过@Qualifier
实现ByName注入),通过AutowiredAnnotationBeanPostProcessor
类实现的依赖注入,可以再构造函数和Setter上面使用,注入的优先级最高,@Autowired(required = false)
支持没找到类不报错,推荐使用。
@Resource
:@Resource
默认按name注入,可以通过name和type属性进行选择性注入
@Inject
: @Inject
是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Named
@Qualifier和@Primary 如果在容器中有两个类的不同实现时,会产生Bean冲突的异常。使用@Qualifier和@Primary
可以定义优先使用的Bean
使用Qualifier注解,选择一个对象的名称,通常比较常用
Primary可以理解为默认优先选择,不可以同时设置多个,内部实质是设置BeanDefinition的primary属性