一、Spring体系结构
- Spring Core:Spring的核心,它是Spring框架最基础的部分,提供IOC和依赖注入特性。
- Spring Context:Spring上下文容器,它是BeanFactory功能加强的一个子接口。
- Spring Web:提供Web应用开发的支持。
- Spring MVC:针对Web应用中MVC思想的实现。
- Spring DAO:提供对JDBC抽象层,简化了JDBC编码,同时,编码更具有健壮性。
- Spring ORM:它支持用于流行的ORM框架的整合,比如:Spring + HIbernate,Spring + Mybatis。
- Spring AOP:面向切面编程,提供了与AOP联盟兼容的编程实现。
本文主讲一些IOC容器基本组件的使用
IOC容器就是对bean进行管理:bean的注册、实例化和管理
二、Spring有哪些注入bean的方法
1、Spring通过bean.xml配置将bean实例化到IOC容器中:
<?xml version="1.0" encoding="UTF-8" ?>
<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-4.1.xsd">
<bean id="person" class="com.enjoy.cap1.Person">
<property name="name" value="zhangSan"></property>
<property name="age" value="19"></property>
</bean>
</beans>
package com.enjoy.cap1;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainTest1 {
public static void main(String[] args) {
//把beans.xml的类加载到容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
//从容器中获取bean
Person person = (Person) applicationContext.getBean("person");
System.out.println(person);
}
}
2、通过注解实例化bean到IOC容器
- @Configuration
告诉Spring这是一个配置类 - @Bean
创建一个配置类(@Configuration),通过@Bean注解实例化
/**
* 配置类 ---- 配置文件
*/
@Configuration
public class MainConfig {
/**
* 给容器中注册一个bean,类型为返回值的类型。bean的id为方法名,或者直接在注解中规定好bean的id
*/
@Bean("aaaPerson")
public Person person(){
return new Person("liSi",20);
}
}
- @ComponentScan
扫描规则:
① 指定扫描范围
@Configuration
@ComponentScan(value = "com.enjoy.cap2")
public class Cap2MainConfig {
/**
* 给容器中注册一个bean,类型为返回值的类型。bean的id为方法名,或者直接在注解中规定好bean的id
*/
@Bean("aaaPerson")
public Person person(){
return new Person("liSi",20);
}
}
② 扫描过滤器
Filter 分为一下几种:
使用includeFilters 方法,useDefaultFilters必须设置为false,如果为true,则将按照注解类型为Component进行引入bean。而Controller、Service和Repository都有@Component注解。
@Configuration
//根据注解来判断注入哪些bean
@ComponentScan(value = "com.enjoy.cap2",includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
}, useDefaultFilters = false)
public class Cap2MainConfig {
/**
* 给容器中注册一个bean,类型为返回值的类型。bean的id为方法名,或者直接在注解中规定好bean的id
*/
@Bean("aaaPerson")
public Person person(){
return new Person("liSi",20);
}
}
@Configuration
//根据注解来判断过滤掉哪些bean
@ComponentScan(value = "com.enjoy.cap2",excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
}, useDefaultFilters = true)
public class Cap2MainConfig {
/**
* 给容器中注册一个bean,类型为返回值的类型。bean的id为方法名,或者直接在注解中规定好bean的id
*/
@Bean("aaaPerson")
public Person person(){
return new Person("lisi",20);
}
}
③ 自定义过滤规则
自定义过滤规则的类:
public class CustomTypeFilter implements TypeFilter {
private ClassMetadata classMetadata;
/**
* @param metadataReader:读取到当前正在扫描类的信息
* @param metadataReaderFactory:可以获取到其他任何类信息
*/
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//获取当前类注解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取当前正在扫描的类信息
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;
}
}
引用的类:
//自定义拦截来注入bean
@Configuration
@ComponentScan(value = "com.enjoy.cap2", includeFilters = {
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = {CustomTypeFilter.class})
}, useDefaultFilters = false)
public class Cap2MainConfig {
/**
* 给容器中注册一个bean,类型为返回值的类型。bean的id为方法名,或者直接在注解中规定好bean的id
*/
@Bean("aaaPerson")
public Person person(){
return new Person("lisi",20);
}
}
- @Scope
IOC容器多实例和单实例:(给容器中注册一个bean,默认为单实例)
① 多实例:仅当bean被使用获取的时候才创建
② 单实例:创建IOC容器的时候,实例bean就被创建
@Scope的使用:(多实例)
@Configuration
public class Cap3MainConfig {
//给容器中注册一个bean,类型为返回值类型,默认是单实例
/*
* prototype:多实例,IOC容器启动的时候,IOC容器启动并不会调用方法创建对象,而是每次获取的时候才会调用方法创建对象
* singleton:单实例(默认),IOC容器启动的时候会调用方法创建对象并放到IOC容器中,以后每次获取的就是直接从容器中拿同一个bean
* request:主要针对web应用,递交一次请求创建一个实例
* session:同一个session创建一个实例
*/
@Scope("prototype")
@Bean
public Person person(){
return new Person("ethan",18);
}
}
测试结果:
- @lazy 懒加载
① 什么是懒加载?
懒加载主要是针对单实例bean,容器在启动的时候不创建对象,仅当第一次调用获取的时候才初始化bean。
② 懒加载的使用?
@Configuration
public class Cap4MainConfig {
/**
* 懒加载:主要针对单实例bean,单实例bean默认在容器启动的时候创建,
* 使用@Lazy注解,容器启动时不创建对象,仅当第一次使用(获取)bean的时候才被初始化创建
*/
@Lazy
@Bean
public Person person(){
System.out.println("给容器中添加person......");
return new Person("张三",20);
}
}
- @Conditional 条件注册bean
① 什么是条件注册bean?
规定一些条件,只有满足条件,才会注册某个bean实例。
②如何根据指定条件选择性的注册bean实例?
@Configuration
public class Cap5MainConfig {
@Bean
public Person person(){
System.out.println("给容器中注入person实例bean。。。。。。");
return new Person("person",18);
}
@Conditional(WinCondition.class)
@Bean
public Person zhangSan(){
System.out.println("给容器中注入zhangSan实例bean。。。。。。");
return new Person("person",18);
}
@Conditional(LinCondition.class)
@Bean
public Person liSi(){
System.out.println("给容器中注入liSi实例bean。。。。。。");
return new Person("person",18);
}
}
public class WinCondition implements Condition {
/**
* @param conditionContext:判断条件可以使用的上下文(环境)
* @param annotatedTypeMetadata:注解的信息
* @return
*/
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//获取IOC容器中正在使用的beanFactory
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
//获取当前环境变量(包括我们操作系统是WIN还是LINUX)
Environment environment = conditionContext.getEnvironment();
String os_name = environment.getProperty("os.name");
if (os_name.contains("Windows")) {
return true;
}
return false;
}
}
public class LinCondition implements Condition {
/**
* @param conditionContext:判断条件可以使用的上下文(环境)
* @param annotatedTypeMetadata:注解的信息
* @return
*/
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//获取IOC容器中正在使用的beanFactory
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
//获取当前环境变量(包括我们操作系统是WIN还是LINUX)
Environment environment = conditionContext.getEnvironment();
String os_name = environment.getProperty("os.name");
if (os_name.contains("linux")) {
return true;
}
return false;
}
}
测试结果:
- @Import 注册bean
① 手动添加组件到IOC容器
@Configuration
@Import(value = {Dog.class, Cat.class})
public class Cap6MainConfig {
/**
* @Import(要导入到容器中的组件):容器会自动注册这个组件,bean的id为全类名
*/
@Bean("person")
public Person person(){
return new Person("zhangsan",20);
}
}
② 使用ImportSelector自定义返回组件
CustomImportSelector自定义引入选择类,实现ImportSelector接口:
public class CustomImportSelector implements ImportSelector {
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"com.enjoy.cap6.bean.Fish","com.enjoy.cap6.bean.Tiger"};
}
}
使用案例:
@Configuration
@Import(value = {CustomImportSelector.class})
public class Cap6MainConfig {
/**
* @Import(要导入到容器中的组件):容器会自动注册这个组件,bean的id为全类名
*/
@Bean("person")
public Person person(){
return new Person("zhangsan",20);
}
}
③ 使用ImportBeanDefinitionRegistrar返回自定义组件
自定义CustomImportBeanDefinitionRegistrar类,实现ImportBeanDefinitionRegistrar 接口:
public class CustomImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* @param annotationMetadata:当前类的注解类
* @param beanDefinitionRegistry:BeanDefinition注册类
* 把所有需要添加到容器中的bean加入;
*/
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
boolean bean1 = beanDefinitionRegistry.containsBeanDefinition("com.enjoy.cap6.bean.Dog");
boolean bean2 = beanDefinitionRegistry.containsBeanDefinition("com.enjoy.cap6.bean.Cat");
//如果Dog和Cat同时存在于IOC容器中,那么就创建Pig类,注入到容器
if (bean1 && bean2) {
//通过RootBeanDefinition类将要注册的bean进行封装
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Pig.class);
beanDefinitionRegistry.registerBeanDefinition("pig",rootBeanDefinition);
}
}
}
使用案例:
@Configuration
@Import(value = {CustomImportBeanDefinitionRegistrar.class})
public class Cap6MainConfig {
/**
* @Import(要导入到容器中的组件):容器会自动注册这个组件,bean的id为全类名
*/
@Bean("person")
public Person person(){
return new Person("zhangsan",20);
}
}
④ FactoryBean接口实现
自定义CustomFactoryBean,实现FactoryBean接口,装配Monkey的类对象
public class CustomFactoryBean implements FactoryBean<Monkey> {
public Monkey getObject() throws Exception {
return new Monkey();
}
public Class<?> getObjectType() {
return Monkey.class;
}
public boolean isSingleton() {
return false;
}
}
可在配置类中使用@Bean注入进来
@Bean
public CustomFactoryBean customFactoryBean(){
return new CustomFactoryBean();
}
注意点:app.getBean("&customFactoryBean") 获取到的bean是customFactoryBean对象,app.getBean(“customFactoryBean”) 获取到的bean是Monkey对象。
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap6MainConfig.class);
System.out.println("IOC容器创建完成......");
Object bean1 = app.getBean("&customFactoryBean");//获取的bean是customFactoryBean
Object bean2 = app.getBean("customFactoryBean");//获取的bean是Monkey
System.out.println("bean的类型 = " + bean1.getClass());//打印结果是:bean的类型 = class com.enjoy.cap6.config.CustomFactoryBean
System.out.println("bean的类型 = " + bean2.getClass());//打印结果是:bean的类型 = class com.enjoy.cap6.bean.Monkey
容器注册组件方式总结:
给容器中注册组件的方式:
① @Bean:【导入第三方的类或包的组件】,比如Person为第三方的类,需要在我们的IOC容器中使用
② 包扫描+组件的标注注解:(@ComponentScan:@Controller,@Service,@Reponsitory,@Component),一般是针对我们自己写的类,使用这个
③ @Import:[快速给容器导入一个组件] 注意:@Bean有点简单
a、@Import(要导入到容器中的组件):容器会自动注册这个组件,bean的id为全类名
b、ImportSelector:是一个接口,返回需要导入到容器的组件的全类名数组
c、ImportBeanDefinitionRegisitrar:可以手动添加组件到IOC容器,所有Bean的注册可以使用BeanDifitionRegistry,写CustomImportBeanDefinitionRegistrar实现ImportBeanDefinitionRegistrar接口即可
④ 使用Spring提供的FactoryBean(工厂bean)进行注册。
三、Bean的生命周期
对于单实例的bean,可以正常调用初始化和销毁方法,对于多实例的bean,容器只负责初始化,但不会管理bean,容器关闭的时候不会调用销毁方法。
- Bean的生命周期指:Bean创建 --》初始化 --》销毁 的过程。
① 、我们可以自定义Bean初始化和销毁方法。容器在Bean进行到当前生命周期的时候,来调用自定义的初始化方法和销毁方法。
//定义一个类对象
public class Bike {
public Bike (){
System.out.println("Bike constructor ........ ");
}
public void init(){
System.out.println("Bike ...... init ......");
}
public void destory(){
System.out.println("Bike ...... destory ......");
}
}
//配置类
@Configuration
public class Cap7MainConfigOfLifeCycle {
@Bean(initMethod = "init",destroyMethod = "destory")
public Bike bike(){
return new Bike();
}
}
//测试
@Test
public void test01(){
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap7MainConfigOfLifeCycle.class);
System.out.println("IOC 容器创建完成。。。。。");
app.close();
}
测试结果是:
②、实现InitializingBean接口的afterPropertiesSet()方法,当beanFactory创建好对象,切把bean所有属性设置好之后,会调用这个方法,相当于初始化方法。
实现DisposableBean的destory()方法,当bean销毁时,会把单实例bean进行销毁。
@Component
public class Train implements InitializingBean, DisposableBean {
public Train() {
System.out.println("Train......constructor............");
}
//当bean销毁时,调用此方法
public void destroy() throws Exception {
System.out.println("Train......destroy............");
}
//当bean属性赋值和初始化完成时调用
public void afterPropertiesSet() throws Exception {
System.out.println("Train......afterPropertiesSet............");
}
}
//配置类
@ComponentScan("com.enjoy.cap7.bean") //扫描注入Train
@Configuration
public class Cap7MainConfigOfLifeCycle {
@Scope("prototype")//多实例的bean,容器只负责初始化,但不会管理bean,容器关闭的时候不会调用销毁方法。
@Bean(initMethod = "init",destroyMethod = "destory")
public Bike bike(){
return new Bike();
}
}
//测试
@Test
public void test01(){
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap7MainConfigOfLifeCycle.class);
System.out.println("IOC 容器创建完成。。。。。");
app.close();
}
测试结果:
③ 使用JSR250规范定义(java规范)的两个注解实现 Bean生命周期–初始化与销毁
@PostConstruct:在Bean创建完成,且属于赋值完成后进行初始化,属于JDK规范的注解
@PreDestory:在Bean将被移除之前进行通知,在容器销毁之前进行清理工作
方法上使用@PostConstruct和@PreDestory注解来自定义实现bean初始化与销毁
@Component
public class Jeep {
public Jeep() {
System.out.println("Jeep ...... constructor ......");
}
@PostConstruct
public void init(){
System.out.println("Jeep ...... PostConstruct ......");
}
@PreDestroy
public void destory(){
System.out.println("Jeep ...... PreDestroy ......");
}
}
//配置类
@Configuration
public class Cap7MainConfigOfLifeCycle {
@Bean
public Jeep jeep(){
return new Jeep();
}
}
//测试
@Test
public void test01(){
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap7MainConfigOfLifeCycle.class);
System.out.println("IOC 容器创建完成。。。。。");
app.close();
}
测试结果:
④使用 BeanPostProcessorsr 控制Bean的生命周期;
实现 BeanPostprocessors 的两个接口即可:
Ⅰ、postProcessBeforeInitialization()
Ⅱ、postprocessAfterInitialization()
//Bike类
public class Bike {
public Bike (){
System.out.println("Bike constructor ........ ");
}
public void init(){
System.out.println("Bike ...... init ......");
}
public void destory(){
System.out.println("Bike ...... destory ......");
}
}
//实现BeanPostProcessor接口
@Component
public class CustomerBeanPostProcessor implements BeanPostProcessor {
//返回一个对象(传过来的对象)
//在初始化方法调用之前进行后置处理工作
//什么时候调用它:init-method=init之前调用
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization......"+beanName+"......"+bean);
return bean;
}
//返回一个对象(传过来的对象)
//在初始化方法调用之前进行后置处理工作
//什么时候调用它:init-method=init之后调用
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization......"+beanName+"......"+bean);
return bean;
}
}
//配置类
@ComponentScan("com.enjoy.cap7.bean")
@Configuration
public class Cap7MainConfigOfLifeCycle {
//@Scope("prototype")
@Bean(initMethod = "init",destroyMethod = "destory")
public Bike bike(){
return new Bike();
}
}
//测试
@Test
public void test01(){
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap7MainConfigOfLifeCycle.class);
System.out.println("IOC 容器创建完成。。。。。");
app.close();
}
测试结果: