【Spring注解驱动】(三)Servlet 3.0
前言
今天是7.21日,终于是看完了。。暑假在家学习是真的差点意思
1 Servlet 3.0简介
Servlet 2.0是在web.xml中配置servlet filter、listener、DispatcherServlet等等,而在Servlet 3.0中,Spring则为我们提供了一系列注解实现了上面的配置。
Servlet 3.0需要tomcat 7.0及以上版本
2 Servlet 3.0 注解开发

首先创建一个servlet3.0的项目,idea中动态网页的创建是JavaEnterprise选项,project template选择web application选项,会自动创建servlet、index.jsp以及web.xml(这里不需要再使用了)
2.1 @WebServlet
在servlet上添加注解,既可实现脱离web.xml配置Servlet
@WebServlet(name = "helloServlet", value = "/hello-servlet")
public class HelloServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.getWriter().write("Hello!");
}
public void destroy() {
}
}
2.2 Shared libraries / runtimes pluggability 运行时插件能力
Servlet容器启动会扫描当前应用的每一个Jar包中,是否包含ServletContainerInitializer的实现类。而一般情况下会将实现类的指向(即其全类名)放置在META-INF/services文件夹下的javax.servlet.ServletContainerInitializer文件中,文件内容指向实现类。
总结:容器在启动应用的时候,会扫描当前应用的每一个Jar包里面(Idea应该放在Resource下面)META-INF/services/javax.servlet.ServletContainerInitialize指定的实现类,启动并运行这个实现类方法。
①创建一个ServletContainerInitializer的自定义实现类MyServletContainerInitializer:
public class MyServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
}
}
②在Resource/META-INF/services/下创建文件javax.servlet.ServletContainerInitialize并添加实现类全类名
com.hikaru.servlet3.MyServletContainerInitializer
如此一来,当servlet容器启动的时候,就会扫描到实现类,执行实现类的onStartup方法。
2.3 @HandlesTypes注解指定感兴趣类型
容器启动的时候,会将@HandlesType指定的这个类型下面的子类(不包括这个类型)(实现类、接口等)传递过来。而onStartup方法的两个参数:
①set:感兴趣的所有子类型
@HandlesTypes(value = {HelloService.class})
public class MyServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
System.out.println("感兴趣的类型:");
for(Class<?> clax:set) {
System.out.println(clax);
}
}
}
输出结果:
感兴趣的类型:
class com.hikaru.service.HelloServiceImpl
class com.hikaru.service.AbstractHelloService
interface com.hikaru.service.HelloServiceExt
2.4 使用ServletContext注册Web组件(Servlet、Filter、Listener)
onStartup方法的第两个参数
②ServletContext:代表web应用的ServletContext,一个web应用包含一个。
2.4.1 创建并注册Servlet
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doGet(req, resp);
resp.getWriter().write("Jay");
}
}
注册组件:
// 注册组件
ServletRegistration.Dynamic userServlet = servletContext.addServlet("userServlet", new UserServlet());
// 配置servlet的配置信息
userServlet.addMapping("/user");
2.4.2 创建并注册Filter
public class UserFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("UserFilter doFilter");
filterChain.doFilter(servletRequest, servletResponse);
}
}
注册组件:
// 注册Filter组件
FilterRegistration.Dynamic userFilter = servletContext.addFilter("userFilter", UserFilter.class);
// 配置组件配置信息
userFilter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
2.4.3 创建并注册Listener
public class UserListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("UserListener contextInitialized");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("UserListener contextDestroyed");
}
}
注册组件
// 注册Listener组件
servletContext.addListener(UserListener.class);
注册组件只需提供组件的class即可,当然也可以进行new创建
综上,MyServletContainerInitializer如下:
@HandlesTypes(value = {HelloService.class})
public class MyServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
System.out.println("感兴趣的类型:");
for(Class<?> clax:set) {
System.out.println(clax);
}
// 注册Servlet组件
ServletRegistration.Dynamic userServlet = servletContext.addServlet("userServlet", new UserServlet());
// 配置servlet的配置信息
userServlet.addMapping("/user");
// 注册Listener组件
servletContext.addListener(new UserListener());
// 注册Filter组件
FilterRegistration.Dynamic userFilter = servletContext.addFilter("userFilter", UserFilter.class);
// 配置组件配置信息
userFilter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
}
}
3 Servlet 3.0 整合SpringMVC
①使用maven-war-plugin插件
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
②导入spring-webmvc依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.1</version>
</dependency>
导入spring-webmvc之后,会自动导入其依赖的依赖,如aop、ioc(core,beans,context,experssion)以及webmvc的依赖
③导入servlet-api依赖
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
tomcat服务器中也会存在servlet-api的jar包,因此在打成war包的时候不应该包括这一部分,否则会造成冲突
scope的各种取值详解
| scope取值 | 有效范围(compile, runtime, test) | 依赖传递 | 例子 |
|---|---|---|---|
| compile | all | 是 | spring-core |
| provided | compile, test | 否 | servlet-api |
| runtime | runtime, test | 是 | JDBC驱动 |
| test | test | 否 | JUnit |
| system | compile, test | 是 |
3.1 Spring启动SpringMVC web容器分析
①上面有讲过,web容器在启动的时候,会扫描每个jar包下的\META-INF\services\javax.servlet.ServletContainerInitializer,而我们导入的spring-web是存在这个文件的,可以看到其内容指向SpringServletContainerInitializer
org.springframework.web.SpringServletContainerInitializer
②因此接下来会加载这个文件内容指向的类,该类(Spring应用)通过@HandlesTypes({WebApplicationInitializer.class}),一启动就会加载WebApplicationInitializer接口下的所有组件,并为非接口、抽象类的组件创建实例对象。
③而WebApplicationInitializer有三个实现类或者子类:
第一个是实现类 AbstractContextLoaderInitializer:通过createRootApplicationContext()方法创建根组件,将listener加入到servlet web容器,以此利用Listener创建根容器,实现容器之间的子父关系。
protected void registerContextLoaderListener(ServletContext servletContext) {
WebApplicationContext rootAppContext = this.createRootApplicationContext();
if (rootAppContext != null) {
ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
listener.setContextInitializers(this.getRootApplicationContextInitializers());
servletContext.addListener(listener);
} else {
this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context");
}
}
主要思想是通过在父容器中注册一个ContextLoaderListener,这样在父容器启动的时候会触发监听器回调,然后将根容器添加到父容器中。
第二个是实现类AbstractContextLoaderInitializer的子类AbstractDispatcherServletInitializer ,首先通过createServletApplicationContext()方法创建了一个web的IOC容器,然后通过createDispatcherServlet(servletAppContext)创建了一个DispatcherServlet,最后通过servletContext.addServlet(servletName, dispatcherServlet)将其添加到web容器并设置一系列映射、启动配置。
protected void registerDispatcherServlet(ServletContext servletContext) {
String servletName = this.getServletName();
Assert.hasLength(servletName, "getServletName() must not return null or empty");
WebApplicationContext servletAppContext = this.createServletApplicationContext();
Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");
FrameworkServlet dispatcherServlet = this.createDispatcherServlet(servletAppContext);
Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
dispatcherServlet.setContextInitializers(this.getServletApplicationContextInitializers());
Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
if (registration == null) {
throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. Check if there is another servlet registered under the same name.");
} else {
registration.setLoadOnStartup(1);
registration.addMapping(this.getServletMappings());
registration.setAsyncSupported(this.isAsyncSupported());
Filter[] filters = this.getServletFilters();
if (!ObjectUtils.isEmpty(filters)) {
Filter[] var7 = filters;
int var8 = filters.length;
for(int var9 = 0; var9 < var8; ++var9) {
Filter filter = var7[var9];
this.registerServletFilter(servletContext, filter);
}
}
this.customizeRegistration(registration);
}
}
第三个是实现类子类AbstractDispatcherServletInitializer的子类AbstractAnnotationConfigDispatcherServletInitializer,这个类实现了注解方式的Dispatcher注册,首先通过抽象方法createRootApplicationContext()创建根容器,然后通过AnnotationConfigWebApplicationContext创建一个web Ioc容器,最后在容器中注册根容器,以此实现两个容器的子父类关系。
总结:以注解方式启动MVC,只需要继承AbstractAnnotationConfigDispatcherServletInitializer,实现其抽象方法指定DispatcherServlet的配置信息即可不必使用web.xml配置。
3.2 以前Spring整合SpringMVC的做法
Spring官网中使用了父子容器的形式,即父容器web容器和子容器根容器,web容器只扫描controller、view resolvers以及其他和web组件相关的bean,而根容器扫描业务层和持久层、控制数据源、事务相关的组件。

