目录
SpringBoot相关知识
/  

SpringBoot相关知识

SpringBoot相关知识

Spring常用注解

springboot的启动类的@SpringBootApplication, 控制器的@Controller, @RestController, 还有与配置相关的一系列注解.

   查找XML文档与配置<bean>元素这两个工作同样可以使用注解来完成. 使用@Component, @Service, 或者@Configuration注解来修饰一个java类, 这些java类会被Spring自动检测并注册到容器中. 在类里面使用@Bean注解修饰的方法, 会被作为一个bean存放到Spring容器中.

@Autowired, @Primary, @Resource

@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 }

Scope注解

   在配置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 !!"); } }

AOP注解

加入依赖

<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注解

ComponentScan注解主要用于检测使用@Component装饰的组件, 并把它们注册到Spring容器中. 除了使用@Component修饰的组件外, 还有间接使用@Component的组件(如@Service, @Repository, @Controller, 或自定义注解), 都会被检测到,. 在使用Springboot时, 我们一般很少接触@ComponentScan注解, 但实际上每个应用都使用它, 它只是被放到@SpringBootApplication里面了. 在使用ComponentScan注解时, 要注意过滤器.

高级Spring注解

限定注解

@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. }

自定义bean的生命周期

前面介绍了, 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注释的方法必须遵守以下所有标准:

    • 该方法不得有任何参数, 除非是在EJB拦截器的情况下, 根据EJB规范的定义, 在这种情况下它将带有一个invocationContext对象
    • 该方法的返回类型必须为void
    • 该方法不得抛出已检查异常
    • 应用PostConstruct的方法可以是Public, protected, package private或private
    • 除了应用程序之外, 该方法不能是static, 该方法可以是final
    • 如果该方法抛出未检查异常, 那么不得将次类放入服务中, 除非是能够处理异常并可从中恢复的EJB .
  • 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

Spring MVC常用注解

@ Controller, @RequestMapping, @GetMapping等.

@RestController

@RestController注解的目的是为了让我们更加简便地使用@Controller和@ResponseBody这两个注解, 查看@RestController源码就可以知道, 这个注解本身就使用了@Controller与@ResponseBody来修饰. ResponseBody注解可以修饰控制方法, 方法的返回值将会被写到HTTP响应体中, 所返回的内容, 将不会放到模型中, 也不会被解释为视图的名称.

@RequestAttribute, @SessionAttribute, @SessionAttributes等注解也较为常用.

@MatrixVariable注解

前面的@PathVariable注解主要是用来获取单一的URI参数, 如果通过URI来传输一些复杂的参数, 则可以考虑使用@MatrixVariable注解. 使用时需要遵守一定的规范, 参数的名称与值使用key-value形式, 多个参数之间使用(;)号隔开, 如果一个参数拥有多个值, 则值与值之间用逗号隔开. 这种URI的参数表现形式, 成为矩阵变量.

/car/{id};color=red;year=2019

该URI传入了id参数, 并且传入了color与year参数, 值分别为red与2019. 对于上面的URI, 如何获取呢? 例子:

  1. 首先开启矩阵变量的支持

    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; } }
  2. 控制器

    @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"; }
  3. 测试

    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"; }

多文件上传

  1. 创建实体类

    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; } }
  2. 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>
  3. 控制器

    /* * 多文件上传 * */ @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的条件注解

   SpringBoot提供了自动配置, 一些内置的或者常用的组件Spring已经帮我们配置好了, 我们在使用这些组件的时候, 加入相关依赖就可以了, 并不需要额外进行配置, 从而减少了搭建项目的工作量. 自动配置是否执行, 取决于条件注解.

类条件注解

   当类路径中存在或不存在某个类时, 再决定是否执行自动配置. 存在该类则使用@ConditionalOnClass, 不存在则使用@ConditionalOnMissingClass, 它们都可以修饰类或者方法. 这两个注解通过配置value属性来指定一个类, 这个类有可能并不存在于实际的运行环境中, 也可以使用name属性来指定权限定类名. 假设项目中存在MySQL的驱动类, 则会启用某个配置类, 此时则可以使用@ConditionalOnClass.

  1. 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; } }
  2. 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的实例

  3. 测试类

    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服务

REST是英文 Representational State Transfer 的缩写, 一般翻译为 "表述性状态转移". REST本身只是分布式系统中的一种架构风格, 并不是某种标准或者规范. 而是一种 轻量级的基于HTTP协议的Web Service风格.

发布REST服务

加入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; } } }

使用RestTemplate调用服务

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调用服务

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()); } }

SpringBoot与Servlet

无论是创建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); } }

创建Servlet

这里使用注解@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 "); } }

使用Springboot构建可部署的war包

<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

使用其他服务器

使用jetty

首先先排除依赖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>

MIU3z6.png

使用Undertow

方法和上面一样

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency>

MIap6K.png

服务器访问日志

服务器访问日志与服务器日志不是同一个概念, 我们一般在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配置 (自定义显示图案)

  1. 在根目录创建banner.txt

    将ASCII图案放在这里

  2. 除了文本外, 还可以提供图片文件用于显示, 图片格式可以为jpg, png或gif, Springboot会将图片转换为ASCII, 以文本的方式将图片显示到控制台中.

application.yml如下

spring: banner: charset: utf-8 image: location: classpath:banner.jpg invert: false

在springboot中配置banner, 可以在Application.yml中配置一下属性:

  • spring.banner.charset: 如果banner的文本文件中有utf-8以外的编码, 则需要配置该项
  • spring.banner.image.location: 指定banner图片的位置
  • spring.banner.image.width: banner图片经转换后的字符长度, 默认为76
  • spring.banner.image.height: 图片经转换后的字符高度
  • spring.banner.image.margin: 设置图片的显示边距 默认为2
  • spring.banner.image.invert: 配置为true 则将图片进行转换显示, 以适应深色的终端风格

标题:SpringBoot相关知识
作者:gitsilence
地址:https://blog.lacknb.cn/articles/2019/11/26/1577974160404.html

Gitalking ...