一:ApplicationContextAware接口
实现ApplicationContextAware接口,重写setApplicationContext方法,可以将spring容器上下文对象注入,
然后持有spring上下文对象,可以通过该对象获取spring容器中注册的任何bean实例。
/** * @author Administrator * 实现ApplicationContextAware接口,重写setApplicationContext方法,会注入 * spring上下文对象,可以在程序中持有一个context的对象,通过context,我们可以获取任何 * spring容器管理的bean */ public class ApplicationContextUtils implements ApplicationContextAware{ private static ApplicationContext context; @Override public void setApplicationContext(ApplicationContext context) throws BeansException { ApplicationContextUtils.context = context; } public static ApplicationContext getApplicationContext(){ return context; } /** * 通过beanName获取实例bean * @param beanName * @return */ @SuppressWarnings("unchecked") public static <T> T getBean(String beanName){ return (T) context.getBean(beanName); } /** * 通过类型获取实例bean,但是如果在spring容器中通过相同的类 * 的不同构造器创建两个不同的对象,通过类型则获取不到对象 * @param clazz * @return */ @SuppressWarnings("unchecked") public static <T> T getBean(Class<?> clazz){ return (T) context.getBean(clazz); } }
测试两种获取实例bean的方式:
@Test public void testAppContext() { String conf = "applicationContext.xml"; ApplicationContext context = new ClassPathXmlApplicationContext(conf); Emp emp = ApplicationContextUtils.getBean("emp"); System.out.println(emp); Emp emp2 = ApplicationContextUtils.getBean(Emp.class); System.out.println(emp2); }
Emp [name=null, age=null, salary=null] Emp [name=null, age=null, salary=null]
可以获取对象,但是如果xml中注册两个相同类型的bean,就会出问题:
<!-- 实例化对象,默认调用无参数构造器 --> <bean id="emp" class="com.hlcui.entity.Emp" init-method="init"></bean> <!-- 实例化对象,有参数构造器 --> <bean id="emp2" class="com.hlcui.entity.Emp"> <property name="name" value="Tom"></property> <property name="age" value="12"></property> <property name="salary" value="14000.0"></property> </bean>
实现ApplicationContextAware接口的同时,还要将类 ApplicationContextUtils 交给spring容器管理,这样在
spring容器启动的时候才会将该对象注册,当检测到该类实现该接口时,会把spring上下文对象注入。
下面看一下接口源码:
public interface ApplicationContextAware extends Aware { public abstract void setApplicationContext(ApplicationContext applicationcontext) throws BeansException; }
ApplicationContextAware接口内部只有一个抽象方法setApplicationContext(),同时又继承Aware接口
Aware接口:
public interface Aware { }
空接口,就是一个标记
二:InitializingBean接口
实现该接口的类,需要重写 afterPropertiesSet() ,在实例化的时候,调用完构造方法创建对象之后,会调用
该方法执行一个初始化bean的动作,作用类似@PostContrcut注解和xml中配置的init-method属性,但是执行顺序
有区别
/** * @author Administrator * 员工Emp类,实现InitializingBean接口,重写afterPropertiesSet方法,还有注解@PostConstruct,同时还有 * 在实例化Emp的时候,配置了init-method="init"属性 */ public class Emp implements InitializingBean{ private String name; private Integer age; private Double salary; public Emp(){ System.out.println("constructor..."); } @Override public void afterPropertiesSet() throws Exception { System.out.println("InitializingBean...afterPropertiesSet"); } @PostConstruct public void reload(){ System.out.println("@PostConstruct"); } public void init(){ System.out.println("method-init"); } 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; } public Double getSalary() { return salary; } public void setSalary(Double salary) { this.salary = salary; } @Override public String toString() { return "Emp [name=" + name + ", age=" + age + ", salary=" + salary + "]"; } }
spring容器xml中配置:
<!-- 该配置扫描注解到spring容器 --> <context:annotation-config/> <bean id="applicationContextUtils" class="com.hlcui.util.ApplicationContextUtils"></bean> <!-- 实例化对象,默认调用无参数构造器 --> <bean id="emp" class="com.hlcui.entity.Emp" init-method="init"></bean> <!-- 实例化对象,有参数构造器 --> <bean id="emp2" class="com.hlcui.entity.Emp"> <property name="name" value="Tom"></property> <property name="age" value="12"></property> <property name="salary" value="14000.0"></property> </bean>
<context:annotation-config/> 的作用是扫描注解到spring容器,但是它的作用有限制,不能用作扫描service,controller,component等
组件注册到spring,例如:<context:component-scan = "com.hlcui.*"/> 它的作用是扫描组件,包括上面的配置。
执行结果如下:
constructor... @PostConstruct InitializingBean...afterPropertiesSet method-init constructor... @PostConstruct InitializingBean...afterPropertiesSet
首先执行第一条:
<bean id="emp" class="com.hlcui.entity.Emp" init-method="init"></bean>
1:执行构造器,创建对象
2:创建对象之后,调用有@PostContruct注解的方法初始化bean对象
3:调用afterProptertiesSeet方法初始化对象
4:调用xml中配置的init-method方法初始化对象
执行第2条语句:
<bean id="emp2" class="com.hlcui.entity.Emp"> <property name="name" value="Tom"></property> <property name="age" value="12"></property> <property name="salary" value="14000.0"></property> </bean>
1:执行构造器,创建对象
2:创建对象之后,调用有@PostContruct注解的方法初始化bean对象
3:调用afterProptertiesSeet方法初始化对象
这里创建完对象之后,有一个set注入的过程,会调用Emp类中的set*方法,对对象进行初始化,然后
在调用2,3步骤。
假如把Emp对象中set方法注释,那么第2条语句执行完创建对象之后,就会停止。
三:注入List<接口> 对象
如果在一个类里面需要依赖多个bean对象时,可以通过实现接口的方式,将所有的bean注入,方式如下:
/** * @author Administrator * IBusinessService接口是一个空接口,作为一个标记 */ public interface IBusinessService { public abstract void execute(); }
/** * @author Administrator * */ @Component public class ABusinessServiceImpl implements IBusinessService{ @Override public void execute() { System.out.println("ABusinessServiceImpl..."); } }
@Component public class BBusinessServiceImpl implements IBusinessService{ @Override public void execute() { System.out.println("BBusinessServiceImpl..."); } }
@Component public class CBusinessServiceImpl implements IBusinessService{ @Override public void execute() { System.out.println("CBusinessServiceImpl..."); } }
在*handler类中依赖上面3个类
@Component public class BusinessServiceHandler { @Autowired private List<IBusinessService> services; public void handler(){ for(IBusinessService s:services){ s.execute(); } } }
测试注入效果:
@Test public void testAppContext() { String conf = "applicationContext.xml"; ApplicationContext context = new ClassPathXmlApplicationContext(conf); BusinessServiceHandler bhandler = ApplicationContextUtils.getBean("businessServiceHandler"); bhandler.handler(); }
ABusinessServiceImpl... BBusinessServiceImpl... CBusinessServiceImpl...
执行成功!