简述Spring的常用接口及生命周期

367 阅读3分钟

一 ApplicationContextAware

当一个类实现了这个接口(ApplicationContextAware)之后,这个类就可以方便获得ApplicationContext中的所有bean。换句话说,就是这个类可以直接获取spring配置文件中,所有有引用到的bean对象。

如果读过源码,就会发现BeanFactory中其实保存了Spring Bean容器中的属性,但是BeanFactory是一个接口且不对外暴露,真正暴露的其实是ApplicationContext,应为ApplicationContext中有一个 getAutowireCapableBeanFactory方法,返回 AutowireCapableBeanFactory接口,而AutowireCapableBeanFactory接口继承了BeanFactory接口。

@Service
public class ExcelOutYardManager implements ApplicationContextAware {

    Map<String,IExcelOutYard> excelOutYardMap;

    private Map<String, IExcelOutYard> getExcelOutYard() {
        if (excelOutYardMap == null) {
            excelOutYardMap = new HashMap<String, IExcelOutYard>();
        }
        return excelOutYardMap;
    }

    public IExcelOutYard getImpleService(String messageType){
        return excelOutYardMap.get(messageType);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, IExcelOutYard> beansOfType = applicationContext.getBeansOfType(IExcelOutYard.class);
        this.getExcelOutYard();
        for (IExcelOutYard iExcelOutYard:beansOfType.values()) {
            excelOutYardMap.put(iExcelOutYard.messageType(),iExcelOutYard);
        }
    }
}

二 InitializingBean接口

InitializingBean接口为bean提供了初始化方法的方式,只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候会执行该方法。一般用这个接口在Spring初始化bean之后做一些初始化操作。

这个特性比较多的运用在应用启动的时候设定自动执行的方法,比如应用启动时,从数据库查询出数据,放入对象

@Service
public class EdiInitializingService implements InitializingBean {

    @Override
    public void afterPropertiesSet() throws Exception {
    -----

三 BeanPostProcessor接口

在Spring容器中完成bean实例化、配置以及其它初始化方法前后要添加一些特殊的处理逻辑。需要定义一个或多个BeanPostProcessor接口实现类,然后注册到Spring IOC容器中。

下面这个例子就是根据自定义注解拦截要注入的接口实现类,运用java反射和代理的知识点来进行有效的实现类注入。

public interface HelloService {
    public void sayHello();
}
@Service
public class HelloServiceImpl1 implements HelloService{
    @Override
    public void sayHello() {
        System.out.println("你好我是HelloServiceImpl1");
    }
}
@Service
public class HelloServiceImpl2 implements HelloService{
    @Override
    public void sayHello() {
        System.out.println("你好我是HelloServiceImpl2");
    }
}
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface RountingInjected {
    String value() default "helloServiceImpl1";
}
@Component
public class HelloServiceInjectProcessor implements BeanPostProcessor {

    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> targetCls = bean.getClass();
        Field[] targetFld = targetCls.getDeclaredFields();
        for (Field field : targetFld) {
            //找到制定目标的注解类
            if (field.isAnnotationPresent(RountingInjected.class)) {
                if (!field.getType().isInterface()) {
                    throw new BeanCreationException("RoutingInjected field must be declared as an interface:" + field.getName()
                            + " @Class " + targetCls.getName());
                }
                try {
                    this.handleRoutingInjected(field, bean, field.getType());
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return bean;
    }

    /**
     * @param field
     * @param bean
     * @param type
     * @throws IllegalAccessException
     */
    private void handleRoutingInjected(Field field, Object bean, Class type) throws IllegalAccessException {
        Map<String, Object> candidates = this.applicationContext.getBeansOfType(type);
        field.setAccessible(true);
        if (candidates.size() == 1) {
            field.set(bean, candidates.values().iterator().next());
        } else if (candidates.size() == 2) {
            String injectVal = field.getAnnotation(RountingInjected.class).value();
            Object proxy = RoutingBeanProxyFactory.createProxy(injectVal, type, candidates);
            field.set(bean, proxy);
        } else {
            throw new IllegalArgumentException("Find more than 2 beans for type: " + type);
        }
    }

}
public class RoutingBeanProxyFactory {

    private final static String DEFAULT_BEAN_NAME = "helloServiceImpl1";

    public static Object createProxy(String name, Class type, Map<String, Object> candidates) {
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setInterfaces(type);
        proxyFactory.addAdvice(new VersionRoutingMethodInterceptor(name, candidates));
        return proxyFactory.getProxy();
    }

    static class VersionRoutingMethodInterceptor implements MethodInterceptor {
        private Object targetObject;

        public VersionRoutingMethodInterceptor(String name, Map<String, Object> beans) {
            this.targetObject = beans.get(name);
            if (this.targetObject == null) {
                this.targetObject = beans.get(DEFAULT_BEAN_NAME);
            }
        }

        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            return invocation.getMethod().invoke(this.targetObject, invocation.getArguments());
        }
    }
}
@Component
public class HelloServiceTest {

    @RountingInjected(value = "helloServiceImpl2")
    private HelloService helloService;

    public void testSayHello() {
        helloService.sayHello();
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext("colin.spring.basic.advanced.bbp");
        HelloServiceTest helloServiceTest = applicationContext.getBean(HelloServiceTest.class);
        helloServiceTest.testSayHello();
    }

}

四 DisposableBean接口

Spring bean被销毁时调用,这个使用较少,但也是生命周期的一部分

五 Spring生命周期图

image.png

六 结语

如果和spring生命周期图对照,那么可以看到,每一个接口的实现就是spring生命周期的一部分,这些接口实际上也体现了Spring框架的灵活性,理解这些接口对spring框架的使用有极大的帮助。