聊聊、Java 命令 第四篇

四、@MapperScan和@ComponentScan
为什么上面这些注释或者标签能被解析呢?它们的区别又是什么?
(1)先说说MapperScan和ComponentScan区别
ComponentScan是扫描包下面的类,将bean注入到实体类中。MapperScan不只是将bean注入到实体类中,还要动态生成Mapper实现类。
(2)为什么注释可以被解析
这里必须说下容器,容器就是运行程序的器件。像我们最熟悉的Tomcat,Jetty等等,要让容器知道怎么运行你的程序,那么你的程序必须符合容器的规范,或者说遵循规矩。
Tomcat是怎么做到的呢?我们不用web.xml来配置Servlet,我们就用Java代码,继承AbstractAnnotationConfigDispatcherServletInitializer就行了。怎么做到的?
这里就不去分析catalina启动脚本,后面单独分析。
我们只说下Spring代码层面的逻辑,首先javax.servlet.ServletContainerInitializer这个接口类。这个类的存在就是为了实现Java代码配置。而且是Servlet 3.0版本才提供,低于3.0都没法用
Java代码实现配置。看看源代码
package javax.servlet;
public interface ServletContainerInitializer {
public void onStartup(Set<Class<?>> c, ServletContext ctx)
        throws ServletException;
}
其中有这样一段注解:
* Interface which allows a library/runtime to be notified of a web
* application's startup phase and perform any required programmatic
* registration of servlets, filters, and listeners in response to it.
*
* <p>Implementations of this interface may be annotated with
* {@link javax.servlet.annotation.HandlesTypes HandlesTypes}, in order to
* receive (at their {@link #onStartup} method) the Set of application
* classes that implement, extend, or have been annotated with the class
* types specified by the annotation.
*
* <p>If an implementation of this interface does not use this annotation,
* or none of the application classes match the ones specified
* by the annotation, the container must pass a <tt>null</tt> Set of classes
* to {@link #onStartup}.
*
* <p>Implementations of this interface must be declared by a JAR file
* resource located inside the <tt>META-INF/services</tt> directory and
* named for the fully qualified class name of this interface, and will be
* discovered using the runtime's service provider lookup mechanism
* or a container specific mechanism that is semantically equivalent to
* it. In either case, ServletContainerInitializer services from web
* fragment JAR files excluded from an absolute ordering must be ignored,
* and the order in which these services are discovered must follow the
* application's classloading delegation model.
意思是,这个接口是为了程序启动时,实现Servlet,Filter,Listener注册的,但是实现这个接口的类必须在一个Jar包的META-INF/services目录下面的javax.servlet.ServletContainerInitializer文件中有定义。而且实现类
必须有@HandlesTypes注解。什么意思?
(1)@HandlesTypes
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface HandlesTypes {
    Class[] value();
}
我们来看看,Spring-web-xxx.jar包下面的META-INF/services文件夹,就有javax.servlet.ServletContainerInitializer这么一个文件,里面内容是org.springframework.web.SpringServletContainerInitializer。
这里可能需要了解JDK SPI机制,SPI全称为(Service Provider Interface) ,是JDK内置的一种服务提供发现机制。后面文章单独分析。
也就是说,启动容器之后,会调用org.springframework.web.SpringServletContainerInitializer中的onStartup方法。
那么org.springframework.web.SpringServletContainerInitializer这个类又是什么样?来看看
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
 public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
   throws ServletException {
    List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();
  if (webAppInitializerClasses != null) {
   for (Class<?> waiClass : webAppInitializerClasses) {
    // Be defensive: Some servlet containers provide us with invalid classes,
    // no matter what @HandlesTypes says...
    if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
      WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
     try {
      initializers.add((WebApplicationInitializer) waiClass.newInstance());
     }
     catch (Throwable ex) {
      throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
     }
    }
   }
  }
  if (initializers.isEmpty()) {
   servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
   return;
  }
  servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
  AnnotationAwareOrderComparator.sort(initializers);
  for (WebApplicationInitializer initializer : initializers) {
   initializer.onStartup(servletContext);
  }
   }
}
有这么一段注解:
 * Delegate the {@code ServletContext} to any {@link WebApplicationInitializer}
 * implementations present on the application classpath.
 * <p>Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)},
 * Servlet 3.0+ containers will automatically scan the classpath for implementations
 * of Spring's {@code WebApplicationInitializer} interface and provide the set of all
 * such types to the {@code webAppInitializerClasses} parameter of this method.
 * <p>If no {@code WebApplicationInitializer} implementations are found on the classpath,
 * this method is effectively a no-op. An INFO-level log message will be issued notifying
 * the user that the {@code ServletContainerInitializer} has indeed been invoked but that
 * no {@code WebApplicationInitializer} implementations were found.
 * <p>Assuming that one or more {@code WebApplicationInitializer} types are detected,
 * they will be instantiated (and <em>sorted</em> if the @{@link
 * org.springframework.core.annotation.Order @Order} annotation is present or
 * the {@link org.springframework.core.Ordered Ordered} interface has been
 * implemented). Then the {@link WebApplicationInitializer#onStartup(ServletContext)}
 * method will be invoked on each instance, delegating the {@code ServletContext} such
 * that each instance may register and configure servlets such as Spring's
 * {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener},
 * or any other Servlet API componentry such as filters.
 * @param webAppInitializerClasses all implementations of
 * {@link WebApplicationInitializer} found on the application classpath
 * @param servletContext the servlet context to be initialized
 * @see WebApplicationInitializer#onStartup(ServletContext)
 * @see AnnotationAwareOrderComparator
原来如此,Servlet 3.0+ 容器会自动去扫描classpath下所有实现了WebApplicationInitializer接口的类,如果没有找到相关类,那么An INFO-level log message will be issued notifying the user,会有一条
INFO级别的日志。如果找到了多个实现类,那么都会被实例化,如果实现了org.springframework.core.Ordered接口或者添加了@Order注解,那么就按照顺序来。
(1)org.springframework.core.Ordered
public interface Ordered {
 /**
  * Useful constant for the highest precedence value.
  * @see java.lang.Integer#MIN_VALUE
  */
 int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
 /**
  * Useful constant for the lowest precedence value.
  * @see java.lang.Integer#MAX_VALUE
  */
 int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
 int getOrder();
}
(2)@Order
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Documented
public @interface Order {
 /**
  * The order value.
  * <p>Default is {@link Ordered#LOWEST_PRECEDENCE}.
  * @see Ordered#getOrder()
  */
 int value() default Ordered.LOWEST_PRECEDENCE;
}
为什么@MapperScan和@ComponentScan会被调用?到这里就很清晰了,<mybatis:scan>和<context:component-scan>标签的调用就很简单了,因为我们在xml里面配置了。
下一篇来聊聊,这些注解和标签被调用了,怎么识别的呢?我怎么知道要去扫描这个包下面的类?哪里有定义吗?

猜你喜欢

转载自www.cnblogs.com/xums/p/10339328.html