tomcat的web三大组件Sciidea搭建web/maven的tomcat项目
文章目录
- 1. web项目的搭建
- 1. 创建web项目
- 2.修改web.xml版本
- 3.添加servlet、jsp依赖
- 4.servlet示例(使用注解)
- 5.配置tomcat
- 6.添加artifact
- 7.部署
- 8.启动tomcat、访问
- 9.打war包
- 10.部署到tomcat
- 2.maven的项目搭建
- 1.创建项目
- 图解
- 2.tomcat启动方式
- 图解
- idea打war包
- 3.改为使用maven启动
- 图解
- idea打war包
- 5.三大组件
- ==web.xml配置方式==
- 1.Servlet
- 1.介绍
- 2.Servlet接口
- 3.ServletConfig
- 4.使用
- 2.Filter
- 1.介绍
- 2.Filter接口
- 3.FilterConfig
- 4.使用
- 5.源码分析
- 3.监听器
- 1.介绍
- 2.Listener接口
- 3.ServletContext
- 4.使用
- ==注解的配置方式==
- 1.@WebServlet
- 2.@WebFilter
- 3.@WebListener
- ==ServletContextListener配置tomcat组件方式==
- servlet 3.0之动态注册
- ServletContainerInitializer
- ==SCI规范==
- ==执行顺序==
- ==意义==
- 6.tomcat组件初始化顺序和请求执行顺序
- ==初始化顺序==
- ==请求执行顺序==
- 7.servlet的基本用法
- 1.请求转发Forward
- 2.重定向Redirect
- 3.保存当前线程的请求对象
1. web项目的搭建
1. 创建web项目
2.修改web.xml版本
3.添加servlet、jsp依赖
4.servlet示例(使用注解)
5.配置tomcat
6.添加artifact
7.部署
8.启动tomcat、访问
9.打war包
10.部署到tomcat
将war包复制到tomcat的webapps目录下,在tomcat的bin目录下双击startup.bat运行项目
-
如果是linux双击startup.sh即可运行项目,
-
tomcat如果早就启动了,那么复制war包到webapps目录下后,war包会自动解压并运行
2.maven的项目搭建
1.创建项目
图解
2.tomcat启动方式
图解
idea打war包
3.改为使用maven启动
上面使用tomcat启动时,pom.xml中什么都没有写,现在改成使用maven启动,pom的xml中如下配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.zzhua</groupId><artifactId>tomcat-demo</artifactId><version>1.0-SNAPSHOT</version><!--一定要设置打包方式为war--><packaging>war</packaging><dependencies><dependency><groupId>javax.servlet.jsp</groupId><artifactId>jsp-api</artifactId><version>2.1</version><scope>provided</scope></dependency><!-- javax.servlet-api --><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency></dependencies><build><plugins><!-- 配置tomcat的运行插件 --><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.2</version><configuration><!-- 配置端口 --><port>8080</port><!-- 配置urlencoding --><uriEncoding>UTF-8</uriEncoding><!-- 配置项目的访问路径 --><path>/zzhua</path><!-- 配置tomcat上传war包的路径 --><!--<url>http://127.0.0.1:8080/manager/text</url>--><!-- tomcat的登陆名 --><!--<username>admin</username>--><!-- tomcat的登陆密码--><!--<password>admin</password>--><!-- 设置打包的名称 --><!--<path>/zzhua</path>--></configuration></plugin><!-- 配置jdk的编译版本 --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.7.0</version><configuration><!-- 指定source和target的版本 --><source>1.8</source><target>1.8</target></configuration></plugin></plugins></build></project>
图解
剩下的Facets和Artifacts这两个里面都是空的
idea打war包
5.三大组件
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_3_1.xsd"version="3.1"><!--配置自定义 Servlet--><servlet><servlet-name>userServlet</servlet-name><servlet-class>com.zzhua.servlet.UserServlet</servlet-class><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>userServlet</servlet-name><url-pattern>/user</url-pattern></servlet-mapping><!--配置自定义过滤器 Filter--><filter><filter-name>userFilter1</filter-name><filter-class>com.zzhua.filter.UserFilter1</filter-class></filter><filter-mapping><filter-name>userFilter1</filter-name><url-pattern>/user</url-pattern></filter-mapping><!--配置自定义过滤器 Filter--><filter><filter-name>userFilter2</filter-name><filter-class>com.zzhua.filter.UserFilter2</filter-class></filter><filter-mapping><filter-name>userFilter2</filter-name><url-pattern>/user</url-pattern></filter-mapping><!--配置监听器--><listener><listener-class>com.zzhua.listener.UserListener</listener-class></listener></web-app>
1.Servlet
1.介绍
Servlet是用来处理请求的
HttpServlet继承GenericServlet,GenericServlet继承javax.servlet
2.Servlet接口
public interface Servlet {public void init(ServletConfig config) throws ServletException; // 初始化方法// ...可以实现上面这个方法,拿到tomcat传过来的servletConfig并保存到本servlet对象中, // 然后在下面这个方法中的实现中获取保存的servletConfigpublic ServletConfig getServletConfig(); // 获取servlet配置的方法// 处理请求的方法public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;public String getServletInfo();public void destroy(); // 销毁的方法
}
3.ServletConfig
/*** A servlet configuration object used by a servlet container to pass* information to a servlet during initialization.*/
public interface ServletConfig {/*** Returns the name of this servlet instance. The name may be provided via* server administration, assigned in the web application deployment* descriptor, or for an unregistered (and thus unnamed) servlet instance it* will be the servlet's class name.** @return the name of the servlet instance*/public String getServletName(); //================= 可以获取到servlet的名字/*** Returns a reference to the {@link ServletContext} in which the caller is* executing.** @return a {@link ServletContext} object, used by the caller to interact* with its servlet container* @see ServletContext*/public ServletContext getServletContext(); // ============= 可以获取到ServletContext/*** Returns a <code>String</code> containing the value of the named* initialization parameter, or <code>null</code> if the parameter does not* exist.** @param name* a <code>String</code> specifying the name of the* initialization parameter* @return a <code>String</code> containing the value of the initialization* parameter*/public String getInitParameter(String name); // ============ 根据名字获取配置的初始化参数对应的值/*** Returns the names of the servlet's initialization parameters as an* <code>Enumeration</code> of <code>String</code> objects, or an empty* <code>Enumeration</code> if the servlet has no initialization parameters.** @return an <code>Enumeration</code> of <code>String</code> objects* containing the names of the servlet's initialization parameters*/public Enumeration<String> getInitParameterNames(); // ============ 获取到所有配置的初始化参数的名字
}
4.使用
public class UserServlet extends HttpServlet {@Overridepublic void init() throws ServletException {System.out.println("userServlet...init初始化");}@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// resp.setContentType("text/html;charset=utf-8"); // 或者如下设置,否则会中文乱码resp.setHeader("content-type","text/plain;charset=UTF-8");resp.getWriter().write("Hello, 你好!");}
}
2.Filter
1.介绍
请求被Servlet处理之前,被Filter过滤器拦截,放行之后,请求才会被Servlet处理
如果配置了多个过滤器,那么将会按照顺序依次拦截
2.Filter接口
public interface Filter {// 初始化方法, 可以从filterConfig中获取ServletContextpublic void init(FilterConfig filterConfig) throws ServletException; // 过滤的逻辑, 通过执行chain.do(request,response)才能放行请求public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain ) throws IOException, ServletException;// 销毁的方法public void destroy();
}
3.FilterConfig
/**** A filter configuration object used by a servlet container to pass information* to a filter during initialization.** @see Filter* @since Servlet 2.3*/
public interface FilterConfig {/*** Get the name of the filter.** @return The filter-name of this filter as defined in the deployment* descriptor.*/public String getFilterName(); // ============= 获取到为该filter配置的名字/*** Returns a reference to the {@link ServletContext} in which the caller is* executing.** @return {@link ServletContext} object, used by the caller to interact* with its servlet container** @see ServletContext*/public ServletContext getServletContext(); // ========== 获取到servletContext/*** Returns a <code>String</code> containing the value of the named* initialization parameter, or <code>null</code> if the parameter does not* exist.** @param name* <code>String</code> specifying the name of the initialization* parameter** @return <code>String</code> containing the value of the initialization* parameter*/public String getInitParameter(String name); // ============= 根据名字获取配置的初始化参数对应的值/*** Returns the names of the filter's initialization parameters as an* <code>Enumeration</code> of <code>String</code> objects, or an empty* <code>Enumeration</code> if the filter has no initialization parameters.** @return <code>Enumeration</code> of <code>String</code> objects* containing the names of the filter's initialization parameters*/public Enumeration<String> getInitParameterNames(); // ============ 获取到所有配置的初始化参数的名字}
4.使用
public class UserFilter1 implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("userFilter1 init...自定义过滤器userFilter1初始化");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("userFilter1 doFilter...自定义过滤器userFilter1执行");// 放行, 不执行这一句将会拦截掉请求, 不会执行后面的filter和servlet,责任链模式filterChain.doFilter(servletRequest,servletResponse); }@Overridepublic void destroy() {System.out.println("userFilter1 destroy...自定义过滤器userFilter1销毁");}
}
5.源码分析
public final class ApplicationFilterChain implements FilterChain {/*** Filters. ApplicationFilterConfig封装了匹配当前请求的所有filter* (注意这里并不是所有的filter,匹配的过程是tomcat自己做的)* (并且执行完请求后,这个数据的每个元素都会release掉,即置为null)*/private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];/*** The int which is used to maintain the current position* in the filter chain.*/private int pos = 0; // 索引, 当前执行的filter在上面这个数组中的位置/*** The int which gives the current number of filters in the chain.*/private int n = 0; // 一共配置的filter的数量/*** The servlet instance to be executed by this chain.*/private Servlet servlet = null;@Overridepublic void doFilter(ServletRequest request, ServletResponse response)throws IOException, ServletException {if( Globals.IS_SECURITY_ENABLED ) {final ServletRequest req = request;final ServletResponse res = response;try {java.security.AccessController.doPrivileged(new java.security.PrivilegedExceptionAction<Void>() {@Overridepublic Void run()throws ServletException, IOException {internalDoFilter(req,res);return null;}});} catch( PrivilegedActionException pe) {Exception e = pe.getException();if (e instanceof ServletException)throw (ServletException) e;else if (e instanceof IOException)throw (IOException) e;else if (e instanceof RuntimeException)throw (RuntimeException) e;elsethrow new ServletException(e.getMessage(), e);}} else {internalDoFilter(request,response); // 调用下面这个方法}}private void internalDoFilter(ServletRequest request,ServletResponse response)throws IOException, ServletException {// Call the next filter if there is one// 0 -> 2 ,假设一共有两个Filter// 第一轮: pos = 0, n = 2, 做判断 0 < 2, 好,这个时候pos + 1 所以此时pos = 1// 假设此时的第一个Filter没有调用chain.doFilter(req,resp)方法,那么会执行下面的return;// 就不会执行下面的servlet调用service方法了// 假设第一轮中,调用了chain.doFilter,这个,又回到上面这个方法,接着又调用本方法, 注意此时是同一个FilterChain// 此时 pos = 1, 所以 1 < 2, 依旧成立, 好,这个时候pos + 1 所以此时pos = 2// 假设第二个过滤器如果又没调用chain.doFilter那么filter.doFilter(request, response, this);// 这一句又断了,就直接执行return;// 就不会执行下面的servlet调用service方法了// 第二轮: 假设第二轮调用了,那么又会回到当前的方法2<2不成立, 那么就会执行下面的servlet调用service方法了// 其实chain.doFilter可以这样理解,chain把过滤的具体功能交给filter来实现, 而filter做完自己的过滤功能后,// 因为已经拿到了chain, 那么就有权决定,是否要把执行权交回给chain, 如果不交回, 那么chain就断了。// =========这是责任链模式的另外一种用法========// 思考: 假设其中某一个Filter, 它不是在最后一句调用的filterChain.doFilter// 那么其实也实现了spring mvc的拦截器一样的功能,等servlet处理完了之后, // 然后逆序调用filter的那句话后面的代码,// 特别注意: 这个时候处理完的request和response已经让servlet处理过了, // 所以filter可以继续接着对这个处理完成了的请求中的request和response对象处理// 然后再返回给客户端。// 假设很多个过滤器, 前面几个都通过了,但是被一个给拦住了,那么也会逆序回调过滤器dofilter代码之后面的代码 if (pos < n) { //ApplicationFilterConfig filterConfig = filters[pos++]; // 根据索引找filtertry {Filter filter = filterConfig.getFilter(); // 先获取到filter,如果之前创建过,直接返回,如果没有,// 那么通过反射创建,并回调初始化方法,然后传个东西给// 它。注意这里的filterConfig是// ApplicationFilterConfig。// 下面分析以下ApplicationFilterConfig// ApplicationFilterChain中组合了配置的Filter,并都封装成了ApplicationFilterConfig数组,// 并且ApplicationFilterConfig中封装了我们配置的filter的所有相关信息,创建filter也是由// ApplicationFilterConfig这个对象创建的。所以tomcat读取web.xml中的filter时,创建的并不是// Filter,而是ApplicationFilterConfig,由这个类来对filter的配置信息做一个封装。真正执行的时候// 从ApplicationFilterConfig类中获取filter,如果之前创建过,那么直接返回,如果之前没有创建过,// 那么通过反射创建Filter,创建好后。// 调用Filter的init方法,并且把当前对象this,也就是ApplicationFilterConfig,传进去。因为// ApplicationFilterConfig还实现了FilterCOnfig接口,它里面还封装了Context,而从Context里面// 由能拿到ServletContext。// 所以filter重写的init方法中的filterConfig实际上就是ApplicationFilterconfigif (request.isAsyncSupported() && "false".equalsIgnoreCase(filterConfig.getFilterDef().getAsyncSupported())) {request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);}if( Globals.IS_SECURITY_ENABLED ) {final ServletRequest req = request;final ServletResponse res = response;Principal principal =((HttpServletRequest) req).getUserPrincipal();Object[] args = new Object[]{req, res, this};SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);} else {filter.doFilter(request, response, this);}} catch (IOException | ServletException | RuntimeException e) {throw e;} catch (Throwable e) {e = ExceptionUtils.unwrapInvocationTargetException(e);ExceptionUtils.handleThrowable(e);throw new ServletException(sm.getString("filterChain.filter"), e);}return; // 这个return;语句是关键,如果中间的FilterChain断了的话,// 那么会直接到这里来,下面调用servlet的方法也不会执行了}// We fell off the end of the chain -- call the servlet instancetry {if (ApplicationDispatcher.WRAP_SAME_OBJECT) {lastServicedRequest.set(request);lastServicedResponse.set(response);}if (request.isAsyncSupported() && !servletSupportsAsync) {request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,Boolean.FALSE);}// Use potentially wrapped request from this pointif ((request instanceof HttpServletRequest) &&(response instanceof HttpServletResponse) &&Globals.IS_SECURITY_ENABLED ) {final ServletRequest req = request;final ServletResponse res = response;Principal principal =((HttpServletRequest) req).getUserPrincipal();Object[] args = new Object[]{req, res};SecurityUtil.doAsPrivilege("service",servlet,classTypeUsedInService,args,principal);} else {// 上面的过滤器执行完了,才轮到servlet去处理这个请求// 注意:这里不一定执行完了, 可以看下上面的思考servlet.service(request, response);}} catch (IOException | ServletException | RuntimeException e) {throw e;} catch (Throwable e) {e = ExceptionUtils.unwrapInvocationTargetException(e);ExceptionUtils.handleThrowable(e);throw new ServletException(sm.getString("filterChain.servlet"), e);} finally {if (ApplicationDispatcher.WRAP_SAME_OBJECT) {lastServicedRequest.set(null);lastServicedResponse.set(null);}}}
}
3.监听器
1.介绍
当web应用启动,创建好ServletContext时,会回调配置的监听器的contextInitialized方法,并且将ServletContextEvent对象传递过来。
除了ServletContextListener接口,tomcat还会回调以下Listener(只要把以下listener配置给tomcat,使用xml或注解方式)
- HttpSessionListener、HttpSessionAttributeListener、HttpSessionIdListener、HttpSessionBindingListener
- ServletRequestListener、ServletRequestAttributeListener
- ServletContextListener、ServletContextAttributeListener
2.Listener接口
以ServletContextListener为例
public interface ServletContextListener extends EventListener {/**** Notification that the web application initialization process is starting.* All ServletContextListeners are notified of context initialization before* any filter or servlet in the web application is initialized.* @param sce Information about the ServletContext that was initialized*/// 通知web应用开始初始化,通知所有ServletContextListeners回调contextInitialized方法, // (并且这个回调的contextInitialized方法的执行时机,任何的filter或者servlet之前)// 1.可以从ServletContextEvent事件对象中取出ServletContext // 2.这样又可以从servletContext中获取到配置的全局属性,即context-param配置的// 3.也可以在此处获取到servletContext,然后使用它来注册servlet或者filter(添加返回的Dynamic配置)public void contextInitialized(ServletContextEvent sce);/**** Notification that the servlet context is about to be shut down. All* servlets and filters have been destroy()ed before any* ServletContextListeners are notified of context destruction.* @param sce Information about the ServletContext that was destroyed*/ // 容器销毁, 这个方法被执行, 并且执行时间是在 任何的filter或者servlet执行销毁方法之后// 可以从ServletContextEvent事件对象中取出ServletContext public void contextDestroyed(ServletContextEvent sce);
}
3.ServletContext
public interface ServletContext {String TEMPDIR = "javax.servlet.context.tempdir";String ORDERED_LIBS = "javax.servlet.context.orderedLibs";String getContextPath();ServletContext getContext(String var1);int getMajorVersion();int getMinorVersion();int getEffectiveMajorVersion();int getEffectiveMinorVersion();String getMimeType(String var1);Set<String> getResourcePaths(String var1);URL getResource(String var1) throws MalformedURLException;InputStream getResourceAsStream(String var1);RequestDispatcher getRequestDispatcher(String var1);RequestDispatcher getNamedDispatcher(String var1);/** @deprecated */Servlet getServlet(String var1) throws ServletException;/** @deprecated */Enumeration<Servlet> getServlets();/** @deprecated */Enumeration<String> getServletNames();void log(String var1);/** @deprecated */void log(Exception var1, String var2);void log(String var1, Throwable var2);String getRealPath(String var1);String getServerInfo();// 根据在web.xml配置的<context-param>标签中配置的key,拿配置的value/* <context-param><param-name>name</param-name><param-value>zzhua173</param-value></context-param>*/String getInitParameter(String var1);Enumeration<String> getInitParameterNames();boolean setInitParameter(String var1, String var2);Object getAttribute(String var1);Enumeration<String> getAttributeNames();void setAttribute(String var1, Object var2);void removeAttribute(String var1);String getServletContextName();Dynamic addServlet(String var1, String var2);Dynamic addServlet(String var1, Servlet var2);Dynamic addServlet(String var1, Class<? extends Servlet> var2);<T extends Servlet> T createServlet(Class<T> var1) throws ServletException;ServletRegistration getServletRegistration(String var1);Map<String, ? extends ServletRegistration> getServletRegistrations();javax.servlet.FilterRegistration.Dynamic addFilter(String var1, String var2);javax.servlet.FilterRegistration.Dynamic addFilter(String var1, Filter var2);javax.servlet.FilterRegistration.Dynamic addFilter(String var1, Class<? extends Filter> var2);<T extends Filter> T createFilter(Class<T> var1) throws ServletException;FilterRegistration getFilterRegistration(String var1);Map<String, ? extends FilterRegistration> getFilterRegistrations();SessionCookieConfig getSessionCookieConfig();void setSessionTrackingModes(Set<SessionTrackingMode> var1);Set<SessionTrackingMode> getDefaultSessionTrackingModes();Set<SessionTrackingMode> getEffectiveSessionTrackingModes();void addListener(String var1);<T extends EventListener> void addListener(T var1);void addListener(Class<? extends EventListener> var1);<T extends EventListener> T createListener(Class<T> var1) throws ServletException;JspConfigDescriptor getJspConfigDescriptor();ClassLoader getClassLoader();void declareRoles(String... var1);String getVirtualServerName();
}
4.使用
public class UserListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent servletContextEvent) {ServletContext servletContext = servletContextEvent.getServletContext();servletContext.setAttribute("ctx", servletContext.getContextPath());System.out.println("UserListener contextInitialized...自定义监听器初始化");}@Overridepublic void contextDestroyed(ServletContextEvent servletContextEvent) {System.out.println("UserListener contextDestroyed...自定义监听器销毁");}
}
注解的配置方式
自servlet3.0开始, tomcat支持使用注解的方式代替web.xml配置的方式, 向web容器中注册Servlet,Filter,Listener.
如:@WebServlet,@WebFilter,@WebListener。
只要将继承自Servlet的非抽象类上加上@WebServlet的注解,启动tomcat后这个类将会被自动扫描到,并实例化成servlet来处理请求,同理filter和listener
1.@WebServlet
/*** 自servlet3.0开始, tomcat支持使用注解的方式代替web.xml配置的方式, 向web容器中注册Servlet,Filter,Listener* 只要把这个类打进war包里,tomcat会自动识别到这个类。因此可以猜测:tomcat会对war包里面的class逐个扫描,* 检查每个类上是否有这个注解,如果有, 那么读取注解上的配置(和读取web.xml类似), 并向web容器中注册这个组件。* 有点像SPI机制*/
@WebServlet(urlPatterns = {"/anno"} , // 不能丢“/”initParams = { // 配置初始化参数@WebInitParam(name = "name",value="zzhua"),@WebInitParam(name = "age",value="25")}
)
public class AnnoServlet implements Servlet {private static ServletConfig SERVLET_CONFIG;private static ServletContext SERVLET_CONTEXT = null;public AnnoServlet() {System.out.println(1);}@Overridepublic void init(ServletConfig servletConfig) throws ServletException {Enumeration<String> initParameterNames = servletConfig.getInitParameterNames();while (initParameterNames.hasMoreElements()) {String initParamName = initParameterNames.nextElement();System.out.println(initParamName + " - " + servletConfig.getInitParameter(initParamName));}SERVLET_CONTEXT = servletConfig.getServletContext();AnnoServlet.SERVLET_CONFIG = servletConfig;}@Overridepublic ServletConfig getServletConfig() {return AnnoServlet.SERVLET_CONFIG;}@Overridepublic void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {System.out.println("我拿到了req和res");}@Overridepublic String getServletInfo() {return "annoServlet";}@Overridepublic void destroy() {}
}
2.@WebFilter
public class Demo1Filter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {Enumeration<String> initParameterNames = filterConfig.getInitParameterNames();while (initParameterNames.hasMoreElements()) {String initParamName = initParameterNames.nextElement();System.out.println(initParamName + "-" + filterConfig.getInitParameter(initParamName));}}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("经过demo1过滤器...");}@Overridepublic void destroy() {}
}
3.@WebListener
@WebListener
public class RequestListener implements ServletRequestListener {@Overridepublic void requestDestroyed(ServletRequestEvent sre) {System.out.println("请求对象被销毁");RequestHolder.setRequest(null); // 清除该请求对象}@Overridepublic void requestInitialized(ServletRequestEvent sre) {System.out.println("当前线程: " + Thread.currentThread().getName());HttpServletRequest servletRequest = (HttpServletRequest)sre.getServletRequest();// 请求能这么存的前提是: tomcat是在本线程里面回调的此方法,并且也是在本线程里面处理该请求,证明确实是这样的RequestHolder.setRequest(servletRequest); // 将tomcat当前线程上创建的request对象放入RequestHolder中}
}
ServletContextListener配置tomcat组件方式
在tomcat中,配置ServletContextListener接口的监听器给tomcat(用上面的2种方式,web.xml方式或者@WebListener注解方式),tomcat会在创建完ServletContext时,回调配置的ServletContextListener监听器的contextInitialized方法,并且会回传ServletContextEvent事件对象,该事件对象中包装了ServletContext对象(即可以从contextInitialized方法中回传的ServletContextEvent对象参数中获取到ServletContext)。特别注意:此时,filter和servlet都还未初始化,所以此时我们可以往tomcat中添加listener、filter、servlet,(这里不能添加ServletContextListener接口的监听器,只能添加其它监听器接口的监听器,比如HttpSessionListener、ServletRequestListener等)
@WebListener
public class UserListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent servletContextEvent) {// 从servletContextEvent中获取ServletContextServletContext servletContext = servletContextEvent.getServletContext();// 使用ServletContext监听器注册了一个ServletListenerservletContext.addListener(new ServletRequestListener() {@Overridepublic void requestDestroyed(ServletRequestEvent servletRequestEvent) {ServletRequest servletRequest = servletRequestEvent.getServletRequest();System.out.println("==>请求被销毁");}@Overridepublic void requestInitialized(ServletRequestEvent servletRequestEvent) {System.out.println("==>监听器listener");}});// 使用ServletContext监听器注册了一个HttpSessionListenerservletContext.addListener(new HttpSessionListener() {@Overridepublic void sessionCreated(HttpSessionEvent httpSessionEvent) {System.out.println("session created...");}@Overridepublic void sessionDestroyed(HttpSessionEvent httpSessionEvent) {System.out.println("session destroyed...");}});// ServletContext监听器注册了一个servletServletRegistration.Dynamic dynamicServlet = servletContext.addServlet("anonymousServlet", new HttpServlet() {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().write("==>hello dynamicServlet");}@Overridepublic void init() throws ServletException {System.out.println("init....anonymous servlet");}});dynamicServlet.addMapping("/anonymous");dynamicServlet.setLoadOnStartup(1);// ServletContext监听器注册了一个filterFilterRegistration.Dynamic dynamicFilter = servletContext.addFilter("anonymousFilter", new Filter() {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("==>"+filterConfig);}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("==>拦截");filterChain.doFilter(servletRequest,servletResponse);}@Overridepublic void destroy() {}});dynamicFilter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class),true,"/anonymous/*");System.out.println("UserListener contextInitialized...自定义监听器初始化");}@Overridepublic void contextDestroyed(ServletContextEvent servletContextEvent) {System.out.println("UserListener contextDestroyed...自定义监听器销毁");}
}
servlet 3.0之动态注册
先看下Registration的继承体系
动态注册使用
说实话,对servlet 3动态加载servlet的机制有些失望,本来期望着可以在运行时完成对servlet的注册和销毁,但现状是,只能在webapp启动在初始化时进行完成注册,可能是为了安全考虑吧。
在Servlet3.0中可以动态注册Servlet,Filter,Listener,每个组件注册都提供三个方法,很细心。
下面谈谈动态注册Servlet,但不要希望太高,只能在初始化时进行注册。在运行时为了安全原因,无法完成注册。在初始化情况下的注册Servlet组件有两种方法:
- 1.实现ServletContextListener接口,在contextInitialized方法中完成注册.
- 2.在jar文件中放入实现ServletContainerInitializer接口的初始化器
在ServletContext类中对应注册API为:
/** * 添加Servlet */ public ServletRegistration.Dynamic addServlet(String servletName, String className); public ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet); public ServletRegistration.Dynamic addServlet(String servletName, Class<? extends Servlet> servletClass); /** * 添加Filter */ public FilterRegistration.Dynamic addFilter(String filterName, String className); public FilterRegistration.Dynamic addFilter(String filterName, Filter filter); public FilterRegistration.Dynamic addFilter(String filterName, Class<? extends Filter> filterClass); /** * 添加Listener */ public void addListener(String className); public <T extends EventListener> void addListener(T t); public void addListener(Class<? extends EventListener> listenerClass);
先说在ServletContextListener监听器中完成注册。
public void contextInitialized(ServletContextEvent sce) { ServletContext sc = sce.getServletContext(); // Register Servlet ServletRegistration sr = sc.addServlet("DynamicServlet", "web.servlet.dynamicregistration_war.TestServlet"); sr.setInitParameter("servletInitName", "servletInitValue"); sr.addMapping("/*"); // Register Filter FilterRegistration fr = sc.addFilter("DynamicFilter", "web.servlet.dynamicregistration_war.TestFilter"); fr.setInitParameter("filterInitName", "filterInitValue"); fr.addMappingForServletNames(EnumSet.of(DispatcherType.REQUEST), true, "DynamicServlet"); // Register ServletRequestListener sc.addListener("web.servlet.dynamicregistration_war.TestServletRequestListener");
}
再说说在jar文件中的servlet组件注册,
需要在jar包含META-INF/services/javax.servlet.ServletContainerInitializer文件,文件内容为已经实现ServletContainerInitializer接口的类:
com.learn.servlet3.jardync.CustomServletContainerInitializer
该实现部分代码:
@HandlesTypes({ JarWelcomeServlet.class })
public class CustomServletContainerInitializer implements ServletContainerInitializer { private static final Log log = LogFactory .getLog(CustomServletContainerInitializer.class); private static final String JAR_HELLO_URL = "/jarhello"; public void onStartup(Set<Class<?>> c, ServletContext servletContext) throws ServletException { log.info("CustomServletContainerInitializer is loaded here..."); log.info("now ready to add servlet : " + JarWelcomeServlet.class.getName()); ServletRegistration.Dynamic servlet = servletContext.addServlet( JarWelcomeServlet.class.getSimpleName(), JarWelcomeServlet.class); servlet.addMapping(JAR_HELLO_URL); log.info("now ready to add filter : " + JarWelcomeFilter.class.getName()); FilterRegistration.Dynamic filter = servletContext.addFilter( JarWelcomeFilter.class.getSimpleName(), JarWelcomeFilter.class); EnumSet<DispatcherType> dispatcherTypes = EnumSet .allOf(DispatcherType.class); dispatcherTypes.add(DispatcherType.REQUEST); dispatcherTypes.add(DispatcherType.FORWARD); filter.addMappingForUrlPatterns(dispatcherTypes, true, JAR_HELLO_URL); log.info("now ready to add listener : " + JarWelcomeListener.class.getName()); servletContext.addListener(JarWelcomeListener.class); }
}
其中@HandlesTypes注解表示 CustomServletContainerInitializer 可以处理的类,在 onStartup方法中,可以通过 <Set> c获取得到。
jar文件中不但可以包含需要自定义注册的servlet,也可以包含应用注解的servlet,具体怎么做,视具体环境而定。把处理某类事物的servlet组件打包成jar文件,有利于部署和传输,功能不要了,直接去除掉jar即可,方便至极!
ServletContainerInitializer
SCI规范
规范
-
在web容器启动时为提供给第三方组件机会做一些初始化的工作,例如注册servlet或者filters等,servlet规范中通过ServletContainerInitializer实现此功能。每个框架要使用ServletContainerInitializer就必须在对应的jar包的==META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer==的文件,文件内容指定具体的**ServletContainerInitializer实现类**,那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作
(注意:在springboot中这样做是没有效果的)。
-
伴随着ServletContainerInitializer一起使用的还有**@HandlesTypes**注解,通过HandlesTypes可以将感兴趣的一些类注入到ServletContainerInitializerde的onStartup方法作为参数传入(这个感兴趣的类从所有的类中找,搜索算法由tomcat负责)。
/*** * ServletContainerInitializers (SCIs) are registered via an entry in the* file META-INF/services/javax.servlet.ServletContainerInitializer that must be* included in the JAR file that contains the SCI implementation.* <p>* SCI processing is performed regardless of the setting of metadata-complete.* SCI processing can be controlled per JAR file via fragment ordering. If* absolute ordering is defined, then only the JARs included in the ordering* will be processed for SCIs. To disable SCI processing completely, an empty* absolute ordering may be defined.* <p>* SCIs register an interest in annotations (class, method or field) and/or* types via the {@link javax.servlet.annotation.HandlesTypes} annotation which* is added to the class.** @since Servlet 3.0 3.0开始支持SCIs*/
public interface ServletContainerInitializer {/*** ServletContainerInitializer的实现类上可以添加@HandleTypes,并且在这个注解里写上感兴趣的类,* 则这个类的子类(子接口、子抽象类、子实现类,总之只会向下寻找,并且不包括当前类)都将添加到一个set集合中,* 并将这个set集合传给ServletContainerInitializer的实现类的onStartUp方法作为第一个参数* ServletContext作为第二个参数** Receives notification during startup of a web application of the classes* within the web application that matched the criteria defined via the* {@link javax.servlet.annotation.HandlesTypes} annotation.** @param c The (possibly null) set of classes that met the specified* criteria* @param ctx The ServletContext of the web application in which the* classes were discovered** @throws ServletException If an error occurs*/void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
}
tomcat启动
maven启动
执行顺序
请看【tomcat组件初始化顺序和请求执行顺序】这一节
意义
- 1.我们可以利用ServletContainerInitializer机制,使用完全基于代码的方式配置ServletContainer容器(注册三大组件),取代以前使用web.xml和注解这种两种注册组件的方式
- 2.因为ServletContainerInitializer是在jar包的META-INF/services下的javax.servlet.ServletContainerInitializer文件中的,我们可以添加或者移除这个jar包,从而实现功能的可插拔性(可插拔性指的是对ServletContext的配置的删减)。
- spring-web jar包下有:SpringServletContainerInitializer这个类,它实现了ServletContainerInitializer接口。
6.tomcat组件初始化顺序和请求执行顺序
初始化顺序
请求执行顺序
7.servlet的基本用法
1.请求转发Forward
转发给其它servlet
@WebServlet("/skip")
public class SkipServlet extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 请求转发是服务器内部的行为,req.getRequestDispatcher("forward").forward(req,resp); // 转发给下面这个servlet处理}
}
@WebServlet("/forward")
public class ForwardServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("hello forward");System.out.println("请求转发是服务内部的转发,让另外一个servlet接着处理...");// 这个就是springmvc中InternalResourceViewResolver视图解析器的原理// tomcat对WEB-INF下的jsp(包括其它在内的所有资源)是不能直接访问的,必须要通过请求转发,才能渲染出来被访问到// (但请求转发意味着又会让tomcat决定走哪个Servlet(即:是走jsp呢?还是继续走DispacherServlet呢?)// 如果转发请求的后缀是以jsp结尾,那么优先是根据转发请求的url找对应目录下的jsp处理请求并渲染出来,注意这个// 优先是指在没有配置/*的前提下(springmvc的前端控制器的url-pattern须配置成/而非/*))// 而对其它目录下的jsp试可以直接在浏览器输入url访问的。req.getRequestDispatcher("/WEB-INF/forward.jsp").forward(req,resp);}
}
转发给jsp
@WebServlet("/test01")
public class Test1Servlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {req.getRequestDispatcher("/test1.jsp").forward(req,resp);}
}
此处踩过坑一:在web.xml中配置spring-mvc的前端控制器时(即DispatcherServlet),把url-pattern设置成了/*,这样会导致所有的请求都给该DispatcherServlet处理了,哪怕是调用RequestDispatcher#forward(req,resp)方法转发的请求,也都被该前端控制器处理了,结果就是导致写的jsp根本渲染不出数据(只在浏览器上显示了jsp的源代码)。所以应该配置为"/“,而不是”/*"。显示为jsp源代码的原因就是,springmvc的InternalResourceViewResolver视图解析器组件能拿到这个文件,并且render出来。而并不是将请求转发给tomcat后,tomcat没有把请求交给该jsp(jsp本质上也是个Servlet)处理。所以tomcat在接收到我们需要转发请求的需求时,它把请求继续交给DispatcherServlet处理,但实际理应交给jsp来处理(这样才会走jsp渲染的逻辑),就是因为配置了/*,而把所有的请求都给了DispatcherServlet处理了。也就是说转发请求的时候,又会让tomcat决定此次转发请求将走哪个Servlet(即:是走jsp呢?还是继续走DispacherServlet呢?),并且如果转发请求的url是以jsp结尾,那么在springmvc如果配置的url-pattern是/而不是/*的情况下(如果配置的是/*,那么又会让DispatcherServlet去处理该转发请求),是会优先去找对应的jsp去处理请求并渲染出来。
转发给静态html
@WebServlet("/test02")
public class Test2Servlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req,HttpServletResponse resp)throws ServletException, IOException {req.getRequestDispatcher("/WEB-INF/test002.html").forward(req,resp);}
}
2.重定向Redirect
@WebServlet("/redirect")
public class RedirectServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.sendRedirect(req.getContextPath() + "/redirect.jsp"); // 需要加上contextPath}
}
3.保存当前线程的请求对象
public class RequestHolder {private static final ThreadLocal<HttpServletRequest> requestThreadLocal = new ThreadLocal<>();public static void setRequest(HttpServletRequest request) {requestThreadLocal.set(request);}public static HttpServletRequest getRequest() {return requestThreadLocal.get();}}
@WebListener
public class RequestListener implements ServletRequestListener {@Overridepublic void requestDestroyed(ServletRequestEvent sre) {System.out.println("请求对象被销毁");RequestHolder.setRequest(null); // 清除该请求对象}@Overridepublic void requestInitialized(ServletRequestEvent sre) {System.out.println("当前线程: " + Thread.currentThread().getName());HttpServletRequest servletRequest = (HttpServletRequest)sre.getServletRequest();// 请求能这么存的前提是: tomcat是在本线程里面回调的此方法,并且也是在本线程里面处理该请求,证明确实是这样的RequestHolder.setRequest(servletRequest); // 将tomcat当前线程上创建的request对象放入RequestHolder中}
}
@WebServlet("/request")
public class RequestServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {HttpSession session = req.getSession();System.out.println(session);System.out.println("当前线程: " + Thread.currentThread().getName());System.out.println("拿到的是同一个请求吗? " +( RequestHolder.getRequest() == req)); // true}
}
相关文章:
tomcat的web三大组件Sciidea搭建web/maven的tomcat项目
文章目录 1. web项目的搭建1. 创建web项目2.修改web.xml版本3.添加servlet、jsp依赖4.servlet示例(使用注解)5.配置tomcat6.添加artifact7.部署8.启动tomcat、访问9.打war包10.部署到tomcat 2.maven的项目搭建1.创建项目图解 2.tomcat启动方式图解idea打…...
《SQL赋能人工智能:解锁特征工程的隐秘力量》
在当今的科技发展进程中,人工智能(AI)已经成为推动各领域变革的核心驱动力。而在人工智能的庞大体系里,特征工程占据着举足轻重的地位,它是将原始数据转化为能够让模型有效学习的特征的关键环节。鲜有人深入探讨的是&a…...
SQLmap工具使用
1. sqlmap介绍 sqlmap是一款自动化的SQL注入工具,用于检测和利用web应用程序中的SQL注入漏洞。不需要我们进行手注,当我们输入url地址后,会自动进行注入指令并将payload返回显示。 在kali中自带。在本机中需要下载,在相应的路径…...
Kubernetes集群管理详解:从入门到精通
1. 引言 Kubernetes(简称k8s)作为当今最流行的容器编排平台,已成为云原生应用部署和管理的事实标准。本文将深入探讨k8s集群管理的各个方面,为运维工程师和开发人员提供一个全面的指南。 2. Kubernetes架构概览 在深入具体的管理任务之前,让我们先回顾一下Kubernetes的基本架…...
【含文档+PPT+源码】基于Python的全国景区数据分析以及可视化实现
项目介绍 本课程演示的是一款基于Python的全国景区数据分析以及可视化实现,主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 包含:项目源码、项目文档、数据库脚本、软件工具等所有资料 带你从零开始部署运行本套系统 该…...
鸿蒙开发者高级认证编程题库
题目一:跨设备分布式数据同步 需求描述 开发一个分布式待办事项应用,要求: 手机与平板登录同一华为账号时,自动同步任务列表任一设备修改任务状态(完成/删除),另一设备实时更新任务数据在设备离线时能本地存储,联网后自动同步实现方案 // 1. 定义分布式数据模型 imp…...
【网络安全】安全的网络设计
网络设计是网络安全的基础,一个好的网络设计可以有效的防止攻击者的入侵。在本篇文章中,我们将详细介绍如何设计一个安全的网络,包括网络架构,网络设备,网络策略,以及如何处理网络安全事件。 一、网络架构…...
基于FLask的重庆市造价工程信息数据可视化分析系统
【FLask】基于FLask的重庆市造价工程信息数据可视化分析系统 (完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 在当今快速发展的建筑工程行业中,造价信息的准确性和时效性对于项目决…...
swift-08-属性、汇编分析inout本质
一、Swift中跟实例相关的属性可以分为2大类 1.1 存储属性( Stored Property) 类似于成员变量这个概念 存储在实例的内存中 结构体、类可以定义存储属性 枚举不可以定义存储属性(因为枚举只存储关联值和case) 1.2 计算属性&am…...
Java 大视界 -- Java 大数据在智能医疗远程护理与患者健康管理中的应用与前景(175)
💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…...
大数据技术发展与应用趋势分析
大数据技术发展与应用趋势分析 文章目录 大数据技术发展与应用趋势分析1. 大数据概述2 大数据技术架构2.1 数据采集层2.2 数据存储层2.3 数据处理层2.4 数据分析层 3 大数据发展趋势3.1 AI驱动的分析与自动化3.2 隐私保护分析技术3.3 混合云架构的普及3.4 数据网格架构3.5 量子…...
如何在Ubuntu上安装Dify
如何在Ubuntu上安装Dify 如何在Ubuntu上安装docker 使用apt安装 # Add Dockers official GPG key: sudo apt-get update sudo apt-get install ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg…...
ffmpeg音频分析
对一个16k 单声道音频,生成频谱图 ./ffmpeg -i input.wav -lavfi "showspectrumpics800x400:modecombined:scalelin:gain1.5" spectrum.png...
每天五分钟深度学习框架pytorch:搭建LSTM完成手写字体识别任务?
本文重点 前面我们学习了LSTM的搭建,我们也学习过很多卷积神经网络关于手写字体的识别,本文我们使用LSTM来完成手写字体的识别。 网络模型的搭建 class RNN(nn.Module):def __init__(self,in_dim,hidden_dim,n_layer,n_class):super(RNN,self).__init__()self.n_layer=n_la…...
Maven工具学习使用(七)——Maven属性
内置属性 主要有两个常用的属性${basedir}表示项目的根目录,即包含pom.xml文件的目录;$[version]表示项目版本。 POM属性 使用该类属性引用POM文件中对应元素的值。例如${project.artifactId}就对应了元素的值,常用的POM属性包括: ${project.build.sourceDirectory} 项…...
【Linux网络与网络编程】05.应用层自定义协议序列化和反序列化
前言 本篇博客通过网络计算器的实现来帮助各位理解应用层自定义协议以及序列化和反序列化。 一、认识自定义协议&&序列化和反序列化 我们程序员写的一个个解决我们实际问题,满足我们日常需求的网络程序都是在应用层。前面我们说到:协议是一种…...
搭建K8S-1.23
0、简介 这里只用3台服务器来做一个简单的集群 地址主机名192.168.160.40kuber-master-1192.168.160.41kuber-master-2192.168.160.42kuber-node-1 1、关闭三个服务 (1)防火墙 systemctl stop firewalld (2)Selinux setenf…...
解决Spring Boot Test中的ByteBuddy类缺失问题
目录 解决Spring Boot Test中的ByteBuddy类缺失问题前奏问题描述问题解决第一步:移除ByteBuddy的特定版本号第二步:更新maven-surefire-plugin配置第三步:清理并重新构建项目 结语 解决Spring Boot Test中的ByteBuddy类缺失问题 前奏 今天&…...
npm 项目命名规则
以下是 npm 项目命名规则的详细说明: 一、核心命名规则 必须使用小写字母 名称中不能包含大写字母。原因: 跨平台兼容性(如 Linux 区分大小写,而 Windows 不区分)。避免命令行和 URL 中的大小写冲突(例如包…...
#SVA语法滴水穿石# (012)关于 first_match、throughout、within 的用法
我们今天学习, SystemVerilog 断言(SVA)中 first_match、throughout、within 运算符。 1. first_match 定义与作用 功能:在可能产生 多个匹配结果 的复合序列(如 or 或重复操作符)中,仅选择第…...
基于springboot+vue的二手车交易系统
开发语言:Java框架:springbootJDK版本:JDK1.8服务器:tomcat7数据库:mysql 5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包:…...
洛谷P11999.投入严厉地本地
洛谷P11999.投入严厉地本地 题目 题目解析及思路 题目要求根据两个字符串s和t,反推出一个映射集合f,其中s的每一个长度为k的子串都可以映射成单个字符或空字符 算出最终映射集合有多少个空字符,用全排列函数去搜索所有情况,判断…...
低代码开发平台:飞帆中新增控件、修改他人控件
飞帆是一个自由的控件平台。所有的网页都由控件搭建而成。 在我的资源、我的控件中,点击新增可以新增控件 对于他人的控件,点击复制控件展开,点击复制到我的控件 飞帆中的控件是使用 Vue2 来实现的...
Openstack指南
什么是云计算 概念 云计算是一种基于互联网的计算方式,通过这种方式,共享的软硬件资源和信息,可以按需求提供给计算机和其他设备。用户不需要了解”云“中的基础设施细节,不必具有相应的专业知识,也无需直接控制。云…...
[自制调试工具]构建高效调试利器:Debugger 类详解
一、引言 在软件开发的漫漫征程中,调试就像是一位忠诚的伙伴,时刻陪伴着开发者解决代码里的各类问题。为了能更清晰地了解程序运行时变量的状态,我们常常需要输出各种变量的值。而 Debugger 类就像是一个贴心的调试助手,它能帮我…...
【网络IP】原生IP是什么?如何获取海外原生IP?
一、什么是原生IP 原生IP地址是互联网服务提供商(ISP)直接分配给用户的真实IP地址,无需代理或转发。这类IP的注册国家与IP所在服务器的注册地相符。这种IP地址直接与用户的设备或网络关联,不会被任何中间服务器或代理转发或隐藏。…...
【小沐学Web3D】three.js 加载三维模型(React Three Fiber)
文章目录 1、简介1.1 Three.js1.2 React Three Fiber 2、测试2.1 初始化环境2.2 app.js修改(显示内置立方体)2.3 app.js修改(显示内置球体)2.4 app.js修改(显示自定义立方体)2.5 app.js修改(显示…...
HTTP查询参数示例(XMLHttpRequest查询参数)(带查询参数的HTTP接口示例——以python flask接口为例)flask查询接口
文章目录 HTTP查询参数请求示例接口文档——获取城市列表代码示例效果 带查询参数的HTTP接口示例——以python flask接口为例app.pyREADME.md运行应用API示例客户端示例关键实现说明:运行方法: HTTP查询参数请求示例 接口文档——获取城市列表 代码示例…...
【玩泰山派】1、mac上使用串口连接泰山派
文章目录 前言picocom工具连接泰山派安装picocom工具安装ch340的驱动串口工具接线使用picocom连接泰山派 参考 前言 windows上面有xshell这个好用的工具可以使用串口连接板子,在mac上好像没找到太好的工具,只能使用命令行工具去搞了。 之前查找说mac上…...
【玩泰山派】0、mac utm安装windows10
文章目录 前言安装过程utm安装下载windows ios镜像安装windows系统解决共享文件夹拷贝受限问题 参考 前言 使用mac开发泰山派,但是买的泰山派没有emmc,只有sd卡,要使用瑞芯微的sd卡烧录工具,这个工具好像只支持windows࿰…...
html 给文本两端加虚线自适应
效果图: <div class"separator">文本 </div>.separator {width: 40%;border-style: dashed;display: flex;align-items: center;color: #e2e2e2;font-size: 14px;line-height: 20px;border-color: #e2e2e2;border-width: 0; }.separator::bef…...
Android学习总结之应用启动流程(从点击图标到界面显示)
一、用户交互触发:Launcher 到 AMS 的跨进程通信 1. Launcher 处理点击事件(应用层) 当用户点击手机桌面上的应用图标时,Launcher(桌面应用)首先捕获点击事件。每个图标对应一个启动 Intent(通…...
Vue2与Vue3不同
Vue3 设计思想与核心变化详解 一、Vue3 设计思想与 Vue2 差异对比 响应式系统重构Vue2 实现(基于 Object.defineProperty) // 在 Vue2 中,通过 data 选项返回一个对象,对象中的属性会被 Object.defineProperty 转换为响应式数据…...
《Java实战:素数检测算法优化全解析——从暴力枚举到筛法进阶》
文章目录 摘要一、需求分析二、基础实现代码与问题原始代码(暴力枚举法)问题分析 三、优化版代码与解析优化1:平方根范围剪枝优化2:偶数快速跳过完整优化代码 四、性能对比五、高阶优化:埃拉托斯特尼筛法算法思想代码实…...
解决报错:node:internal/errors:496 ErrorCaptureStackTrace(err);
报错信息 我使用npm init vuelatest创建项目时出现如下报错 node:internal/errors:496 ErrorCaptureStackTrace(err); ^ TypeError [ERR_IMPORT_ASSERTION_TYPE_MISSING]: Module “file:///D:/develop/nodejs/node_cache/_npx/2f7e7bff16d1c534/node_modules/create-vue/loc…...
【Linux笔记】进程管理章节笔记
一、进程与线程的概念 1、进程 1)概念 进程是程序的执行实例,拥有独立的资源(如内存、文件描述符等)。每个进程在创建时会被分配唯一的进程ID,即为PID,也叫进程编号。 2)特点 资源隔离&#…...
使用Webpack搭建React项目:从零开始
🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》、《前端求职突破计划》 🍚 蓝桥云课签约作者、…...
使用Cusor 生成 Figma UI 设计稿
一、开发环境 系统:MacOS 软件版本: Figma(网页或APP版) 注:最好是app版,网页版figma 没有选项 import from manifest app下载地址:Figma Downloads | Web Design App for Desktops & …...
前端 vs 后端:技术分工详解——从用户界面到系统逻辑的全解析
前端(Frontend) 和 后端(Backend) 是软件开发中两个核心概念,分别对应用户直接交互的部分和系统背后的逻辑处理部分。它们共同构成完整的应用程序,但分工不同。 目录 一、前端(Frontend…...
CentOS 7上配置SQL Server链接其他SQL Server服务器
概述 本文介绍在CentOS 7系统上运行的SQL Server如何链接访问其他SQL Server服务器的详细步骤,包括驱动安装、配置和连接测试。 安装必要组件 1. 安装ODBC驱动 # 安装基础ODBC组件 sudo yum install unixODBC unixODBC-devel# 添加Microsoft仓库 curl https://p…...
Scade One - 将MBD技术从少数高安全领域向更广泛的安全嵌入式软件普及
Scade One是继Scade Suite version 6自2008年起发展近20年后的首次主要改进版本。在Scade One发布的同时,Scade团队发布了一系列介绍Scade One的博客。本篇Scade One - Democratizing model-based development是其中的一部分。在后面的内容中,将复述博客…...
第十二章:容器间网络_《凤凰架构:构建可靠的大型分布式系统》
第十二章 容器间网络 一、Linux网络虚拟化基础 1. 网络命名空间(Network Namespace) 隔离网络栈:每个网络命名空间拥有独立的IP地址、路由表、防火墙规则等网络配置。实现方式:通过ip netns命令管理,容器启动时自动创…...
详解七大排序
目录 一.直接插入排序 (1)基本思想 (2)算法步骤 (3)代码实现 (4)算法特性 (5)算法优化 (6)示例演示 二.希尔排序 (…...
第八章 Python基础进阶-数据可视化(终)
此章节练习主要分为:折线图、地图、柱状图,若用户只是学习Python的基础语法知识,可以不看此章节。 主要是讲解第三方包PyEcharts技术,Python数据的可视化操作。 一.json数据格式 json的概念: (1&#x…...
【Hadoop3.1.4】完全分布式集群搭建
一、虚拟机的建立与连接 1.建立虚拟机 详情见【Linux】虚拟机的安装 把上面三个参数改掉 2.连接虚拟机 具体见【Linux】远程连接虚拟机防火墙 二、修改主机名 在Centos7中直接使用root用户执行hostnamectl命令修改,重启(reboot)后永久生…...
NLP简介及其发展历史
自然语言处理(Natural Language Processing,简称NLP)是人工智能和计算机科学领域中的一个重要分支,致力于实现人与计算机之间自然、高效的语言交流。本文将介绍NLP的基本概念以及其发展历史。 一、什么是自然语言处理?…...
Java异步编程中的CompletableFuture介绍、常见错误及最佳实践
一、Future接口的局限性 Java 5引入的Future接口为异步编程提供了基础支持,但其设计存在明显局限性,导致复杂场景下难以满足需求: 阻塞获取结果 必须通过future.get()阻塞线程等待结果,无法实现真正的非阻塞: Executo…...
基于FLask的共享单车需求数据可视化分析系统
【FLask】基于FLask的共享单车需求数据可视化分析系统 (完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 该系统能够整合并处理大量共享单车使用数据,通过直观的可视化手段࿰…...
vue2项目中,多个固定的请求域名 和 通过url动态获取到的ip域名 封装 axios
vue2 使用场景:项目中,有固定的请求域名,而有某些接口是其他域名 /utils/request.js 固定请求域名 import axios from axios import Vue from vuelet baseURL switch (window.location.hostname) {case localhost: // 本地case 127.0.0.1…...
【嵌入式学习3】基于python的tcp客户端、服务器
目录 1、tcp客户端 2、tcp服务器 3、服务器多次连接客户端、多次接收信息 1、tcp客户端 """ tcp:客户端 1. 导入socket模块 2. 创建socket套接字 3. 建立tcp连接(和服务端建立连接) 4. 开始发送数据(到服务端) 5. 关闭套接字 """ import soc…...