задний план
В проекте мы столкнемся со следующими ситуациями, то есть нам нужно выполнить некоторые операции при запуске Tomcat.Первое, о чем мы думаем, это наследовать ServletContextListener, а затем добавить операции, которые будут выполняться в contextInitialized.Это метод ; затем для проекта Spring вы также можете наследовать InitialzationBean для реализации, выполнения метода при инициализации и уничтожении bean-компонентов, потому что ServletContextListener необходимо настроить в web.xml, и он может внедрять другие bean-компоненты, поэтому я решил наследовать InitialzationBean для достижения.
яма столкнулась
Создайте новый класс, наследуйте InitialzationBean, код выглядит следующим образом:
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
@Component
public class DoOnStart implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("xxxxxxxx");
}
}
Я думал, что это нормально. После запуска Tomcat я обнаружил, что метод afterPropertiesSet был выполнен дважды. Странно, Spring инициализирует bean-компоненты дважды? С этим предположением была проведена следующая проверка:
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class DoOnStart implements InitializingBean, ApplicationContextAware {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("xxxxxxxx");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("xxxxxxxx");
}
}
С помощью Debug обнаруживается, что метод setApplicationContext действительно выполняется дважды, то есть были инициализированы два контейнера. пространство имен spring-servlet, см. здесь, Мао Саид:
В первый раз Spring инициализирует bean-компонент, а во второй раз Spring MVC снова инициализирует bean-компонент.
Так как же решить проблему загрузки двух пар? То есть, чтобы Spring MVC сканировал только аннотацию @Controller, конфигурация выглядит следующим образом:
<!-- spring 配置文件-->
<context:component-scan base-package="com.xxx.xxx">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
<!-- spring mvc -->
<context:component-scan base-package="com.xxx.xxx.web" use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
Зачем отделять файлы конфигурации Spring от файлов конфигурации Spring MVC?
Мы тестируем с помощью следующего кода:
@Service
public class DoOnStart implements InitializingBean {
@Autowired
private XXXController xxxController;
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("xxxxxxxx");
}
}
Бывают следующие ситуации:
- Spring загружает все компоненты, MVC загружает контроллер
- Может
- Spring загружает все bean-компоненты, контейнер MVC ничего не загружает
- Может
- Spring загружает все bean-компоненты, кроме контроллера, MVC загружает только контроллер
- Нет, родительский контейнер не может получить доступ к компонентам дочернего контейнера.
- Spring не загружает бины, MVC загружает все бины
- Может
Получается, что Spring — это родительский контейнер, а Spring MVC — дочерний контейнер, дочерний контейнер может получить доступ к bean-компонентам родительского контейнера, но родительский контейнер не может получить доступ к bean-компонентам дочернего контейнера.
- Существуют ли одноэлементные компоненты в одном или двух экземплярах в родительско-дочернем контейнере?
Инициализированный дважды, контейнер Spring сначала инициализирует bean-компонент, а контейнер MVC инициализирует bean-компонент, поэтому должно быть два bean-компонента.
- Почему бы не сканировать все bean-компоненты в подконтейнерах?
Недостатком является то, что он не способствует расширению.
Анализ исходного кода
Взглянув на исходный класс загруженного компонента Spring AbstractAutowireCapableBeanFactory, мы можем увидеть загадку. Метод invokeInitMethods в классе AbstractAutowireCapableBeanFactory объясняется очень четко. Исходный код выглядит следующим образом:
protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
throws Throwable {
//判断该bean是否实现了实现了InitializingBean接口,如果实现了InitializingBean接口,则只掉调用bean的afterPropertiesSet方法
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
public Object run() throws Exception {
//直接调用afterPropertiesSet
((InitializingBean) bean).afterPropertiesSet();
return null;
}
},getAccessControlContext());
} catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
//直接调用afterPropertiesSet
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null) {
String initMethodName = mbd.getInitMethodName();
//判断是否指定了init-method方法,如果指定了init-method方法,则再调用制定的init-method
if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
//进一步查看该方法的源码,可以发现init-method方法中指定的方法是通过反射实现
invokeCustomInitMethod(beanName, bean, mbd);
}
}
Суммировать
-
Spring предоставляет два способа инициализации bean-компонентов, реализовать интерфейс initializingBean, реализовать метод instialPropertiesset или указать в файле конфигурации два способа, которые можно использовать в обоих случаях.
-
Реализация интерфейса InitializingBean заключается в непосредственном вызове метода afterPropertiesSet, что относительно более эффективно, чем вызов метода, указанного init-методом, через отражение. Но метод init удаляет зависимость от Spring
-
Если при вызове метода afterPropertiesSet возникает ошибка, метод, указанный init-методом, не вызывается
-
Чтобы отделить файлы конфигурации Spring от файлов конфигурации Spring MVC