如下面的项目中,使用的是listener配合xml的方式构建子父容器,Spring父容器创建

时触发Listener回调通过xml文件将service、事务等组件读取到Spring IOC容器,再建立Spring容器和SpringMVC容器的子父类关系。
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-web-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-persist-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
3.3 基于注解的Spring SpringMVC整合
①创建AbstractAnnotationConfigDispatcherServletInitializer类的自定义实现类MyWebAppInitializer,相当于原来的web.xml,其中的三个实现方法:
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{RootConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{AppConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
1)getRootConfigClasses:返回父容器即根容器的实例,相当于原来的配置Spring 容器的spring.xml配置文件
2) getServletConfigClasses:返回子容器即web容器的实例,相当于原来配置的springMVC.xml。
3)getServletMappings:配置DispatcherServlet的配置信息,"/"表示拦截除jsp外的所有请求,"/*"表示拦截所有包括jsp的请求,而jsp页面是由tomcatjsp引擎解析的,所以这里应该使用"/"。
②创建父子容器的配置类
AppConfig:只扫描注解为Config组件
@Configuration
@ComponentScan(value = "com.hikaru", includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
}, useDefaultFilters = false)
public class AppConfig {
}
includeFilters想要生效必须禁用默认的过滤规则
RootConfig
@Configuration
@ComponentScan(value = "com.hikaru", excludeFilters = {
@ComponentScan.Filter(type=FilterType.ANNOTATION, classes = {Controller.class})
})
public class RootConfig {
}
③创建相应组件
UserController
@Controller
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/user")
@ResponseBody
public String user() {
return userService.getUser();
}
}
UserServiceImpl
@Service
public class UserServiceImpl implements UserService{
@Override
public String getUser() {
return "Jay";
}
}
如此一来,Controller的组件和Service的组件都在Spring父容器中了,便完成了Spring SpringMVC的整合
3.4 通过WebMvcConfigurer接口定制与接管SpringMVC
@Configuration
@ComponentScan(value = "com.hikaru", includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
}, useDefaultFilters = false)
public class AppConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
}
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
}
@Override
public void addFormatters(FormatterRegistry registry) {
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
}
@Override
public void addCorsMappings(CorsRegistry registry) {
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
}
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
}
@Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
}
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
}
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
}
@Override
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
}
@Override
public Validator getValidator() {
return null;
}
@Override
public MessageCodesResolver getMessageCodesResolver() {
return null;
}
}
如configureDefaultServletHandling()实现方法就是用来开启静态资源,开启方法直接调用参数的enable方法即可。相当于原xml实现中的<mvc:default-servlet-handler>
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
java 8开始支持接口默认实现了,即实现一个接口不需要实现它的所有方法,所以这里不需要再使用WebMvcConfigurerAdapter直接使用WebMvcConfigurer接口即可。
3.4.1 configureViewResolvers()配置视图解析器
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
// registry.jsp();
registry.jsp("/WEB-INF/views", ".jsp");
}
如果采用默认的写法,会是下面的配置:
public UrlBasedViewResolverRegistration jsp() {
return this.jsp("/WEB-INF/", ".jsp");
}
然后配置servlet进行测试,但不知道为什么我这里一直报500视图解析器没有启动。。找了半天也没有找到哪里写的不对,以后有机会再回来看吧。。不过前后端分离这种写法基本也用不到了。
后面的静态资源、拦截器也就先不看了
4 servlet 3.0 异步请求
4.1 原生Servlet实现异步处理
在Servlet 3.0之前,servlet采用Thread-Per-Request方式处理请求,即每一个Http请求都由某一个线程从头到尾负责处理。
如果一个请求需要进行IO操作,比如访问数据库,调用第三方服务接口等,那么其所对应的线程会同步地等待IO操作完成,而IO操作是非常慢的,所以此时的线程并不能及时放回线程池以供后续使用,在并发量越来越大的情况下,这将带来严重的性能问题,即便是像Spring、Struts这样高层的框架也脱离不了这样的桎梏,因为他们都是建立在Servlet上的,为了解决这样的问题,Servlet3.0引入了异步处理,然后在Servlet3.1中又引入了非阻塞IO进一步增强异步处理的性能。
测试
@WebServlet(value = "/hello-servlet", asyncSupported = true)
public class HelloServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
System.out.println("主线程开始:" + Thread.currentThread());
// 开启异步模式
AsyncContext asyncContext = request.startAsync();
// 业务逻辑进行异步处理;开始异步处理
asyncContext.start(new Runnable() {
@Override
public void run() {
try {
System.out.println("副线程开始");
sayHello();
// 通知异步完成
asyncContext.complete();
// 获取异步上下文
AsyncContext asyncContext1 = request.getAsyncContext();
// 获取响应
ServletResponse response1 = asyncContext1.getResponse();
response1.getWriter().write("hello async");
System.out.println("副线程结束");
} catch (InterruptedException | IOException e) {
e.printStackTrace();
}
}
});
System.out.println("主线程结束:" + Thread.currentThread());
}
public void sayHello() throws InterruptedException {
System.out.println(Thread.currentThread() + "ing...");
Thread.sleep(3000);
}
public void destroy() {
}
}
4.2 SpringMVC实现异步处理
4.2.1 通过返回Callable
借助Callable实现异步处理:
①控制器返回一个Callable
②Spring异步处理,将Callable提交到TaskExecutor 使用一个隔离的线程进行执行
③DispatcherServlet将所有的Filter退出web容器的线程,但是response保持打开状态,此时还能向浏览器进行响应
④Callable返回结果,SpringMVC将请求重新派发给容器,恢复之前的处理。
⑤根据Callable的返回结果,SpringMVC继续进行视图渲染流程(从收请求到视图渲染)
@Controller
public class AsyncController {
@RequestMapping("/async")
@ResponseBody
public Callable<String> async01() {
System.out.println("主线程开始" + Thread.currentThread());
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("副线程开始:" + Thread.currentThread());
Thread.sleep(2000);
System.out.println("副线程结束:" + Thread.currentThread());
return null;
}
};
System.out.println("主线程结束" + Thread.currentThread());
return callable;
}
}
输出结果:
主线程开始Thread[http-nio-8080-exec-6,5,main]
主线程结束Thread[http-nio-8080-exec-6,5,main]
副线程开始:Thread[MvcAsync1,5,main]
副线程结束:Thread[MvcAsync1,5,main]
4.2.2 通过返回DefferredResult

