org.springframework
spring-context
4.3.12.RELEASE
我们一起注入一个bean使用xml来配置
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="person" class="com.cuzz.bean.Person">
<property name="name" value="cuzz">property>
<property name="age" value="18">property>
bean>
beans>
我可以使用ClassPathXmlApplicationContext
来获取
/**
*
* @Date: 2018/9/23 10:48
* @Description:
*/
public class MainTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
// 用id获取
Person bean = (Person) applicationContext.getBean("person");
System.out.println(bean);
}
}
输出Person(name=cuzz, age=18)
编写一个配置类
/**
*
* @Date: 2018/9/23 10:55
* @Description: 配置类
*/
//配置类==配置文件
@Configuration // 告诉Spring这是一个配置类
public class MainConfig {
// 给容器中注册一个Bean,类型为返回值类型,id默认用方法名
// 也可以指定id
@Bean(value = "person01")
public Person person() {
return new Person("vhsj", 16);
}
}
可以通过AnnotationConfigApplicationContext
来获取,并且获取id
/**
*
* @Date: 2018/9/23 10:59
* @Description:
*/
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
Person person = (Person) context.getBean(Person.class);
System.out.println(person);
String[] names = context.getBeanNamesForType(Person.class);
for (String name: names) {
System.out.println(name);
}
}
}
输出
Person(name=vhsj, age=16)
person01
由于给bean添加一个一个value,可以改变默认id
只要标注了注解就能扫描到如:@Controller @Service @Repository @component
<context:component-scan base-package="com.cuzz">context:component-scan>
在配置类中添加
/**
*
* @Date: 2018/9/23 10:55
* @Description: 配置类
*/
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.cuzz") // 指定包
public class MainConfig {
}
添加controller、service等
测试
/**
*
* @Date: 2018/9/23 13:03
* @Description:
*/
public class IOCTest {
@Test
public void test01() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
// 获取所有bean定义的名字
String[] beanNames = applicationContext.getBeanDefinitionNames();
for (String name : beanNames) {
System.out.println(name);
}
}
}
输出结果
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
bookController
bookDao
bookService
person01
可以看出添加@Controller @Service @Repository @component注解的都可以扫描到
还可以指定添加某些类,和排除某些类,进入ComponentScan注解中有下面两个方法
ComponentScan.Filter[] includeFilters() default {};
ComponentScan.Filter[] excludeFilters() default {};
includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件
excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件
配置类,排除Controller
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.cuzz", excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
})
public class MainConfig {
}
运行测试方法,可以得出没有Controller类的
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
bookDao
bookService
person01
第一和第二比较常用
@ComponentScans(
value = {
@ComponentScan(value="com.atguigu",includeFilters = {
/* @Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}),*/
@Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})
},useDefaultFilters = false)
}
)
//@ComponentScan value:指定要扫描的包
//excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件
//includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件
FilterType.ANNOTATION:按照注解
FilterType.ASSIGNABLE_TYPE:按照给定的类型;
FilterType.ASPECTJ:使用ASPECTJ表达式
FilterType.REGEX:使用正则指定
FilterType.CUSTOM:使用自定义规则
新建一个MyTypeFilte类实现TypeFilter接口
/**
*
* @Date: 2018/9/23 15:03
* @Description:
*/
public class MyTypeFilter implements TypeFilter{
/**
* metadataReader:读取到的当前正在扫描的类的信息
* metadataReaderFactory:可以获取到其他任何类信息的
*/
@Override
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);
// 这些类名中包含er就返回true
if(className.contains("er")){
return true;
}
return false;
}
}
使用自定义注解记得需要关闭默认过滤器useDefaultFilters = false
/**
*
* @Date: 2018/9/23 10:55
* @Description: 配置类
*/
@Configuration
@ComponentScan(value = "com.cuzz",
includeFilters = @ComponentScan.Filter(type = FilterType.CUSTOM,
classes = MyTypeFilter.class),
useDefaultFilters = false)
public class MainConfig {
// 给容器中注册一个Bean,类型为返回值类型,id默认用方法名
// 也可以指定id
@Bean(value = "person01")
public Person person() {
return new Person("vhsj", 16);
}
}
测试
--->com.cuzz.AppTest
--->com.cuzz.bean.MainTest
--->com.cuzz.config.IOCTest
--->com.cuzz.config.MainTest
--->com.cuzz.App
--->com.cuzz.bean.Person
--->com.cuzz.config.MyTypeFilter
--->com.cuzz.controller.BookController
--->com.cuzz.dao.BookDao
--->com.cuzz.sevice.BookService
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig // 不是扫描的
person // 这个是在bean中
myTypeFilter // 有er
bookController // 有er
bookService // 有er
person01 // 这个是在bean中
@Test
public void test02() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
// 获取所有bean定义的名字
String[] beanNames = applicationContext.getBeanDefinitionNames();
for (String name : beanNames) {
System.out.println(name);
}
Object bean = applicationContext.getBean("person");
Object bean2 = applicationContext.getBean("person");
System.out.println(bean == bean2); // 输出true
}
ConfigurableBeanFactory#SCOPE_PROTOTYPE // 多实例 每次获取时创建对象,不会放在ioc容器中
ConfigurableBeanFactory#SCOPE_SINGLETON // 单实例 ioc容器启动是创建对象,以后从容器中获取
WebApplicationContext#SCOPE_REQUEST // web同一次请求创建一个实例
WebApplicationContext#SCOPE_SESSION // web同一个session创建一个实例
如果我们把Scope修改
/**
*
* @Date: 2018/9/23 15:40
* @Description:
*/
@Configuration
public class MainConfig2 {
/**
* ConfigurableBeanFactory#SCOPE_PROTOTYPE
* @see ConfigurableBeanFactory#SCOPE_SINGLETON
* @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST request
* @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION sesssion
* @return\
* @Scope:调整作用域
* prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。
* 每次获取的时候才会调用方法创建对象;
* singleton:单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中。
* 以后每次获取就是直接从容器(map.get())中拿,
* request:同一次请求创建一个实例
* session:同一个session创建一个实例
*
* 懒加载:
* 单实例bean:默认在容器启动的时候创建对象;
* 懒加载:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化;
*
*/
@Scope(value = "prototype")
@Bean
public Person person() {
return new Person("vhuj", 25);
}
}
则测试输出false
懒加载的是针对单实例Bean,默认是在容器启动的时创建的,我们可以设置懒加载容器启动是不创建对象,在第一次使用(获取)Bean创建对象,并初始化
先给添加一个@Lazy注解
@Configuration
public class MainConfig2 {
@Lazy
@Bean
public Person person() {
System.out.println("给容器中添加Person...");
return new Person("vhuj", 25);
}
}
编写一个测试方法
@Test
public void test03() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
System.out.println("ioc容器创建完成...");
Object bean = applicationContext.getBean("person");
}
输出
ioc容器创建完成...
给容器中添加Person...
添加一个@Lazy是在第一次获取时,创建对象,以后获取就不需要创建了,直接从容器中获取,因为它是单实例
按照一定条件进行判断,满足条件给容器中注册Bean
如果系统是windows,给容器中注入"bill"
如果系统是linux,给容器中注入"linus"
编写WindowCondition类并重写matches方法
/**
*
* @Date: 2018/9/23 20:30
* @Description: 判断是否是windows
*/
public class WindowCondition implements Condition{
/**
* @param context 判断条件
* @param metadata 注释信息
* @return boolean
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
if (property.contains("Windows")) {
return true;
}
return false;
}
}
context有以下方法
// 能获取ioc使用的beanfactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 能获取到类加载器
ClassLoader classLoader = context.getClassLoader();
// 获取到环境变量
Environment environment = context.getEnvironment();
// 获取到Bean定义的注册类
BeanDefinitionRegistry registry = context.getRegistry();
添加Bean添加Condition条件
@Configuration
public class MainConfig2 {
@Conditional({WindowCondition.class})
@Bean("bill")
public Person person01() {
return new Person("Bill Gates", 60);
}
@Conditional({LinuxCondition.class})
@Bean("linux")
public Person person02() {
return new Person("linus", 45);
}
}
@Test
public void test04() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
// 获取环境变量
ConfigurableEnvironment environment = applicationContext.getEnvironment();
String property = environment.getProperty("os.name");
System.out.println(property);
// 获取所有bean定义的名字
String[] beanNames = applicationContext.getBeanDefinitionNames();
for (String name : beanNames) {
System.out.println(name);
}
// key 是id
Map<String, Person> map = applicationContext.getBeansOfType(Person.class);
System.out.println(map);
}
发现只有“bill”这个Bean被注入
Windows 7
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig2
bill
{bill=Person(name=Bill Gates, age=60)}
@Import可以导入第三方包,或则自己写的类,比较方便,Id默认为全类名
比如我们新建一个类
/**
*
* @Date: 2018/9/23 21:08
* @Description:
*/
public class Color {
}
我们只需要在配置类添加一个@Import把这个类导入
@Import({Color.class})
@Configuration
public class MainConfig2 {}
返回导入组件需要的全类名的数组
public interface ImportSelector {
/**
* Select and return the names of which class(es) should be imported based on
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
编写一个MyImportSelector类实现ImportSelector接口
/**
*
* @Date: 2018/9/23 21:15
* @Description:
*/
public class MyImportSelector implements ImportSelector{
// 返回值就导入容器组件的全类名
// AnnotationMetadata:当前类标注的@Import注解类的所有注解信息
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[] {"com.cuzz.bean.Car"};
}
}
在配置类中,通过@Import导入
/**
*
* @Date: 2018/9/23 15:40
* @Description: 配置类
*/
@Import({Color.class, MyImportSelector.class})
@Configuration
public class MainConfig2 {}
测试结果,com.cuzz.bean.Car
注入了
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig2
com.cuzz.bean.Color
com.cuzz.bean.Car
public interface ImportBeanDefinitionRegistrar {
/**
* Register bean definitions as necessary based on the given annotation metadata of
* the importing {@code @Configuration} class.
* Note that {@link BeanDefinitionRegistryPostProcessor} types may not be
* registered here, due to lifecycle constraints related to {@code @Configuration}
* class processing.
* @param importingClassMetadata annotation metadata of the importing class
* @param registry current bean definition registry
*/
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}
编写一个ImportBeanDefinitionRegistrar实现类
/**
*
* @Date: 2018/9/23 21:29
* @Description:
*/
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* @param importingClassMetadata 当前类的注解信息
* @param registry 注册类
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 查询容器
boolean b = registry.containsBeanDefinition("com.cuzz.bean.Car");
// 如果有car, 注册一个汽油类
if (b == true) {
// 需要添加一个bean的定义信息
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Petrol.class);
// 注册一个bean, 指定bean名
registry.registerBeanDefinition("petrol", rootBeanDefinition);
}
}
}
配置类
/**
*
* @Date: 2018/9/23 15:40
* @Description: 配置类
*/
@Import({Color.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
@Configuration
public class MainConfig2 {}
测试结果,出现了petrol
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig2
com.cuzz.bean.Color
com.cuzz.bean.Car
petrol
/**
* 给容器中注册组件;
* 1)、包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的类]
* 2)、@Bean[导入的第三方包里面的组件]
* 3)、@Import[快速给容器中导入一个组件]
* 1)、@Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名
* 2)、ImportSelector:返回需要导入的组件的全类名数组;
* 3)、ImportBeanDefinitionRegistrar:手动注册bean到容器中
* 4)、使用Spring提供的 FactoryBean(工厂Bean);
* 1)、默认获取到的是工厂bean调用getObject创建的对象
* 2)、要获取工厂Bean本身,我们需要给id前面加一个&
* &colorFactoryBean
*/
编写一个ColorFactoryBean类
/**
*
* @Date: 2018/9/23 21:55
* @Description: Spring定义的工厂Bean
*/
public class ColorFactoryBean implements FactoryBean<Color> {
// 返回一个Color对象
@Override
public Color getObject() throws Exception {
return new Color();
}
@Override
public Class<?> getObjectType() {
return Color.class;
}
// 是否为单例
@Override
public boolean isSingleton() {
return true;
}
}
注入到容器中
@Bean
public ColorFactoryBean colorFactoryBean() {
return new ColorFactoryBean();
}
测试
@Test
public void test05() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
Object bean = applicationContext.getBean("colorFactoryBean");
// 工厂bean调用的是getClass()方法
System.out.println("colorFactoryBean的类型是: " + bean.getClass());
}
输出,发现此时的bean调用的方法是getObjectType方法
colorFactoryBean的类型是: class com.cuzz.bean.Color
如果需要获取BeanFactory本身,可以在id前面加一个“&”标识
@Test
public void test05() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
Object bean = applicationContext.getBean("colorFactoryBean");
// 工厂bean调用的是getClass()方法
System.out.println("colorFactoryBean的类型是: " + bean.getClass());
Object bean2 = applicationContext.getBean("&colorFactoryBean");
// 工厂bean调用的是getClass()方法
System.out.println("colorFactoryBean的类型是: " + bean2.getClass());
}
此时输出
colorFactoryBean的类型是: class com.cuzz.bean.Color
colorFactoryBean的类型是: class com.cuzz.bean.ColorFactoryBean
Bean的创建、初始化和销毁是由容器帮我们管理的
我们可以自定义初始化和销毁方法,容器在进行到当前生命周期的时候来调用我买自定义的初始化和销毁方法
构造(对象创建)
单实例: 在容器启动的时候创建
多实例: 在每次获取的时候创建对象
**初始化:**对象创建完成后,并赋值化,调用初始化方法
**销毁:**单实例是在容器关闭的时候销毁,多实例容器不会管理这个Bean,容器不会调用销毁方法
编写一个Car类
/**
*
* @Date: 2018/9/23 21:20
* @Description:
*/
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...");
}
}
在xml中我们可以指定init-method
和destroy-method
方法,如
<bean id="car" class="com.cuzz.bean.Car" init-method="init" destroy-method="destroy">bean>
使用注解我们可以
/**
*
* @Date: 2018/9/24 12:49
* @Description: 配置类
*/
@Configuration
public class MainConfigOfLifecycle {
@Bean(initMethod = "init", destroyMethod = "destroy")
public Car car() {
return new Car();
}
}
测试
/**
*
* @Date: 2018/9/24 13:00
* @Description:
*/
public class IOCTestLifeCycle {
@Test
public void test01() {
// 创建ioc容器
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(MainConfigOfLifecycle.class);
System.out.println("容器创建完成...");
// 关闭容器
System.out.println("--->开始关闭容器");
applicationContext.close();
System.out.println("--->已经关闭容器");
}
}
可以看出先创建car,再调用init方法,在容器关闭时销毁实例
car constructor...
car...init...
容器创建完成...
--->开始关闭容器
car...destroy...
--->已经关闭容器
在配置数据源的时候,有很多属性赋值,销毁的时候要把连接给断开
可以通过Bean实现InitializingBean来定义初始化逻辑,是设置好所有属性会调用afterPropertiesSet()
方法
public interface InitializingBean {
/**
* Invoked by a BeanFactory after it has set all bean properties supplied
* (and satisfied BeanFactoryAware and ApplicationContextAware).
* This method allows the bean instance to perform initialization only
* possible when all bean properties have been set and to throw an
* exception in the event of misconfiguration.
* @throws Exception in the event of misconfiguration (such
* as failure to set an essential property) or if initialization fails.
*/
void afterPropertiesSet() throws Exception;
}
可以通过Bean实现DisposableBean来定义销毁逻辑,会调用destroy()方法
public interface DisposableBean {
/**
* Invoked by a BeanFactory on destruction of a singleton.
* @throws Exception in case of shutdown errors.
* Exceptions will get logged but not rethrown to allow
* other beans to release their resources too.
*/
void destroy() throws Exception;
}
编写一个Cat类
/**
*
* @Date: 2018/9/24 13:36
* @Description:
*/
public class Cat implements InitializingBean, DisposableBean{
public Cat() {
System.out.println("cat constructor...");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("cat...init...");
}
@Override
public void destroy() throws Exception {
System.out.println("cat...destroy...");
}
}
测试
cat constructor...
cat...init...
容器创建完成...
--->开始关闭容器
cat...destroy...
--->已经关闭容器
@PostContruct在Bean创建完成并且属性赋值完成,来执行初始化
@PreDestroy在容器销毁Bean之前通知我们进行清理工作
编写一个Dog类,并把他注入到配置类中
/**
*
* @Date: 2018/9/24 14:03
* @Description:
*/
public class Dog {
public Dog() {
System.out.println("dog constructor...");
}
@PostConstruct
public void postConstruct() {
System.out.println("post construct...");
}
@PreDestroy
public void preDestroy() {
System.out.println("pre destroy...");
}
}
测试结果
dog constructor...
post construct...
容器创建完成...
--->开始关闭容器
pre destroy...
--->已经关闭容器
在Bean初始化前后做一些处理
public interface BeanPostProcessor {
// 在初始化之前工作
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
// 在初始化之后工作
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
编写一个MyBeanPostProcessor实现BeanPostProcessor接口
/**
*
* @Date: 2018/9/24 14:21
* @Description: 后置处理器,初始化前后进行处理工作
*/
public class MyBeanPostProcessor implements BeanPostProcessor{
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("--->postProcessBeforeInitialization..." + beanName +"==>" + bean);
return bean;
}
@Override
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@1dc67c2
--->postProcessAfterInitialization...org.springframework.context.event.internalEventListenerProcessor==>org.springframework.context.event.EventListenerMethodProcessor@1dc67c2
--->postProcessBeforeInitialization...org.springframework.context.event.internalEventListenerFactory==>org.springframework.context.event.DefaultEventListenerFactory@2bd765
--->postProcessAfterInitialization...org.springframework.context.event.internalEventListenerFactory==>org.springframework.context.event.DefaultEventListenerFactory@2bd765
cat constructor...
--->postProcessBeforeInitialization...cat==>com.cuzz.bean.Cat@1d3b207
cat...init...
--->postProcessAfterInitialization...cat==>com.cuzz.bean.Cat@1d3b207
容器创建完成...
--->开始关闭容器
cat...destroy...
--->已经关闭容器
在实例创建之前后创建之后会被执行
通过debug到populateBean,先给属性赋值在执行initializeBean方法
try {
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
initializeBean方法时,
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// 执行before方法
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
...
try {
// 执行初始化
invokeInitMethods(beanName, wrappedBean, mbd);
}
if (mbd == null || !mbd.isSynthetic()) {
// 执行after方法
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
Spring底层对BeanPostProcessor
的使用:
Bean赋值、注入其他组件、@Autowired、生命周期注解功能、@Async等等都使用到了BeanPostProcessor这个接口的实现类,很重要
ApplicationContextAwareProcesser原理:
在创建对象(如dog)之后,检查是否实现了ApplicationContextAware,如果实现了,就注入applicationContext。这就是容器的强大,对你写的那些bean,如果都没实现ApplicationContextAware接口,容器也没必要给你干这个事。
package com.atguigu.bean;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class Dog implements ApplicationContextAware {
//@Autowired
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// TODO Auto-generated method stub
this.applicationContext = applicationContext;
}
}
/**
* bean的生命周期:
* bean创建---初始化----销毁的过程
* 容器管理bean的生命周期;
* 我们可以自定义初始化和销毁方法;容器在bean进行到当前生命周期的时候来调用我们自定义的初始化和销毁方法
*
* 构造(对象创建)
* 单实例:在容器启动的时候创建对象
* 多实例:在每次获取的时候创建对象\
*
* BeanPostProcessor.postProcessBeforeInitialization
* 初始化:
* 对象创建完成,并赋值好,调用初始化方法。。。
* BeanPostProcessor.postProcessAfterInitialization
* 销毁:
* 单实例:容器关闭的时候
* 多实例:容器不会管理这个bean;容器不会调用销毁方法;
*
*
* 遍历得到容器中所有的BeanPostProcessor;挨个执行beforeInitialization,
* 一但返回null,跳出for循环,不会执行后面的BeanPostProcessor.postProcessorsBeforeInitialization
*
* BeanPostProcessor原理
* populateBean(beanName, mbd, instanceWrapper);给bean进行属性赋值
* initializeBean
* {
* applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
* invokeInitMethods(beanName, wrappedBean, mbd);执行自定义初始化
* applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
*}
*
*
*
* 1)、指定初始化和销毁方法;
* 通过@Bean指定init-method和destroy-method;
* 2)、通过让Bean实现InitializingBean(定义初始化逻辑),
* DisposableBean(定义销毁逻辑);
* 3)、可以使用JSR250;
* @PostConstruct:在bean创建完成并且属性赋值完成;来执行初始化方法
* @PreDestroy:在容器销毁bean之前通知我们进行清理工作
* 4)、BeanPostProcessor【interface】:bean的后置处理器;
* 在bean初始化前后进行一些处理工作;
* postProcessBeforeInitialization:在初始化之前工作
* postProcessAfterInitialization:在初始化之后工作
*
* Spring底层对 BeanPostProcessor 的使用;
* bean赋值,注入其他组件,@Autowired,生命周期注解功能,@Async,xxx BeanPostProcessor;
*
* todo: 可以看看BeanPostProcessor的实现类
*
*/
看看执行顺序
dog constructor...
postProcessBeforeInitialization...dog=>com.atguigu.bean.Dog@5d740a0f
Dog...ApplicationContextAware.@PostConstruct...
postProcessAfterInitialization...dog=>com.atguigu.bean.Dog@5d740a0f
Dog...ApplicationContextAware.@PreDestroy...
cat constructor...
postProcessBeforeInitialization...cat=>com.atguigu.bean.Cat@7d68ef40
cat...InitializingBean.afterPropertiesSet...
postProcessAfterInitialization...cat=>com.atguigu.bean.Cat@7d68ef40
cat...DisposableBean.destroy...
使用@Value赋值
使用xml时候导入配置文件是
<context:property-placeholder location="classpath:person.properties"/>
使用注解可以在配置类添加一个@PropertySource注解把配置文件中k/v保存到运行的环境中
使用${key}来获取
/**
*
* @Date: 2018/9/24 18:43
* @Description:
*/
@PropertySource(value = {"classpath:/person.properties"})
@Configuration
public class MainConfigOfPropertyValue {
@Bean
public Person person() {
return new Person();
}
}
@Data
public class Person {
@Value("vhuj")
private String name;
@Value("#{20-2}")
private Integer age;
@Value("${person.nickName}")
private String nickName;
}
测试
@Test
public void test01() {
printBean(applicationContext);
System.out.println("---------------------------");
Person person = (Person) applicationContext.getBean("person");
System.out.println(person);
System.out.println("---------------------------");
}
输出
---------------------------
Person(name=vhuj, age=18, nickName=三三)
---------------------------
此外,也可以代码获得值
ConfigurableEnvironment environment = applicationContext.getEnvironment();
String property = environment.getProperty("person.nickName");
System.out.println(property);
applicationContext.close();
自动转配:
Spring利用依赖注入(DI),完成对IOC容器中各个组件的依赖关系赋值
@Autowired自动注入:
a. 默认优先按照类型去容器中寻找对应的组件,如果找到去赋值
b. 如果找到到相同类型的组件,再将属性名(`BookDao bookdao`)作为组件的id去容器中查找
c. 接下来还可以使用`@Qualifier("bookdao")`明确指定需要装配的id
d. 默认是必须的,我们可以指定 `@Autowired(required=false)`,指定非必须
@Primary让Spring自动装配时首先装配
/**
* 自动装配;
* Spring利用依赖注入(DI),完成对IOC容器中中各个组件的依赖关系赋值;
*
* 1)、@Autowired:自动注入:
* 1)、默认优先按照类型去容器中找对应的组件:applicationContext.getBean(BookDao.class);找到就赋值
* 2)、如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找
* applicationContext.getBean("bookDao")
* 3)、@Qualifier("bookDao"):使用@Qualifier指定需要装配的组件的id,而不是使用属性名
* 4)、自动装配默认一定要将属性赋值好,没有就会报错;
* 可以使用@Autowired(required=false);
* 5)、@Primary:让Spring进行自动装配的时候,默认使用首选的bean;
* 也可以继续使用@Qualifier指定需要装配的bean的名字
* BookService{
* @Autowired
* BookDao bookDao;
* }
*
* 2)、Spring还支持使用@Resource(JSR250)和@Inject(JSR330)[java规范的注解]
* @Resource:
* 可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的;
* 没有能支持@Primary功能没有支持@Autowired(reqiured=false);
* @Inject:
* 需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能;
* @Autowired:Spring定义的; @Resource、@Inject都是java规范
*
* AutowiredAnnotationBeanPostProcessor:解析完成自动装配功能;
*
* 3)、 @Autowired:构造器,参数,方法,属性;都是从容器中获取参数组件的值
* 1)、[标注在方法位置]:@Bean+方法参数;参数从容器中获取;默认不写@Autowired效果是一样的;都能自动装配
* 2)、[标在构造器上]:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取
* 3)、放在参数位置:
*
* 4)、自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx);
* 自定义组件实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware;
* 把Spring底层一些组件注入到自定义的Bean中;
* xxxAware:功能使用xxxProcessor;
* ApplicationContextAware==》ApplicationContextAwareProcessor;
*
*
* @author lfy
*
*/
Spring还支持使用@Resource (JSR250) 和@Inject (JSR330) 注解,这两个是java规范
@Resource和@Autowired一样实现自动装配功能,默认是按组件名称进行装配的
没有支持@Primary和@Autowird(required=false)的功能
@Autowired:构造器、参数、方法属性等
标注到方法位子上@Bean+方法参数,参数从容器中获取
/**
*
* @Date: 2018/9/24 20:57
* @Description:
*/
public class Boss {
// 属性
@Autowired
private Car car;
// 构造器 如果构造器只有一个有参构造器可以省略
@Autowired
public Boss(@Autowired ar car) {
}
public Car getCar() {
return car;
}
// set方法
@Autowired // 参数
public void setCar(@Autowired Car car) {
this.car = car;
}
}
自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory 等等),自定义组件实现xxxAware,在创建对象的时候会调用接口规定的方法注入相关的组件
/**
* Marker superinterface indicating that a bean is eligible to be
* notified by the Spring container of a particular framework object
* through a callback-style method. Actual method signature is
* determined by individual subinterfaces, but should typically
* consist of just one void-returning method that accepts a single
* argument.
*/
public interface Aware {
}
我们实现几个常见的Aware接口
package com.atguigu.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.stereotype.Component;
import org.springframework.util.StringValueResolver;
@Component
public class Red implements ApplicationContextAware,BeanNameAware,EmbeddedValueResolverAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// TODO Auto-generated method stub
System.out.println("传入的ioc:"+applicationContext);
this.applicationContext = applicationContext;
}
@Override
public void setBeanName(String name) {
// TODO Auto-generated method stub
System.out.println("当前bean的名字:"+name);
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
// TODO Auto-generated method stub
String resolveStringValue = resolver.resolveStringValue("你好 ${os.name} 我是 #{20*18}");
System.out.println("解析的字符串:"+resolveStringValue);
}
}
注入到配置中测试
/**
*
* @Date: 2018/9/25 10:28
* @Description:
*/
public class IOCTestAware {
@Test
public void test01() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAware.class);
}
}
测试结果
当前Bean的名字: red
当前的BeanFactory: org.springframework.beans.factory.support.DefaultListableBeanFactory@159c4b8: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,mainConfigOfAware,red]; root of factory hierarchy
传入的ioc: org.springframework.context.annotation.AnnotationConfigApplicationContext@1e89d68: startup date [Tue Sep 25 10:29:17 CST 2018]; root of context hierarchy
把Spring自定义组件注入到容器中
原理:
public interface ApplicationContextAware extends Aware {}
xxxAware
都是通过xxxProcessor
来处理的
比如:ApplicationContextAware
对应ApplicationContextAwareProcessor
Profile是Spring为我们提供可以根据当前环境,动态的激活和切换一系组件的功能
a. 使用命令动态参数激活:虚拟机参数位子加载 `-Dspring.profiles.active=test
b. 使用代码激活环境
public class IOCTest_Profile {
//1、使用命令行动态参数: 在虚拟机参数位置加载 -Dspring.profiles.active=test
//2、代码的方式激活某种环境;
@Test
public void test01(){
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext();
//1、创建一个applicationContext
//2、设置需要激活的环境
applicationContext.getEnvironment().setActiveProfiles("dev");
//3、注册主配置类
applicationContext.register(MainConfigOfProfile.class);
//4、启动刷新容器
applicationContext.refresh();
String[] namesForType = applicationContext.getBeanNamesForType(DataSource.class);
for (String string : namesForType) {
System.out.println(string);
}
Yellow bean = applicationContext.getBean(Yellow.class);
System.out.println(bean);
applicationContext.close();
}
}
/**
* Profile:
* Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能;
*
* 开发环境、测试环境、生产环境;
* 数据源:(/A)(/B)(/C);
*
*
* @Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件
*
* 1)、加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境
* 2)、写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
* 3)、没有标注环境标识的bean在,任何环境下都是加载的;
*/
@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MainConfigOfProfile implements EmbeddedValueResolverAware{
@Value("${db.user}")
private String user;
private StringValueResolver valueResolver;
private String driverClass;
@Bean
public Yellow yellow(){
return new Yellow();
}
@Profile("test")
@Bean("testDataSource")
public DataSource dataSourceTest(@Value("${db.password}")String pwd) throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Profile("dev")
@Bean("devDataSource")
public DataSource dataSourceDev(@Value("${db.password}")String pwd) throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/ssm_crud");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Profile("prod")
@Bean("prodDataSource")
public DataSource dataSourceProd(@Value("${db.password}")String pwd) throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/scw_0515");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
// TODO Auto-generated method stub
this.valueResolver = resolver;
driverClass = valueResolver.resolveStringValue("${db.driverClass}");
}
}