前言

今天是7.21日,终于是看完了。。暑假在家学习是真的差点意思

1 Servlet 3.0简介

Servlet 2.0是在web.xml中配置servlet filterlistenerDispatcherServlet等等,而在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容器只扫描controllerview 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容器的实例,相当于原来配置的spring.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注解驱动】(三)servlet3.0的更多相关文章

  1. Spring 注解驱动(二)Servlet 3.0 注解驱动在 Spring MVC 中的应用

    Spring 注解驱动(二)Servlet 3.0 注解驱动在 Spring MVC 中的应用 Spring 系列目录(https://www.cnblogs.com/binarylei/p/1019 ...

  2. 0、Spring 注解驱动开发

    0.Spring注解驱动开发 0.1 简介 <Spring注解驱动开发>是一套帮助我们深入了解Spring原理机制的教程: 现今SpringBoot.SpringCloud技术非常火热,作 ...

  3. 【Spring注解驱动开发】聊聊Spring注解驱动开发那些事儿!

    写在前面 今天,面了一个工作5年的小伙伴,面试结果不理想啊!也不是我说,工作5年了,问多线程的知识:就只知道继承Thread类和实现Runnable接口!问Java集合,竟然说HashMap是线程安全 ...

  4. 【Spring注解驱动开发】组件注册-@ComponentScan-自动扫描组件&指定扫描规则

    写在前面 在实际项目中,我们更多的是使用Spring的包扫描功能对项目中的包进行扫描,凡是在指定的包或子包中的类上标注了@Repository.@Service.@Controller.@Compon ...

  5. 【Spring注解驱动开发】使用@Scope注解设置组件的作用域

    写在前面 Spring容器中的组件默认是单例的,在Spring启动时就会实例化并初始化这些对象,将其放到Spring容器中,之后,每次获取对象时,直接从Spring容器中获取,而不再创建对象.如果每次 ...

  6. 【Spring注解驱动开发】使用@Import注解给容器中快速导入一个组件

    写在前面 我们可以将一些bean组件交由Spring管理,并且Spring支持单实例bean和多实例bean.我们自己写的类,可以通过包扫描+标注注解(@Controller.@Servcie.@Re ...

  7. 【Spring注解驱动开发】在@Import注解中使用ImportSelector接口导入bean

    写在前面 在上一篇关于Spring的@Import注解的文章<[Spring注解驱动开发]使用@Import注解给容器中快速导入一个组件>中,我们简单介绍了如何使用@Import注解给容器 ...

  8. 【Spring注解驱动开发】如何实现方法、构造器位置的自动装配?我这样回答让面试官很满意!

    在 冰河技术 微信公众号前面的文章中,我们介绍了如何使用注解来自动装配Spring组件.之前将的都是在来的字段上添加注解,那有没有什么方法可以实现方法.构造器位置的自动装配吗?今天我们就一起来探讨下如 ...

  9. 【spring 注解驱动开发】Spring AOP原理

    尚学堂spring 注解驱动开发学习笔记之 - AOP原理 AOP原理: 1.AOP原理-AOP功能实现 2.AOP原理-@EnableAspectJAutoProxy 3.AOP原理-Annotat ...

  10. Spring 注解驱动(一)基本使用规则

    Spring 注解驱动(一)基本使用规则 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) 一.基本使用 @Configur ...

随机推荐

  1. Vue系列---【自定义vue组件发布npm仓库】

    自定义vue组件发布npm仓库 参考链接:自定义vue组件发布npm仓库

  2. python+基本3D显示

    想要将双目照片合成立体图实现三维重建,完全没有头绪.但是对三维理解是必须的.所以将以前在单片机上运行的 3D画图 程序移植到python上.效果如下: 没有用numpy.openGL等,只用了纯mat ...

  3. iOS Unity 项目解析

    本文旨在记录Unity 导出的iOS 项目笔记,另带接入SDK的终极方案,顺带对比Android 项目 1蓝色的目录 Data 这个就是项目的数据,每个项目不一样也就是这个目录不一样,是不是可以把这个 ...

  4. Windows server 防火墙开放oracle监听端口

    Windows server 防火墙开放oracle监听端口 Windows server 2008 开放1521端口 Windows server 2003 开放监听程序例外先开防火墙,再开监听例外 ...

  5. element的el-table使用模板插槽在火狐和IE无法显示tooltip(浏览器兼容)

    el-table中使用show-overflow-tooltip属性,配合tooltip出现的浏览器兼容性问题 el-table中使用show-overflow-tooltip属性内容过长被隐藏时显示 ...

  6. DNS服务学习笔记

    1.基本概念 ​ DNS(Domain Name System)域名系统,在TCP/IP网络中有非常重要的地位,能够提供域名与IP地址的解析服务. ​ DNS是一个分布式数据库,命名系统采用层次的逻辑 ...

  7. Java中int型数据类型对应MySQL数据库中哪种类型?

    java类   mysql数据库 java.lang.Byte byte TINYINT java.lang.Short short SMALLINT java.lang.Integer intege ...

  8. jmeter 变量的使用

    jmeter添加变量 一.添加用户自定义变量 添加用户自定义变量 作用:常用数据参数化.当变量发生变化时,不需要逐个脚本修改,只需要修改用户自定义中的变量就可以了. 变量使用如下图 二.函数助手定义变 ...

  9. Crypto入门 (九) easy_RSA

    前言: 建议看这篇随笔之前先看入门(8)数论基础,简单学习下,有利于你看懂后面得算法原理,链接给出:https://www.cnblogs.com/yuanchu/p/13494104.html ea ...

  10. 艾思软件app开发公司帮您分析:开发一个APP多少钱?

    首先你要知道你所要开发的APP, 是不是已经成熟的相同的产品, 如果有的话那还是建议直接购买, 这种已经能满足你需求的成品APP价格会很便宜, 总成本一般也就1到2万的级别. 如果没有那就需要定制开发 ...