SpringBoot相关知识
springboot的启动类的@SpringBootApplication, 控制器的@Controller, @RestController, 还有与配置相关的一系列注解.
查找XML文档与配置<bean>元素这两个工作同样可以使用注解来完成. 使用@Component, @Service, 或者@Configuration注解来修饰一个java类, 这些java类会被Spring自动检测并注册到容器中. 在类里面使用@Bean注解修饰的方法, 会被作为一个bean存放到Spring容器中.
@Autowired默认按类型注入, 找不到就按名称注入.
还可以使用构造器注入
InjectBean myBean;
@Autowired
public InjectApp(InjectBean myBean2){
this.myBean = myBean2;
}
@Resource默认按名称注入, 找不到就按类型注入
不能修饰构造器
@Primary注解 注入
如果容器中配置两个相同类型的bean, 我们在想要用的Bean上添加@Primary注解, 我们再使用@Autowired自动注入, 就不会报错了, 就会自动注入带@Primary的bean 了. 如果有多个bean都带了@Primary, 就会报异常.
例子
User.java
package cn.lacknb.beans;
public class User {
private String username;
public User(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
BeanConfig.java
package cn.lacknb.beans;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
@Component
public class BeanConfig {
@Bean
public User getUser(){
return new User("ccc");
}
@Bean
@Primary
public User getUser2(){
return new User("bbb");
}
}
测试类
@Autowired
private User user;
@Test
public void test03(){
System.out.println(user.getUsername());
// 这里会输出 bbb
}
在配置Bean的时候, 可以指定bean的作用域, 一般的bean可以配置爱为单例(singleton)或非单例(prototype). 配置为singleton的话, Spring的bean工厂都会只返回同一个bean实例, 而配置prototype, 则每次都会创建一个新的实例
package cn.lacknb.test;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
@Service
//@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public class ServiceTest {
public void Hello(){
System.out.println("Hello world !!");
}
}
加入依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
AopService.java
package cn.lacknb.aop;
import org.springframework.stereotype.Component;
@Component
public class AopService {
public void saleService(){
System.out.println("要代理的销售业务方法");
}
}
ProxyService.java
package cn.lacknb.aop;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class ProxyService {
@Before("execution(* cn.lacknb.aop.AopService.saleService(..))")
public void before(){
System.out.println("业务方法调用前");
}
@After("execution(* cn.lacknb.aop.AopService.saleService(..))")
public void after(){
System.out.println("业务方法调用后");
}
}
@Autowired
private AopService aopService;
@Test
public void test02(){
System.out.println(aopService.getClass());
aopService.saleService();
}
根据结果可知, 我们的业务方法已经被代理, 因此在方法调用前和调用后, 都会执行通知到的方法, 输出的代理类为经过cglib处理的类. 在springboot2.0中, 默认情况下, 不管是代理接口还是类, 都使用cglib代理. 我们将spring.aop.proxy-target-class配置为false, 这样在代理接口时, 会使用jdk 动态代理. **据本人测试, 无论false或者true, 输出的都是cglib处理的类. **
ComponentScan注解主要用于检测使用@Component装饰的组件, 并把它们注册到Spring容器中. 除了使用@Component修饰的组件外, 还有间接使用@Component的组件(如@Service, @Repository, @Controller, 或自定义注解), 都会被检测到,. 在使用Springboot时, 我们一般很少接触@ComponentScan注解, 但实际上每个应用都使用它, 它只是被放到@SpringBootApplication里面了. 在使用ComponentScan注解时, 要注意过滤器.
@Primary注解, 当存在多个同类型的bean时, 可以指定优先注入的bean, 如果想对bean的注入选择做进一步的控制, 则可以使用限定注解. 限定注解可以与特定的参数关联起来, 缩小类型的匹配范围, 最后一个选择符合条件的bean来注入.
例子:
需要到上面的user类
QuaConfig.java
package cn.lacknb.beans;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class QuaConfig {
@Bean
public User quaBeanA(){
return new User("quaBeanA");
}
@Bean
public User quaBeanB(){
return new User("quaBeanB");
}
}
创建测试类
@Autowired
@Qualifier("quaBeanB")
private User user;
/*
* 测试 限定注解
* */
@Test
public void test04(){
System.out.println(user.getUsername()); // 输出quaBeanB
}
当存在两个相同类型的注解时, 可以根据bean的名称来指定注入的bean, 如果需要根据特定的属性来指定注入的bean, 则可以自定义限定注解.
例子:
使用上面的User实体类
创建自定义注解TestQualifer.java
package cn.lacknb.beans;
import org.springframework.beans.factory.annotation.Qualifier;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface TestQualifer {
String type();
}
创建CustomConfig.java
package cn.lacknb.beans;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/*
* 首先先定义一个TestQualifer注解, 该注解使用了@Qualifer修饰, 并且需要设置type属性
* 在配置bean的时候, 需要为相应的bean设置不同的类型
* spring容器提供了不同类型的user bean, 并使用@TestQualifer为它们进行了标识.
* 在使用这些bean时, 同样使用@TestQualifer并指定type属性, 就可以拿回相应的bean
* */
@Configuration
public class CustomConfig {
@Bean
@TestQualifer(type = "jerry")
public User userBean(){
return new User("and dog");
}
@Bean
@TestQualifer(type = "tom")
public User catBean(){
return new User("cat");
}
}
创建测试类
/*
* 测试自定义限定注解
* */
@Autowired
@TestQualifer(type = "jerry")
private User user;
@Test
public void test05(){
System.out.println(user.getUsername()); // 输出 and dog.
}
前面介绍了, Scope注解主要用于配置爱bean在容器中的生命周期, 常被配置为singleton, 和 prototype (多态)的, 在web环境下, 还可以配置为request, session等值. 表示Spring容器会为一次请求或一个会话分配一个bean的实例. 如果对bean的生命周期有特殊的需求, 则可以考虑使用自定义的scope, 自己写代码来定义bean的生命周期.
需求: 一个bean需要被使用4次之后, 就需要获取新的bean实例, 针对这样的需求, 可编写一个自定义的scope
创建MyScope.java
package cn.lacknb.util;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
import java.util.HashMap;
import java.util.Map;
public class MyScope implements Scope {
/*
记录使用bean的使用次数
*/
private Map<String, Integer> beanCounts = new HashMap<String, Integer>();
/*
* 保存实例
* */
private Map<String, Object> beans = new HashMap<String, Object>();
@Override
public Object get(String s, ObjectFactory<?> objectFactory) {
if (beanCounts.get(s) == null){
beanCounts.put(s, 0);
}
// 第一次使用, 放到实例的Map中
Integer beancount = beanCounts.get(s);
if (beancount == 0){
Object newObject = objectFactory.getObject();
beans.put(s, newObject);
}
// 获取对象
Object bean = beans.get(s);
// 计数器加1
Integer newBeanCount = beancount + 1;
// 使用次数超过4次了, 设置为0
if (newBeanCount >= 4){
newBeanCount = 0;
}
//设置新的次数
beanCounts.put(s, newBeanCount);
// 返回实例
return bean;
}
@Override
public Object remove(String s) {
return null;
}
@Override
public void registerDestructionCallback(String s, Runnable runnable) {
}
@Override
public Object resolveContextualObject(String s) {
return null;
}
@Override
public String getConversationId() {
return null;
}
}
创建ScopeConfig.java
package cn.lacknb.util;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.CustomScopeConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import javax.annotation.PostConstruct;
@Configuration
public class ScopeConfig {
/*
* @PostConstruct注解 用于在依赖关系注入完成之后需要执行的方法上
*
*
* */
@Autowired
BeanFactory factory;
@PostConstruct
public void customScopeConfigurer(){
// ScopeConfig初始化后执行本方法, 创建MyScope的实例
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
configurer.addScope("four", new MyScope());
configurer.postProcessBeanFactory((ConfigurableListableBeanFactory) factory);
}
@Bean
@Scope(scopeName = "four")
public ScopeBean beanA(){
return new ScopeBean();
}
}
这里的@PostConstruct注解 用于在依赖关系注入完成之后需要执行的方法上, 以执行任何初始化. 此方法必须在将类放入服务之前调用, 支持依赖关系注入的所有类都必须支持此注释. 即使类中没有请求注入任何资源, 用PostConstruct注释的方法也必须被调用. 只有一个方法可以用此进行注释. 应用PostConstruct注释的方法必须遵守以下所有标准:
ScopeBean.java
package cn.lacknb.util;
public class ScopeBean {
public ScopeBean() {
System.out.println("ScopeBean初始化....");
}
}
测试类
/*
* 测试一个bean实例使用4次
* */
@Autowired
ApplicationContext context;
@Test
public void test06(){
for (int i = 0; i < 7; i++) {
System.out.println(context.getBean("beanA"));
}
}
输出结果
ScopeBean初始化....
cn.lacknb.util.ScopeBean@3af4e0bf
cn.lacknb.util.ScopeBean@3af4e0bf
cn.lacknb.util.ScopeBean@3af4e0bf
cn.lacknb.util.ScopeBean@3af4e0bf
ScopeBean初始化....
cn.lacknb.util.ScopeBean@245a26e1
cn.lacknb.util.ScopeBean@245a26e1
cn.lacknb.util.ScopeBean@245a26e1
@ Controller, @RequestMapping, @GetMapping等.
@RestController注解的目的是为了让我们更加简便地使用@Controller和@ResponseBody这两个注解, 查看@RestController源码就可以知道, 这个注解本身就使用了@Controller与@ResponseBody来修饰. ResponseBody注解可以修饰控制方法, 方法的返回值将会被写到HTTP响应体中, 所返回的内容, 将不会放到模型中, 也不会被解释为视图的名称.
@RequestAttribute, @SessionAttribute, @SessionAttributes等注解也较为常用.
前面的@PathVariable注解主要是用来获取单一的URI参数, 如果通过URI来传输一些复杂的参数, 则可以考虑使用@MatrixVariable注解. 使用时需要遵守一定的规范, 参数的名称与值使用key-value形式, 多个参数之间使用(;)号隔开, 如果一个参数拥有多个值, 则值与值之间用逗号隔开. 这种URI的参数表现形式, 成为矩阵变量.
/car/{id};color=red;year=2019
该URI传入了id参数, 并且传入了color与year参数, 值分别为red与2019. 对于上面的URI, 如何获取呢? 例子:
首先开启矩阵变量的支持
package cn.lacknb.controller;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
@Configuration
public class WebConfig extends WebMvcConfigurationSupport {
@Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
final RequestMappingHandlerMapping requestMappingHandlerMapping = super.requestMappingHandlerMapping();
/*
* 将 RequestMappingHandlerMapping的RemoveSemicolonContent的属性设置为false, 即可开启对矩阵变量的支持
* */
requestMappingHandlerMapping.setRemoveSemicolonContent(false);
return requestMappingHandlerMapping;
}
}
控制器
@RequestMapping("/car/{id}")
@ResponseBody
public String test03(@PathVariable Integer id, @MatrixVariable String color, @MatrixVariable String year){
System.out.println(id);
System.out.println(color);
System.out.println(year);
return "test";
}
测试
url为: http://localhost:8080/car/11;color=red;year=2019
控制台输出id color和year的值.
fileUpload.html
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-4.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://localhost:8080/file/upload" method="post" enctype="multipart/form-data">
上传文件: <input type="file" name="file" />
<input type="submit" value="上传">
</form>
</body>
</html>
application.yml
spring:
thymeleaf:
cache: false
mode: HTML
servlet:
multipart:
# 上传文件总的最大值
max-file-size: 10MB
# 单个文件的最大值
max-request-size: 10MB
控制器
@RequestMapping("/file/upload")
public String test04(@RequestParam("file") MultipartFile file, Model model) throws IOException {
String fileName = UUID.randomUUID() + file.getOriginalFilename();
String path = "/media/nianshao/nie475/java项目/files";
File filePath = new File(path);
if (!file.isEmpty()) {
if (!filePath.exists()) {
filePath.mkdirs();
}
File targetFile = new File(path, fileName);
file.transferTo(targetFile);
model.addAttribute("aaa", "上传成功");
}else{
model.addAttribute("aaa", "文件为空, 重新上传");
}
return "test01";
}
创建实体类
package cn.lacknb.beans;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
public class MyFile {
private List<MultipartFile> files;
public List<MultipartFile> getFiles() {
return files;
}
public void setFiles(List<MultipartFile> files) {
this.files = files;
}
}
upload.html
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-4.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://localhost:8080/files/many" method="post" enctype="multipart/form-data">
上传文件: <input type="file" name="files" /> <br>
上传文件: <input type="file" name="files" /> <br>
上传文件: <input type="file" name="files" /> <br>
上传文件: <input type="file" name="files" /> <br>
上传文件: <input type="file" name="files" /> <br>
上传文件: <input type="file" name="files" /> <br>
<input type="submit" value="上传">
</form>
</body>
</html>
控制器
/*
* 多文件上传
* */
@RequestMapping("/files/many")
public String test05(@ModelAttribute MyFile files, Model model) throws IOException {
String path = "/media/nianshao/nie475/java项目/files";
File filePath = new File(path);
if (!filePath.exists()){
filePath.mkdirs();
}
List<MultipartFile> files11 = files.getFiles();
int count = 0;
for (int i = 0; i < files11.size(); i++) {
if (!files11.get(i).isEmpty()) {
String fileName = UUID.randomUUID() + files11.get(i).getOriginalFilename();
File targetFile = new File(filePath, fileName);
files11.get(i).transferTo(targetFile);
count++;
}
}
model.addAttribute("aaa", "成功上传" + count + "个文件");
if (count == 0){
model.addAttribute("aaa", "上传的文件为空");
}
return "test01";
}
SpringBoot提供了自动配置, 一些内置的或者常用的组件Spring已经帮我们配置好了, 我们在使用这些组件的时候, 加入相关依赖就可以了, 并不需要额外进行配置, 从而减少了搭建项目的工作量. 自动配置是否执行, 取决于条件注解.
当类路径中存在或不存在某个类时, 再决定是否执行自动配置. 存在该类则使用@ConditionalOnClass, 不存在则使用@ConditionalOnMissingClass, 它们都可以修饰类或者方法. 这两个注解通过配置value属性来指定一个类, 这个类有可能并不存在于实际的运行环境中, 也可以使用name属性来指定权限定类名. 假设项目中存在MySQL的驱动类, 则会启用某个配置类, 此时则可以使用@ConditionalOnClass.
MyBean.java
package cn.lacknb.bean;
public class MyBean {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
OnClassConfig.java
package cn.lacknb.config;
import cn.lacknb.bean.MyBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ConditionalOnClass(name = "com.mysql.jdbc.Driver")
@Configuration
public class OnClassConfig {
@Bean
public MyBean myBean(){
return new MyBean();
}
}
当项目有mysql的依赖时, 就会创建 MyBean的实例
测试类
package cn.lacknb.contoller;
import cn.lacknb.bean.MyBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
public class OnClassController {
@Autowired
ApplicationContext context;
@RequestMapping("/bean")
public String getBean(){
Map<String, MyBean> beans = context.getBeansOfType(MyBean.class);
System.out.println(beans.size());
return "success";
}
}
代码中的getBean方法会到容器中获取MyBean类型的bean, 并以Map形式返回, 最后输出Map的size. 在pom.xml文件中, 加入MySql依赖, 测试运行, 控制台输出为1, 将mysql的依赖注释掉, 则输出为0. 使用@ConditionalOnMissingClass则有相反的效果.
REST是英文 Representational State Transfer 的缩写, 一般翻译为 "表述性状态转移". REST本身只是分布式系统中的一种架构风格, 并不是某种标准或者规范. 而是一种 轻量级的基于HTTP协议的Web Service风格.
加入spring-boot-starter-web依赖,
package cn.lacknb.REST;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
/*
* 发布与调用REST服务
* 一定要加@RestController注解
* 浏览器输入 http://localhost:8080/person/name 返回json数据
* 如果不使用springboot, 估计你还要为寻找依赖包而疲于奔命
* */
@SpringBootApplication
@RestController
public class RESTexample {
public static void main(String[] args) {
SpringApplication.run(RESTexample.class, args);
}
/*
* MediaType.APPLICATION_JSON_VALUE的值为 application/json 将生产数据的类型
*
* */
@GetMapping(value = "/person/{name}", produces = MediaType.APPLICATION_JSON_VALUE)
public Person person(@PathVariable String name){
Person person = new Person();
person.setName(name);
person.setAge(23);
return person;
}
static class Person{
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
}
package cn.lacknb.REST;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class MyService {
@Autowired
private RestTemplateBuilder restTemplateBuilder;
public RestTemplate restTemplate(){
return restTemplateBuilder.rootUri("http://localhost:8080").build();
}
/*
* 使用RestTemplateBuilder创建template
* */
public RESTexample.Person useBuild(){
RESTexample.Person p = restTemplate().getForObject("/person/aynu", RESTexample.Person.class);
return p;
}
}
测试类
package cn.lacknb.REST;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = RESTexample.class)
public class MyServiceTest {
@Autowired
private MyService myService;
@Test
public void test(){
RESTexample.Person person = myService.useBuild();
System.out.println(person.getName() + "===" + person.getAge());
}
}
Feign是github上的一个开源项目, 其目的就是为了简化web service客户端的开发. 加入依赖
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>LATEST</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-gson</artifactId>
<version>RELEASE</version>
</dependency>
package cn.lacknb.feign;
import cn.lacknb.REST.RESTexample;
import feign.Param;
import feign.RequestLine;
/*
* 在接口中使用了@RequestLine和@Param注解, 这两个注解是Feign的注解. 使用注解修饰后
* getPerson方法被调用, 然后使用HTTP的get方法向 /person/name, 服务发送请求
* */
public interface PersonClient {
@RequestLine("GET /person/{name}")
RESTexample.Person getPerson(@Param("name") String name);
}
package cn.lacknb.feign;
import cn.lacknb.REST.RESTexample;
import feign.Feign;
import feign.gson.GsonDecoder;
public class FeignMain {
public static void main(String[] args) {
// 调用接口
PersonClient personClient = Feign.builder().decoder(new GsonDecoder()).target(PersonClient.class, "http:" +
"//localhost:8080/");
RESTexample.Person p = personClient.getPerson("aynu");
System.out.println(p.getName() + "****" + p.getAge());
}
}
无论是创建servlet, listener 还是filter, Springboot启动类都需要加 注解@ServletComponentScan("需要扫描的包")
package cn.lacknb.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@SpringBootApplication(scanBasePackages = "cn.lacknb")
@ServletComponentScan("cn.lacknb.servlet")
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
这里使用注解@WebServlet("/tets")
package cn.lacknb.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/test")
public class TestServlet extends HttpServlet {
public TestServlet() {
System.out.println("servlet初始化");
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servlet中的service方法被调用");
}
}
浏览器访问 http://localhost:8080/test
package cn.lacknb.servlet;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
@WebListener
public class TestListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext初始化");
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext销毁...");
}
}
package cn.lacknb.servlet;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/filter/*")
public class TestLisnter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("过滤器初始化");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("dofilter 方法执行");
}
@Override
public void destroy() {
System.out.println("destroy ");
}
}
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
</parent>
<groupId>cn.lacknb.war</groupId>
<artifactId>Springboot-war</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<build>
<finalName>test-war</finalName>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
package cn.lacknb.main;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
/*
* 启动类继承SpringBootServletInitializer类 这里需要实现重写父类的configure方法
* SpringBootServletInitializer类将会调用configure方法来得到一个SpringApplicationBuilder
* 实例, 根据名称所知, 这个SpringApplicationBuilder会帮我们创建上下文, 实际上它会帮我们创建
* WebApplicationContext实例, 其在创建Spring上下文的过程中, 会查找使用了@Configuration注解的
* 配置类, 然后对它们进行初始化
*
* */
@SpringBootApplication
@RestController
public class WarApp extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(WarApp.class);
}
public static void main(String[] args) {
SpringApplication.run(WarApp.class, args);
}
@GetMapping("/hello")
@ResponseBody
public String hello(){
return "hello, war .....package......";
}
}
启动类继承SpringBootServletInitializer类 这里需要实现重写父类的configure方法
SpringBootServletInitializer类将会调用configure方法来得到一个SpringApplicationBuilder
实例, 根据名称所知, 这个SpringApplicationBuilder会帮我们创建上下文, 实际上它会帮我们创建
WebApplicationContext实例, 其在创建Spring上下文的过程中, 会查找使用了@Configuration注解的
配置类, 然后对它们进行初始化
-- 对于启动类, 我们可以这样理解: 在eclipse运行main方法时, 会使用原来的方式(执行jar包的方式)来启动web应用(含服务器), 如果将项目打包成一个war包放到web服务器上运行, 就会创建WebApplicationContext, 在创建的过程中, 会调用configure方法. 另外, 父类调用一个方法来实现部分工作, 但该方法需要由继承的子类实现, 这种模式是GoF设计模式的一种: 模板方法.
将war包放在tomcat的webapp目录下, 然后直接启动tomcat, 在浏览器地址栏输入: localhost:8080/test-war/hello
首先先排除依赖tomcat, 这个是springboot默认使用的.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
添加jetty服务器
<!--使用jetty服务器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
方法和上面一样
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
服务器访问日志与服务器日志不是同一个概念, 我们一般在tomcat控制台中看到的日志, 是服务器日志, 而服务器访问日志, 则会记录服务处理的消息, 默认情况下, tomcat并不会记录fangwen 日志. 创建一个maven项目, 将application.yml文件 添加如下信息.
server:
tomcat:
basedir: my-tomcat
accesslog:
pattern: 'ip: %A, response code: %s, time: %t'
enabled: true # 用于开启日志记录
directory: my-logs
buffered: false # 表示不进行缓冲, 直接将日志记录到文件中
日志显示格式为:
ip: 127.0.1.1, response code: 200, time: [21/Nov/2019:18:01:50 +0800]
ip: 127.0.1.1, response code: 200, time: [21/Nov/2019:18:01:50 +0800]
ip: 127.0.1.1, response code: 200, time: [21/Nov/2019:18:01:51 +0800]
在根目录创建banner.txt
将ASCII图案放在这里
除了文本外, 还可以提供图片文件用于显示, 图片格式可以为jpg, png或gif, Springboot会将图片转换为ASCII, 以文本的方式将图片显示到控制台中.
application.yml如下
spring:
banner:
charset: utf-8
image:
location: classpath:banner.jpg
invert: false
在springboot中配置banner, 可以在Application.yml中配置一下属性: