<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>4.3.13.RELEASEversion>
dependency>
dependencies>
<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">
<bean id="person" class="win.bojack.bean.Person">
<property name="age" value="33" />
<property name="name" value="zhangsan" />
bean>
beans>
// 老的方式(xml配置文件):
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
Object person = ac.getBean("person");
System.out.println(person)
@Configuration # 这个注解的含义就是告诉spring这是一个配置类.
public class MainConfig {
@Bean("person11") # 这个注解表示这个方法生成一个bean,默认bean的名字就是方法名,当然也可以指定bean name
public Person person() {
return new Person("lisi", 44);
}
}
// 注解方式:
ApplicationContext ac = new AnnotationConfigApplicationContext(MainConfig.class);
Person person = ac.getBean(Person.class);
System.out.println(person);
component-scan的目的主要是扫描指定包同级和下级包中所有标注了@Componet ,@Service ,@Repository,@Controller的类.将它们注册为bean
package win.bojack.dao;
@Repository
public class BookDao {
}
<context:component-scan base-package="win.bojack" />
@ComponentScan("win.bojack") # 指定要扫描的包
ApplicationContext ac = new AnnotationConfigApplicationContext(MainConfig.class);
String[] beanNames = ac.getBeanDefinitionNames();
for (String beanName : beanNames) {
System.out.println(beanName);
}
打印时会将MainConfig也打印出来,因为MainConfig是一个@Configuration ,它是一个@Component. @Component会被@Component-scan自动扫描
@Component-scan会扫描所有标注了@Componet ,@Service ,@Repository,@Controller的类,太过粗暴.可以使用@includeFilters来指定扫描哪些类,@excludeFilters来指定排除哪些类.需要注意的是,用includeFilters之前,要用useDefaultFilters=false将默认的过滤器禁用掉才会生效:
@ComponentScan(value = "win.bojack",
excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})
})
@ComponentScan(value = "win.bojack",
useDefaultFilters = false,
includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,
classes = {Controller.class, Repository.class
})
})
@ComponentScan是一个@Repeatable的,因此可以写多个@ComponentScan.也可以用@ComponentScans,里面包含多个@ComponentScan数组:
@ComponentScans(value = {
@ComponentScan(value = "win.bojack",
useDefaultFilters = false,
includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,
classes = {Controller.class, Repository.class
})
}),
@ComponentScan(value = "win.xxxxx",
useDefaultFilters = false,
includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,
classes = {Controller.class, Repository.class
})
})
})
自定义TypeFilter类:
public class MyTypeFilter implements TypeFilter {
/**
*
* @param metadataReader 读取到的当前正在扫描的类的信息
* @param metadataReaderFactory 可以获取到其他相关类的信息(一般是超类或接口)
* @return
* @throws IOException
*/
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// 获得当前类注解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
// 获得当前正在扫描的类的信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
String className = classMetadata.getClassName();
System.out.println("className ---> " + className);
// 获得当前类的资源
Resource resource = metadataReader.getResource();
//
return className.contains("er");
}
}
然后在配置类中添加如下注解:
@Configuration
@ComponentScan(value = "win.bojack", useDefaultFilters = false, includeFilters = {
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = MyTypeFilter.class)
})
即可实现只注册名称中er的bean
bean的scope有四种:
Object person = ac.getBean("person");
Object person1 = ac.getBean("person");
System.out.println(person == person1);
// 结果为true
编写两个Condition类:
public class WindowsCondition implements Condition {
/**
*
* @param context 判断条件能使用的上下文环境
* @param metadata 注释信息
* @return
*/
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 1获取IOC使用的beanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 2获取类加载器
ClassLoader classLoader = context.getClassLoader();
// 3获取bean定义的注册类
BeanDefinitionRegistry registry = context.getRegistry();
// 4获取当前环境信息
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
return property.contains("Windows");
}
}
public class LinuxCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
return property.contains("Linux");
}
}
编写两个方法,分别返回两个bean:
@Bean("bill")
@Conditional(WindowsCondition.class)
public Person person01() {
return new Person("bill gates" , 66);
}
@Conditional(LinuxCondition.class)
@Bean("linus")
public Person person02() {
return new Person("linus", 48);
}
测试方法:
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MainConfig2.class);
private void printBeanNames() {
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
@Test
public void test3() {
System.out.println("ioc容器创建完成...");
printBeanNames();
Map<String, Person> beansOfType = ac.getBeansOfType(Person.class);
System.out.println(beansOfType);
ConfigurableEnvironment environment = ac.getEnvironment();
String property = environment.getProperty("os.name");
System.out.println(property);
}
bill
{bill=Person{name='bill gates', age=66}}
Windows 10
只打印了: bill.
如果在run中指定参数 -Dos.name=Linux,则只打印linus.
还可以在Condition实现类中添加其他条件,比如检查一个类是否注册,和对类进行注册:
if (registry.containsBeanDefinition("person11")) {
registry.registerBeanDefinition(// TODO);
}
@Conditional注解除了在方法上使用,还可以用在类上, 类中配置的所有bean注册都会生效
给容器注册bean有几种方法:
@Controller/@Service/@Repository/@Component
)@Bean
[导入第3方包里面的组件]@Import
[快速导入组件]
@Import(Color.class)
,容器会注册这个组件,组件名默认是类的全名@ImportSelector
生成一个类,实现ImportSelector
接口, 返回一个数组,包含所有需要导入的类的路径(springboot中用得多)@ImportBeanDefinitionRegistrar
生成一个类,实现MyImportBeanDefinitionRegistrar
接口的registerBeanDefinitions
方法,可以实现一些Bean导入逻辑.@ImportSelector:
// 自定义逻辑返回需要导入的组件
public class MyImportSelector implements ImportSelector {
// 返回值就是导入到容器中的组件类路径
// AnnotationMetadata : 当前标注@Import注解的类的所有注解信息
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 注意这里不要返回null,至少返回一个空数组
return new String[]{"win.bojack.bean.Blue", "win.bojack.bean.Red", "win.bojack.bean.Yellow"};
}
}
在配置类上添加:
@Import({Color.class, MyImportSelector.class})
@ImportBeanDefinitionRegistrar:
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
*
* @param importingClassMetadata : 当前类的注解信息
* @param registry BeanDefinition注册类:
* 把所有需要添加到容器中的bean:调用
* BeanDefinitionRegistry.registerBeanDefinition手工注册进来
*/
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean reg1 = registry.containsBeanDefinition("win.bojack.bean.Red");
boolean reg2 = registry.containsBeanDefinition("win.bojack.bean.Blue");
if (reg1 && reg2) {
// 指定Bean定义信息,{Bean的类型,scope...}
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Rainbow.class);
// 指定Bean名注册一个Bean
registry.registerBeanDefinition("rainBow", rootBeanDefinition);
}
}
}
&colorFactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {
// 返回一个Color对象,这个对象会添加到容器中
public Color getObject() throws Exception {
return new Color();
}
public Class<?> getObjectType() {
return Color.class;
}
// 是否单例
public boolean isSingleton() {
return true;
}
}
在配置类中增加Bean:
@Bean
public ColorFactoryBean colorFactoryBean() {
return new ColorFactoryBean();
}
测试类:
// 这里获取的Bean是colorFactoryBean,打印getClass时却是Color, 因为实际上调用的是ColorFactoryBean中的getObjectType方法, 返回的Bean也是ColorFactoryBean中的getObject方法
Object colorFactoryBean = ac.getBean("colorFactoryBean");
System.out.println(" color 类型: " + colorFactoryBean.getClass());
// 如果想要返回colorFactoryBean,则需要:
Object bean1 = ac.getBean("&colorFactoryBean");
// 在Bean前面加&是BeanFactory这个接口的约定
Bean的生命周期:bean的创建–初始化–销毁的过程
容器管理的bean的生命周期。我们可以自定义初始化和销毁方法:容器在bean进行到当前生命周期某个阶段时,自动调用指定的方法
构造(对象创建)
单实例:在容器启动的时候创建对象
多实例:在每次获取的时候创建对象
执行顺序:
BeanPostProcessor.postProcessBeforeInitialization
初始化:在对象创建完成并赋值好时,调用初始化方法…
BeanPostProcessor.postProcessAfterInitialization
销毁:容器关闭时
单实例:容器关闭的时候
多实例:容器不会管理这个Bean容器不会调用销毁方法(需要手动调用销毁方法)
指定初始化和销毁方法:在xml配置文件时,可以指定
init-method
和destroy-method
,或通过@Bean指定init-method
和destroy-method
;
通过让Bean实现过InitializingBean
接口来实现定义初始化逻辑,和DisposableBean
接口来实现销毁逻辑
通过@PostConstructor
在Bean创建完成并且属性赋值完成时来执行方法
和@PreDestroy在容器销毁Bean之前来执行清理工作
@Configuration
@ComponentScan("win.bojack.bean")
public class MainConfigOfLifeCycle {
// @Scope("prototype")
@Bean(initMethod = "init", destroyMethod = "destroy")
public Car car() {
return new Car();
}
}
public class Car {
public Car() {
System.out.println("car constructor...");
}
public void init() {
System.out.println("car init...");
}
public void destroy() {
System.out.println("car destroy...");
}
}
public void test01() {
// 1,创建IOC容器
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
System.out.println("容器创建完成");
ac.close();
}
打印结果:
car constructor...
car init...
容器创建完成
car destroy...
InitializingBean
DisposableBean:
@Component
public class Cat implements InitializingBean, DisposableBean {
public Cat() {
System.out.println("cat constructor...");
}
public void destroy() throws Exception {
System.out.println("cat destroy...");
}
public void afterPropertiesSet() throws Exception {
System.out.println("cat propertiesSet ...");
}
}
@PostConstruct
@PreDestroy
注解:@Component
public class Dog {
public Dog() {
System.out.println("dog Constructor ... ");
}
@PostConstruct
public void init() {
System.out.println("dog PostConstruct ... ");
}
@PreDestroy
public void destroy() {
System.out.println("dog PreDestroy ... ");
}
}
结果:
cat constructor...
cat propertiesSet ...
dog Constructor ...
dog PostConstruct ...
car constructor...
car init...
容器创建完成
car destroy...
dog PreDestroy ...
cat destroy...
在Bean初始化前后进行一些处理工作,通过实现BeanPostProcessor的两个方法来实现一些功能:
/**
* 后置处理器:在容器初始化前后进行处理工作。对所有组件都生效。
* 将后置处理器加入到容器中
*/
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization +++ " + beanName + "==>" + bean);
// 在这里可以对bean进行一些额外的处理,然后再返回
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization ... " + beanName + "==>" + bean);
return bean;
}
}
结果:
postProcessBeforeInitialization +++ org.springframework.context.event.internalEventListenerProcessor==>org.springframework.context.event.EventListenerMethodProcessor@36f0f1be
postProcessAfterInitialization ... org.springframework.context.event.internalEventListenerProcessor==>org.springframework.context.event.EventListenerMethodProcessor@36f0f1be
postProcessBeforeInitialization +++ org.springframework.context.event.internalEventListenerFactory==>org.springframework.context.event.DefaultEventListenerFactory@6ee12bac
postProcessAfterInitialization ... org.springframework.context.event.internalEventListenerFactory==>org.springframework.context.event.DefaultEventListenerFactory@6ee12bac
postProcessBeforeInitialization +++ mainConfigOfLifeCycle==>win.bojack.config.MainConfigOfLifeCycle$$EnhancerBySpringCGLIB$$39885bd3@64c87930
postProcessAfterInitialization ... mainConfigOfLifeCycle==>win.bojack.config.MainConfigOfLifeCycle$$EnhancerBySpringCGLIB$$39885bd3@64c87930
cat constructor...
postProcessBeforeInitialization +++ cat==>win.bojack.bean.Cat@525f1e4e
cat propertiesSet ...
postProcessAfterInitialization ... cat==>win.bojack.bean.Cat@525f1e4e
dog Constructor ...
postProcessBeforeInitialization +++ dog==>win.bojack.bean.Dog@5ea434c8
dog PostConstruct ...
postProcessAfterInitialization ... dog==>win.bojack.bean.Dog@5ea434c8
car constructor...
postProcessBeforeInitialization +++ car==>win.bojack.bean.Car@1d548a08
car init...
postProcessAfterInitialization ... car==>win.bojack.bean.Car@1d548a08
容器创建完成
car destroy...
dog PreDestroy ...
cat destroy...
可以看出,所有的组件都经过了MyBeanPostProcessor的两个方法的处理
从AnnotationConfigApplicationContext
的初始化开始,一层层地调用,一直到AbstractAutowireCapableBeanFactory.initializeBean
方法,这个方法执行applyBeanPostProcessorsBeforeInitialization
,然后invokeInitMethods
,再执行applyBeanPostProcessorsAfterInitialization
。这两个方法找到所有的BeanPostProcessor
,执行每个beanPostProcesssor
的postProcessBeforeInitialization
和postProcessAfterInitialization
方法并获得结果,如果有一个方法的结果为null
,直接返回结果,不会执行后面的BeanPostProcessor
populateBean(beanName, mbd, instanceWrapper); // 给bean进行属性赋值
initializeBean() {
applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
invokeInitMethods(beanName, wrappedBean, mbd); // 执行自定义初始化
applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
BeanPostProcessor
接口众多的实现类中,有一个ApplicationContextAwareProcessor
,它的作用就是给组件注入IOC容器。如果一个组件想要IOC容器,就可以实现ApplicationContextAware
并实现方法:
private ApplicationContext ac;
public void setApplicationContext(ApplicationContext ac) throws BeansException {
this.ac = ac;
}
然后这个组件就可以很愉快的调用容器本身的一些功能了,很神奇,很蒙B有木有??这正是ApplicationContextAwareProcessor
实现的,它有一个postProcessBeforeInitialization
方法,这个方法判断组件有没有实现ApplicationContextAware
接口,如果有实现的话就调用invokeAwareInterfaces
方法,将组件转成对应的ApplicationContextAware
,再将applicationContext
设置进来。
BeanPostProcessor
还有一些重要的实现类,比如BeanValidationPostProcessor
,在做数据检验时很有用。它可以在初始化前后调用postProcessBeforeInitialization
和postProcessAfterInitialization
再调用doValidate
来实现数据校验功能。
InitDestroyAnnotationBeanPostProcessor
,它处理PostConstruct
和PostDestroy
注解的,实现原理也是通过调用调用postProcessBeforeInitialization
和postProcessAfterInitialization
方法来实现的。
AutowiredAnnotationBeanPostProcessor
:处理@Autowired
标记的组件,原理同上。
总结Spring
底层,通过调用BeanPostProcessor
,实现bean
赋值,注入其他组件,@Autowired
,生命周期注解功能,@Async
,都是通过xxxxBeanPostProcessor
来实现的。
#注意指定编码
// 配置类:
@Configuration
@PropertySource(value = {"classpath:/xxxx.properties"}, encoding = "GBK")
public class MainConfigOfPropertiesValue {
@Value("zhangsan")
private String name;
@Value("#{20-4}")
private Integer age;
@Value("${name.nickName}")
private String nickName;
# xxxx.properties
name.nickName=小张
还可以用这种方式获得properties文件中配置的属性值:
String property = ac.getEnvironment().getProperty("person.nickName");
System.out.println(property);
还可以使用@PropertySources
来指定多个@PropertySource
,因为@PropertySource
是个@Repeatable
的
Spring 利用依赖注入(DI),完成对IOC容器中各个组件的依赖关系赋值
1、@Autowired
,自动注入
默认优先按照类型去容器中找对应的组件,如果找到就进行赋值。applicationContext.getBean(BookDao.class);
如果找到多个相同类型的组件,再将属性作为组件的id去容器中查找applicationContext.getBean("bookDao");
@Qualifier("bookDao3")
使用@Qualifier
指定需要装配的组件的id,而不是使用属性名
自动装配一定要将属性赋值好,没有就会报错。也可以使用@Autowired(required= false)
来实现非必须的依赖,也就是找得到组件就自动装配,没有找到就不装配。
可以使用@Primary
设置首选的组件
2、Spring还支持@Resource
(JSR250规范)和@Inject
(JSR330规范)
@Resource
也能实现自动装配功能,默认是按照组件名称来装配的。没有Primary功能也没有Autowired(required=xx)的功能
@Inject
需要导入javax.inject
依赖,功能和@Autowired
类似,@Inject
没有任何属性,因此不太好用
@Autowired
是Spring定义的,@Resource
和@Inject
都是Java规范
@Autowired
是利用AutowiredAnnotationBeanPostProcessor
来实现自动装配的
3、 @Autowired
可以标注在方法上,也可以标注在构造方法,或者构造方法的参数上。如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略。参数位置的组件还是可以从容器中自动获取
4、自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,…),就可以自定义组件实现xxxAware:在创建对象的时候,会调用 接口规定的方法来注入相关的组件。有一个叫Aware的总接口,它是利用一种像回调机制的方式来实现自动调用。
自定义组件实现xxxAware:在创建对象的时候,会调用接口规定的方法注入相关的组件;Aware,把Spring底层的一些组件注入到自定义的Bean中;
每一个xxxAware都有对应的xxxBeanPostProcessor
:
比如ApplicationContextAware
对应着ApplicationContextAwareProcessor
,后者就是一个BeanPostProcessor