一.IOC功能
1.传统方式
(1) 在classpath下创建beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!-- 包扫描,只要类标注了@Controller,@Service,@Repository,@Component -->
<!-- 扫描 com.miracle 下的组件-->
<context:component-scan base-package="com.miracle"></context:component-scan>
<bean id="person" class="com.miracle.springAnnotation.bean.Person">
<property name="age" value="18"></property>
<property name="name" value="zhangsan"></property>
</bean>
</beans>
(2)编写bean
package com.miracle.springAnnotation.bean;
public class Person {
private String name;
private Integer age;
public Person() {
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
(3)使用bean
package com.miracle.springAnnotation;
import com.miracle.springAnnotation.bean.Person;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Person person = (Person)context.getBean("person");
System.out.println(person);
}
}
2.注解方式
(1)创建配置类(等同于原来的配置文件)
package com.miracle.springAnnotation.config;
import com.miracle.springAnnotation.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
// 配置类 等同于 配置文件
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.miracle") // 开启包扫描,扫描定义在 com.miracle 下的包
public class MainConfig {
// 默认的是单例的,通过@Scope注解的值来修改
// @Scope("prototype") 多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。每次获取的时候才会调用方法创建对象
// @Scope("singleton") 单实例的:ioc容器启动会调用方法创建对象放到ioc容器中
// 给容器中注册一个Bean,类型为返回值的类型,id默认是用方法名作为id,@Bean("id") 来指定bean的id
// @Lazy 懒加载bean
@Scope("prototype")
@Bean
public Person person() {
return new Person("lisi", 20);
}
}
(2)使用bean
package com.miracle.springAnnotation;
import com.miracle.springAnnotation.bean.Person;
import com.miracle.springAnnotation.config.MainConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest {
public static void main(String[] args) {
// 创建注解类型的 ApplicationContext 传入注解配置文件的class对象
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
Person person = context.getBean(Person.class);
System.out.println(person);
// 通过类型,获取容器中这个类型的所有bean的名字
String[] namesForType = context.getBeanNamesForType(Person.class);
for (String name : namesForType) {
System.out.println(name);
}
}
}
3.组件注册相关注解说明
(1)懒加载
针对单实例bean,默认在容器启动的时候创建对象
设置懒加载后,容器启动不创建对象,第一次获取bean时创建对象,并初始化
package com.miracle.springAnnotation.config;
import com.miracle.springAnnotation.bean.Person;
import org.springframework.context.annotation.*;
@Configuration
@ComponentScan(value = "com.miracle")
public class MainConfig2 {
@Lazy // 懒加载bean
@Bean
public Person person() {
return new Person("lisi", 20);
}
}
(2)按照条件注册bean
@Conditional:按照一定的条件进行判断,满足条件给容器中注册bean
假设有个需求是按照操作系统的不同(Linux/windows),给容器中注册不同的bean
通过查看Conditional注解的定义,可以知道这个注解可以加在类和方法上
所以我们在配置类中的方法里创建bean,所以将其加在方法上
查看注解接收参数 Class<? extends Condition>[] value();
可知注解接收 condition 数组
查看condition源码,发现是一个接口,包含一个matches方法,返回boolean值
在condition包下创建 condition实现类
WindowsCondition.java
package com.miracle.springAnnotation.condition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* 判断操作系统是否windows系统
*/
public class WindowsCondition implements Condition {
/**
* @param context :判断条件能使用的上下文(环境)
* @param metadata:注释信息
* @return
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 判断是否是windows系统
String osName = context.getEnvironment().getProperty("os.name");
if(osName.contains("Windows")) {
return true;
}
return false;
}
}
LinuxCondition.java
package com.miracle.springAnnotation.condition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* 判断操作系统是否linux系统
*/
public class LinuxCondition implements Condition {
/**
* @param context :判断条件能使用的上下文(环境)
* @param metadata:注释信息
* @return
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 判断是否是linux系统
String osName = context.getEnvironment().getProperty("os.name");
if(osName.contains("linux")) {
return true;
}
return false;
}
}
MainConfig2.java
在配置类中创建bean的方法上加 @Conditional 注解
package com.miracle.springAnnotation.config;
import com.miracle.springAnnotation.bean.Person;
import com.miracle.springAnnotation.condition.LinuxCondition;
import com.miracle.springAnnotation.condition.WindowsCondition;
import org.springframework.context.annotation.*;
@Configuration
@ComponentScan(value = "com.miracle")
public class MainConfig2 {
/**
* 需求:根据操作系统的不同,注册不同的bean,windows注册bill,linux注册linus
*
*/
@Conditional({WindowsCondition.class})
@Bean
public Person bill() {
return new Person("Bill Gates", 62);
}
@Conditional({LinuxCondition.class})
@Bean
public Person linus() {
return new Person("linus", 48);
}
}
如果@Conditional注解加到类上时,对类中组件统一设置,只有满足当前条件,这个类中配置的所有bean注册才能生效
(3)用@Import 注解向容器中导入bean
第一种方式:
package com.miracle.springAnnotation.config;
import com.miracle.springAnnotation.bean.Color;
import com.miracle.springAnnotation.bean.Red;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@ComponentScan(value = "com.miracle")
@Import({Color.class, Red.class}) //Import 向容器中导入组件,id默认是组件的全类名
public class MainConfig3 {
}
第二种方式:
编写selector
package com.miracle.springAnnotation.config;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
// 需要实现 ImportSelector 接口
public class MyImportSelector implements ImportSelector {
// 自定义逻辑返回需要导入的组件
// 返回值,就是要导入到容器中的组件全类名的数组
// AnnotationMetadata : 当前标注 @Import注解的类的所有注解信息
@Override
public String[] selectImports(AnnotationMetadata AnnotationMetadata) {
return new String[]{"com.miracle.springAnnotation.bean.Red", "com.miracle.springAnnotation.bean.Yellow", "com.miracle.springAnnotation.bean.Blue"};
}
}
在配置类中导入selector
package com.miracle.springAnnotation.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@ComponentScan(value = "com.miracle")
@Import({MyImportSelector.class}) //导入selector
public class MainConfig3 {
}
第三种方式:
编写 definitionRegistry
package com.miracle.springAnnotation.config;
import com.miracle.springAnnotation.bean.Color;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
// 要实现 ImportBeanDefinitionRegistrar 接口
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* @param importingClassMetadata : 当前类的注解信息
* @param registry : bean定义的注册类
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 传入要定义bean的class对象
RootBeanDefinition definition = new RootBeanDefinition(Color.class);
// 传入bean名字,definition对象
registry.registerBeanDefinition("color", definition);
}
}
编写配置类
package com.miracle.springAnnotation.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@ComponentScan(value = "com.miracle")
@Import({MyImportBeanDefinitionRegistrar.class}) //导入beanDefinitionRegistrar
public class MainConfig4 {
}
第四种方式:
编写工厂 ColorFactoryBean.java
package com.miracle.springAnnotation.config;
import com.miracle.springAnnotation.bean.Color;
import org.springframework.beans.factory.FactoryBean;
// 创建一个spring定义的FactoryBean 这里指定要创建什么类型的对象
public class ColorFactoryBean implements FactoryBean<Color> {
// 工厂调用getObject方法,创建对象Color对象,这个对象会添加到容器中
@Override
public Color getObject() throws Exception {
return new Color();
}
@Override
public Class<?> getObjectType() {
return Color.class;
}
// 控制是否单例
@Override
public boolean isSingleton() {
return false;
}
}
编写配置类
package com.miracle.springAnnotation.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(value = "com.miracle")
public class MainConfig5 {
@Bean // 这里创建了一个id为colorFactoryBean的bean,但是这个bean的类型是ColorFactoryBean实现的接口里面的泛型
public ColorFactoryBean colorFactoryBean() {
return new ColorFactoryBean();
}
}
使用bean
// 这里直接拿工厂的名字,拿出来的就接口泛型里面的类型
Object obj = applicationContext.getBean("colorFactoryBean");
System.out.println("bean的类型:" + obj.getClass());
(4)指定bean的初始化和销毁方法
方式一
创建配置类 MainConfigOfLifeCycle.java
package com.miracle.springAnnotation.config;
import com.miracle.springAnnotation.bean.Car;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* bean的生命周期:bean创建---初始化---销毁的过程
*
* 容器管理bean的生命周期:
* 我们可以自定义初始化和销毁方法:容器在bean进行到当前生命周期的时候来调用我们自定义的初始化和销毁方法
* 指定初始化和销毁方法: 指定 init-method 和 destroy-method 方法
*/
@Configuration
public class MainConfigOfLifeCycle {
// 通过@Bean注解里面写 initMethod,destroyMethod 的值来指定初始化方法和销毁方法
@Bean(initMethod = "init", destroyMethod = "detory")
public Car car() {
return new Car();
}
}
定义bean
package com.miracle.springAnnotation.bean;
public class Car {
public Car() {
System.out.println("car constructor...");
}
// 定义bean的初始化方法
public void init() {
System.out.println("car ... init...");
}
// 定义bean的销毁方法
public void detory() {
System.out.println("car ... detory...");
}
}
方式二:通过让Bean实现接口InitializingBean(定义初始化逻辑),DisposableBean(定义销毁逻辑)
创建配置类 MainConfig6.java
package com.miracle.springAnnotation.config;
import com.miracle.springAnnotation.bean.Cat;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MainConfig6 {
@Bean
public Cat cat() {
return new Cat();
}
}
定义bean
package com.miracle.springAnnotation.bean;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class Cat implements InitializingBean, DisposableBean {
public Cat() {
System.out.println("cat constructor");
}
// 实现销毁方法
@Override
public void destroy() throws Exception {
System.out.println("cat...destroy");
}
// 实现初始化方法
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("cat...init");
}
}
方式三:可以使用JSR250规范提供的两个注解
@PostConstruct 在bean创建完成并且属性赋值完成,来执行初始化方法
@PreDestroy 在容器销毁bean之前通知我们进行清理工作
创建配置类 MainConfig7.java
package com.miracle.springAnnotation.config;
import com.miracle.springAnnotation.bean.Dog;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MainConfig7 {
@Bean
public Dog dog() {
return new Dog();
}
}
创建bean
package com.miracle.springAnnotation.bean;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class Dog {
public Dog() {
System.out.println("dog constructor");
}
// 只需要在初始化方法上添加注解@PostConstruct,那么spring就会在对象创建并赋值之后调用
@PostConstruct
public void init() {
System.out.println("Dog....@PostConstruct...");
}
// 只需要在初始化方法上添加注解@PreDestroy,那么spring就会在容器移除对象之前调用
@PreDestroy
public void detory() {
System.out.println("Dog....@PreDestroy...");
}
}
方式四:
使用spring提供的BeanPostProcessor(bean的后置处理器)
BeanPostProcessor接口说明,bean的后置处理器定义后会在spring创建每个bean对象的过程中都执行处理器中的方法
包含两个方法
// 在所有初始化方法调用之前执行
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
// 在所有初始化方法调用之后执行
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
编写配置文件
MainConfig8.java
package com.miracle.springAnnotation.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(value = "com.miracle") // 扫描到自定义的BeanPostProcessor
public class MainConfig8 {
}
编写自定义BeanPostProcessor
package com.miracle.springAnnotation.config;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
/**
* 定义自己的 后置处理器 : 初始化前后进行处理工作
*/
@Component // 要使我们自定义的处理器工作,需要把这个类注册到springIOC容器中
public class MyBeanPostProcessor implements BeanPostProcessor {
// 参数 bean 是容器帮我创建的bean
// 参数 beanName 是bean的名字
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization..." + beanName + "=>" + bean);
// 返回值 是 返回将要用的bean实例,可以原封不动的返回bean,也可以返回包装过的bean,但是这里不能返回null,查看源代码发现,如果返回null,那么后面的BeanPostProcessor都不会执行了
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization..." + beanName + "=>" + bean);
return bean;
}
}