外置tomcat
首先我们看spring的官网的springmvc部分。
https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-servlet
官网推荐使用这种方式启动。那么我们也这样启动。
首先我们建立一个项目。不指定父项目。添加spring和springmvc这两个maven。
<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.mymvc</groupId>
<artifactId>mvctest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
</dependencies>
</project>
package com.test;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.springframework.web.WebApplicationInitializer;
public class MyWebApplicationInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext servletContext) throws ServletException {
}
}
这样能发现会有报错。
这里是ServletContext找不到,所以我们需要引入servlet的包。
<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.mymvc</groupId>
<artifactId>mvctest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
引入了servlet的包就解决掉这个错误了。
把官网推荐的配置代码复制粘贴上去。
package com.test;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
public class MyWebApplicationInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext servletContext) throws ServletException {
// Load Spring web application configuration
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
ac.register(AppConfig.class);
ac.refresh();
System.out.println("-----------test--------------");
// Create and register the DispatcherServlet
DispatcherServlet servlet = new DispatcherServlet(ac);
ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/app/*");
}
}
使用tomcat启动流程。
这里打印出了test,说明这个已经MyWebApplicationInitializer这个类是起作用了的。
现在我们就能写一个controller了。这里为了方便,我把MyWebApplicationInitializer中的registration.addMapping改为registration.addMapping("/");。即
package com.test;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
public class MyWebApplicationInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext servletContext) throws ServletException {
// Load Spring web application configuration
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
ac.register(AppConfig.class);
ac.refresh();
System.out.println("-----------test--------------");
// Create and register the DispatcherServlet
DispatcherServlet servlet = new DispatcherServlet(ac);
ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/");
}
}
写一个controller。
package com.test;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class TestController {
@RequestMapping("/index")
public String test() {
System.out.println("---------controller----------");
return "index";
}
}
这里启动直接访问是访问不了的。因为没有加视图解析器。
首先在pom中添加tomcat模块。主要是tomcat-embed-core。
<!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-core -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>9.0.33</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-jasper -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>9.0.33</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-el -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-el</artifactId>
<version>9.0.33</version>
</dependency>
然后我们需要配置视图解析器。
一共有3种方式:xml,@bean方式,接口方式。xml方式就不写了,说一下另外2种方式。
视图解析器有多个:
我们选一个合适的使用就可以了。
@Bean方式
我们可以在@Configuration中,使用@Bean的方式去注册一个视图解析器。
package com.test;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration
@ComponentScan("com.test")
public class AppConfig {
@Bean
public InternalResourceViewResolver internalResourceViewResolver() {
InternalResourceViewResolver internalResourceViewResolver=new InternalResourceViewResolver();
internalResourceViewResolver.setPrefix("/");
internalResourceViewResolver.setSuffix(".jsp");
return internalResourceViewResolver;
}
}
启动项目,访问http://localhost:8080/mvc/index。
证明访问进来了。TestController已经生效。
使用接口的方式:
package com.test;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@ComponentScan("com.test")
public class AppConfig implements WebMvcConfigurer {
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/",".jsp");
}
// @Bean
// public InternalResourceViewResolver internalResourceViewResolver() {
// InternalResourceViewResolver internalResourceViewResolver=new InternalResourceViewResolver();
// internalResourceViewResolver.setPrefix("/");
// internalResourceViewResolver.setSuffix(".jsp");
// return internalResourceViewResolver;
// }
}
由于使用的UrlBasedViewResolverRegistration的视图解析器,与InternalResourceViewResolver不一样,UrlBasedViewResolverRegistration会跳转到另外一个url,即再次访问http://localhost:8080/mvc/index,造成循环出错。而InternalResourceViewResolver直接使用的静态资源。所以UrlBasedViewResolverRegistration访问会出错,但是后台依然会打印。这里就不改错误了,知道原因即可。有兴趣的自己可以弄一下,这里贴下错误重点。
这里有一个问题,就是WebApplicationInitializer并没有做任何的操作,程序就自动扫描到了我们写的代码,这个是不是很神奇。
我们通过debug调试模式,发现以下调用链。
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
这里就是循环了所有的WebApplicationInitializer,而initializers是怎么得到的呢?
在这个类上我们能看到以下东西:
意思就是只要实现了ServletContainerInitializer这个接口,就能启动,如果在类上加了@HandlesTypes注解,这个注解中的接口的实现就会被扫描到。但是具体是怎么启动的呢?这个是需要配置的。
这个配置文件代表的意思是:在这个配置文件上写上对应的类,就能启动这个类,而当前的@HandlesTypes是WebApplicationInitializer接口,我们又实现了WebApplicationInitializer,就能被扫面到,这样就拿到了我们自己的MyWebApplicationInitializer这个类。如果还不相信我们可以模拟一个。
首先我们也实现这个接口。
package com.test.myinit;
import java.util.Set;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
public class Myinit implements ServletContainerInitializer {
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
System.out.println("--------Init-----------");
}
}
启动程序:
可以发现我们没有打印init,说明没有被执行到。
接下来我们把配置文件配置上,并且配置上我们的类:
在启动:
这里执行了init,说明方法执行了,证明前面的说法是没有错的。目前是执行到了这个地方了的。
接下来我们再弄一个@HandlesTypes的实现类。
修改Myinit:
package com.test.myinit;
import java.util.Set;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes;
@HandlesTypes(Test.class)
public class Myinit implements ServletContainerInitializer {
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
System.out.println(c);
System.out.println("--------Init-----------");
}
}
新加接口和一个实现类:
package com.test.myinit;
public interface Test {
}
package com.test.myinit;
public class MyTest implements Test {
}
再次启动:
这里就看到它找到了实现类了。证明完毕。
内嵌tomcat
前面说了这么多,我们的tomcat是外部启动访问的,而像springboot这种是内嵌了一个tomcat,这种方式实现很简单。
我在这里有个问题,前面使用的是tomcat9.0.33,不知道为啥启动不了端口,后经过排查改成了8.5.42就行,所以这里我们将pom的tomcat版本改下:
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mymvc</groupId>
<artifactId>mvc</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>mvc Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.0</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-core -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>8.5.42</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-jasper -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>8.5.42</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-el -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-el</artifactId>
<version>8.5.42</version>
</dependency>
</dependencies>
<build>
<finalName>mvc</finalName>
</build>
</project>
启动内置的tomcat,因为这里是内置的,所以如果只是启动的话,就跟前面没有任何关联,因此需要告诉tomcat你的源码在哪里。
package custom;
import java.io.File;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.WebResourceRoot;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.webresources.DirResourceSet;
import org.apache.catalina.webresources.StandardRoot;
public class CustomSpringBoot {
public static void run() {
Tomcat tomcat = new Tomcat();
String sourcePath = CustomSpringBoot.class.getResource("/").getPath();
tomcat.setPort(8080);
//告诉tomcat源码在哪里
StandardContext ctx = (StandardContext) tomcat.addWebapp("/",new File("src/main/resources").getAbsolutePath());
System.out.println("ctx:"+ctx);
WebResourceRoot resources = new StandardRoot(ctx);
resources.addPreResources(new DirResourceSet(resources, "/", sourcePath, "/"));
ctx.setResources(resources);
try {
tomcat.start();
tomcat.getServer().await();
} catch (LifecycleException e) {
e.printStackTrace();
}
System.out.println("started tomcat!!!");
}
}
这里写一个自己的servlet:
package custom;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取当前路径
String basepath = MyServlet.class.getResource("/").getPath();
String requestURI = req.getRequestURI();
String path=basepath+requestURI;
System.out.println("basepath:"+basepath);
System.out.println("requestURI:"+requestURI);
resp.setContentType("text/html");
resp.getWriter().write("aaa");
}
}
修改Myinit.java
package com.test.myinit;
import java.util.Set;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.servlet.annotation.HandlesTypes;
import custom.MyServlet;
@HandlesTypes(Test.class)
public class Myinit implements ServletContainerInitializer {
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
//启动tomcat,然后配置我们的Servlet
System.out.println("--------Myinit----------");
ServletRegistration.Dynamic addServlet = ctx.addServlet("XQ", new MyServlet());
addServlet.setLoadOnStartup(1);
addServlet.addMapping("/");
}
}
最后建立一个测试类:
package custom;
public class Test {
public static void main(String[] args) {
CustomSpringBoot.run();
}
}
启动访问:
这里说明内置的tomcat生效了。而且启动也初始化了。
最后再说一下怎么访问到资源文件的。
我们通过MyServlet.class.getResource("/").getPath()得到项目的绝对路径,通过req.getRequestURI()得到访问的路径。然后加在一起就是项目中要访问文件位置的绝对路径。这样就达到了访问的情况。
package custom;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取当前路径
//class所在目录
String basepath = MyServlet.class.getResource("/").getPath();
//访问的uri
String requestURI = req.getRequestURI();
String path=basepath+requestURI;
System.out.println("basepath:"+basepath);
System.out.println("requestURI:"+requestURI);
System.out.println("path:"+path);
File file= new File(path);
InputStream inputStream = new FileInputStream(file);
byte[] bytes = new byte[2048];
inputStream.read(bytes);
inputStream.close();
String str = new String(bytes);
System.out.println(str);
resp.setContentType("text/html");
resp.getWriter().write(str);
}
}
package com.test;
/**
* 工具类
*/
import javax.servlet.http.HttpServletResponse;
import java.io.*;
public class FileUtils {
public static final int DEFAULT_SPEED = 1024 * 1024;
/**
* 将读取到的字节利用写流写出去
* @param is 读流
* @param os 写流
* @param speed 写出,读入速度
* @return boolean
*/
public static boolean writeFile(InputStream is, OutputStream os, int speed) {
boolean success = false;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(is);
bos = new BufferedOutputStream(os);
int fileSize = is.available();
long written = 0;
byte bytes[] = new byte[speed];
while (written < fileSize) {
if (written + speed > fileSize) {
speed = (int) (fileSize - written);
bytes = new byte[speed];
}
bis.read(bytes);
bos.write(bytes);
bos.flush();
written += speed;
}
success = true;
} catch (IOException e) {
e.printStackTrace();
}
try {
bis.close();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
return success;
}
/**
* @see FileUtils#writeFile(InputStream, OutputStream, int)
* @param file 被读取的文件
* @param os 写出流
* @return boolean
* @throws IOException
*/
public static boolean writeFile(File file, OutputStream os) throws IOException {
return writeFile(new FileInputStream(file), os, DEFAULT_SPEED);
}
public static String getSuffix(String path) {
int index = path.lastIndexOf(".");
return index != -1 ? path.substring(index) : "";
}
/**
* 将文件read拷贝到write
* @param read 被读取的文件
* @param write 指向文件
* @return boolean
* @throws IOException
*/
public static boolean copyFile(File read, File write) throws IOException {
return writeFile(new FileInputStream(read), new FileOutputStream(write), DEFAULT_SPEED);
}
// public static boolean copyFile(MultipartFile read, File write) throws IOException {
// return writeFile(read.getInputStream(), new FileOutputStream(write), DEFAULT_SPEED);
// }
public static void downloadFile(InputStream is, HttpServletResponse response) throws IOException {
FileUtils.writeFile(is, response.getOutputStream(), DEFAULT_SPEED);
}
public static void downloadFile(File file, HttpServletResponse response) throws IOException {
String fileName = file.getName();
response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes("GBK"), "ISO8859_1"));
FileUtils.writeFile(file, response.getOutputStream());
}
public static boolean createFile(String path,String content){
File myFilePath = new File(path);
try {
if (!myFilePath.exists()) {
myFilePath.createNewFile();
}
FileWriter resultFile = new FileWriter(myFilePath);
PrintWriter myFile = new PrintWriter(resultFile);
myFile.println(content);
myFile.close();
resultFile.close();
return true;
}
catch (Exception e) {
System.out.println("新建文件操作出错");
return false;
}
}
public static boolean deleteFile(String path){
File myDelFile = new File(path);
try {
myDelFile.delete();
return true;
}
catch (Exception e) {
System.out.println("删除文件操作出错");
return false;
}
}
public static boolean updateFileName(String filepath,String file2path){
File toBeRenamed = new File(filepath);
if (!toBeRenamed.exists() || toBeRenamed.isDirectory()) {
System.out.println("File does not exist: "+filepath);
return false;
}
File newFile = new File(file2path);
if (toBeRenamed.renameTo(newFile)) {
return true;
} else {
return false;
}
}
}
这样就访问到了静态资源。就类似于springboot的静态访问方式一样。
项目代码:
https://download.csdn.net/download/qq_37822914/12319683