基本思路是通过消息中间件来起到缓冲的作用,来处理异步请求。如应用1只能获取响应创建订单请求,但是只有应用2能进行创建订单,那么应用1就可以把创建订单的消息放在消息中间件中,应用2负责监听消息中间件,并创建订单后将处理结果返回放在消息中间件中,线程2负责监听消息中间件的返回结果,并最终响应给客户端。
①首先创建一个DeferredQueue队列,用来存储和获取请求消息
②再创建一个createOrder请求,并将消息存储到DeferredQueue队列,一旦另一个create请求完成则此createOrder请求也进行响应
@ResponseBody
@RequestMapping("/createOrder")
public DeferredResult<Object> createOrder() {
DeferredResult<Object> deferredResult =
new DeferredResult((long) 3000, "create fail");
DeferredQueue.save(deferredResult);
return deferredResult;
}
③最后创建一个create请求通过获取DeferredQueue队列消息,用于监听createOrder请求实际创建订单,创建完成之后将结果返回
@RequestMapping("/create")
@ResponseBody
public String create() {
String s = UUID.randomUUID().toString();
DeferredResult<Object> deferredResult = DeferredQueue.get();
deferredResult.setResult(deferredResult);
return "success ===>" + s;
}
结果:
http://localhost:8080/SpringMVC_Async_war_exploded/createOrder
30616c4d-d58c-4909-991c-9bcc6cef5d58
http://localhost:8080/SpringMVC_Async_war_exploded/create
success ===>30616c4d-d58c-4909-991c-9bcc6cef5d58
【Spring注解驱动】(三)Servlet 3.0的更多相关文章
- Spring注解驱动开发(七)-----servlet3.0、springmvc
ServletContainerInitializer Shared libraries(共享库) / runtimes pluggability(运行时插件能力) 1.Servlet容器启动会扫描, ...
- Spring 注解驱动(二)Servlet 3.0 注解驱动在 Spring MVC 中的应用
Spring 注解驱动(二)Servlet 3.0 注解驱动在 Spring MVC 中的应用 Spring 系列目录(https://www.cnblogs.com/binarylei/p/1019 ...
- 0、Spring 注解驱动开发
0.Spring注解驱动开发 0.1 简介 <Spring注解驱动开发>是一套帮助我们深入了解Spring原理机制的教程: 现今SpringBoot.SpringCloud技术非常火热,作 ...
- 【Spring注解驱动开发】面试官:如何将Service注入到Servlet中?朋友又栽了!!
写在前面 最近,一位读者出去面试前准备了很久,信心满满的去面试.没想到面试官的一个问题把他难住了.面试官的问题是这样的:如何使用Spring将Service注入到Servlet中呢?这位读者平时也是很 ...
- 【Spring注解驱动开发】聊聊Spring注解驱动开发那些事儿!
写在前面 今天,面了一个工作5年的小伙伴,面试结果不理想啊!也不是我说,工作5年了,问多线程的知识:就只知道继承Thread类和实现Runnable接口!问Java集合,竟然说HashMap是线程安全 ...
- 【Spring注解驱动开发】组件注册-@ComponentScan-自动扫描组件&指定扫描规则
写在前面 在实际项目中,我们更多的是使用Spring的包扫描功能对项目中的包进行扫描,凡是在指定的包或子包中的类上标注了@Repository.@Service.@Controller.@Compon ...
- 【Spring注解驱动开发】使用@Scope注解设置组件的作用域
写在前面 Spring容器中的组件默认是单例的,在Spring启动时就会实例化并初始化这些对象,将其放到Spring容器中,之后,每次获取对象时,直接从Spring容器中获取,而不再创建对象.如果每次 ...
- 【Spring注解驱动开发】使用@Import注解给容器中快速导入一个组件
写在前面 我们可以将一些bean组件交由Spring管理,并且Spring支持单实例bean和多实例bean.我们自己写的类,可以通过包扫描+标注注解(@Controller.@Servcie.@Re ...
- 【Spring注解驱动开发】在@Import注解中使用ImportSelector接口导入bean
写在前面 在上一篇关于Spring的@Import注解的文章<[Spring注解驱动开发]使用@Import注解给容器中快速导入一个组件>中,我们简单介绍了如何使用@Import注解给容器 ...
- 【Spring注解驱动开发】如何实现方法、构造器位置的自动装配?我这样回答让面试官很满意!
在 冰河技术 微信公众号前面的文章中,我们介绍了如何使用注解来自动装配Spring组件.之前将的都是在来的字段上添加注解,那有没有什么方法可以实现方法.构造器位置的自动装配吗?今天我们就一起来探讨下如 ...
随机推荐
- 量化交易-可视化展示(grafana)
先上图 简单的实现了一下,效果还好,可玩性强 大概部署mysql+grafana step 1: 服务器:阿里云,ucloud啥的随意,配置也不需要什么,我的是阿里云1核1GB,足以 我用的ubunt ...
- Think Python 学习笔记
#!/usr/bin/env python# coding: utf-8# # Think Python 学习笔记# 1.关于异或计算符# In[2]:6^2# 2.关于函数# 注意:变量名称不能用数 ...
- jxg项目Day3 -数据库
可以通过 DOS 命令启动 MySQL 服务,点击"开始"菜单,在搜索框中输入"cmd",以管理员身份运行,按回车键,弹出命令提示符界面.mysql启动:net ...
- RabbitMQ service does not start due to corrupted mnesia database in RSA Security Analytics
rabbitmq异常:mnesia database损坏
- 简单总结一下html中能见到的各种相对路径
试列举如下(在本文中,星号表示资源名): href="/*" href="//*" href="*" href="./*" ...
- windows server 2008 创建计划任务不能正常执行
- java8利用流和lambda表达式对list遍历处理
java8的lambda表达式提供了一些方便list操作的方法,主要涵盖分组.过滤.求和.最值.排序.去重. 优点: (1) 简洁,跟之前的传统写法对比,能少写不少代码; (2) 易并行计算.尤其适用 ...
- leetcode medium 记录 1-50
# Title Solution 二刷 Difficulty 备注 2 Add Two Numbers X Medium 3 Longest Substring Wi ...
- Java笔记第十一弹
TCP通信程序 TCP发送数据 //需要进行三次握手 import java.io.*; public class Main{ public static void main(String[] arg ...
- Vue模板语法 && 数据绑定
模板语法 Vue模板语法包括两大类 插值语法 功能:用于解析标签体内容. 写法:{{xxx}},xxx是js表达式,可以直接读取倒data中所有区域. 指令语法 功能:用于解析标签(包括:标签属性.标 ...