SpringMVC & SpringBoot小记

 

SpringMVC

1、SpringMVC常用注解

https://blog.csdn.net/lipinganq/article/details/79155072 

https://blog.csdn.net/javazejian/article/details/54561302

用于依赖注入:@Autowired、@Resource、@Value

用于Bean定义:@Component、@Controller、@Service、@Repository、@Scope、@RequestMapping(@GetMapping、@PostMapping....)、@ResponseBody

@Controller、@Service、@Repository三者与@Component等效。

 

1、@Component

@Component
是所有受Spring 管理组件的通用形式,@Component注解可以放在类的头上,@Component不推荐使用。

2、@Controller

@Controller对应表现层的Bean,也就是Action,例如:

1 @Controller
2 @Scope("prototype")
3 public class UserAction extends BaseAction<User>{
4 ……
5 }

使用@Controller注解标识UserAction之后,就表示要把UserAction交给Spring容器管理,Spring容器中会存在一个名字为"userAction"action,这个名字是根据UserAction类名来取的。注意:如果@Controller不指定其value【@Controller】,则默认的bean名字为这个类的类名首字母小写如果指定value【@Controller(value="UserAction")】或者【@Controller("UserAction")】,则使用value作为bean的名字

这里的UserAction还使用了@Scope注解,@Scope("prototype")表示将Action的范围声明为原型,可以利用容器的scope="prototype"来保证每一个请求有一个单独的Action来处理,避免strutsAction的线程安全问题。spring 默认scope是单例模式(scope="singleton"),这样只会创建一个Action对象,每次访问都是同一Action对象,数据不安全,struts2 是要求每次次访问都对应不同的Actionscope="prototype" 可以保证当有请求的时候都创建一个Action对象

3、@ Service

@Service对应的是业务层Bean,例如:

1 @Service("userService")
2 public class UserServiceImpl implements UserService {
3 ………
4 }

@Service("userService")注解是告诉Spring,当Spring要创建UserServiceImpl的的实例时,bean的名字必须叫做"userService",这样当Action需要使用UserServiceImpl的的实例时,就可以由Spring创建好的"userService",然后注入给Action:在Action只需要声明一个名字叫“userService”的变量来接收由Spring注入的"userService"即可,具体代码如下:

1 // 注入userService
2 @Resource(name = "userService")
3 private UserService userService;

注意:在Action声明的“userService”变量的类型必须是“UserServiceImpl”或者是其父类“UserService”,否则由于类型不一致而无法注入,由于Action中的声明的“userService”变量使用了@Resource注解去标注,并且指明了其name = "userService",这就等于告诉Spring,说我Action要实例化一个“userService”,你Spring快点帮我实例化好,然后给我,当Spring看到userService变量上的@Resource的注解时,根据其指明的name属性可以知道,Action中需要用到一个UserServiceImpl的实例,此时Spring就会把自己创建好的名字叫做"userService"的UserServiceImpl的实例注入给Action中的“userService”变量,帮助Action完成userService的实例化,这样在Action中就不用通过“UserService userService = new UserServiceImpl();”这种最原始的方式去实例化userService了。如果没有Spring,那么当Action需要使用UserServiceImpl时,必须通过“UserService userService = new UserServiceImpl();”主动去创建实例对象,但使用了Spring之后,Action要使用UserServiceImpl时,就不用主动去创建UserServiceImpl的实例了,创建UserServiceImpl实例已经交给Spring来做了,Spring把创建好的UserServiceImpl实例给Action,Action拿到就可以直接用了。Action由原来的主动创建UserServiceImpl实例后就可以马上使用,变成了被动等待由Spring创建好UserServiceImpl实例之后再注入给Action,Action才能够使用。这说明Action对“UserServiceImpl”类的“控制权”已经被“反转”了,原来主动权在自己手上,自己要使用“UserServiceImpl”类的实例,自己主动去new一个出来马上就可以使用了,但现在自己不能主动去new“UserServiceImpl”类的实例,new“UserServiceImpl”类的实例的权力已经被Spring拿走了,只有Spring才能够new“UserServiceImpl”类的实例,而Action只能等Spring创建好“UserServiceImpl”类的实例后,再“恳求”Spring把创建好的“UserServiceImpl”类的实例给他,这样他才能够使用“UserServiceImpl”,这就是Spring核心思想“控制反转”,也叫“依赖注入”,“依赖注入”也很好理解,Action需要使用UserServiceImpl干活,那么就是对UserServiceImpl产生了依赖,Spring把Acion需要依赖的UserServiceImpl注入(也就是“给”)给Action,这就是所谓的“依赖注入”。对Action而言,Action依赖什么东西,就请求Spring注入给他,对Spring而言,Action需要什么,Spring就主动注入给他。

4、@ Repository

@Repository对应数据访问层Bean ,例如:

1 @Repository(value="userDao")
2 public class UserDaoImpl extends BaseDaoImpl<User> {
3 ………
4 }

@Repository(value="userDao")注解是告诉Spring,让Spring创建一个名字叫“userDao”的UserDaoImpl实例。

当Service需要使用Spring创建的名字叫“userDao”的UserDaoImpl实例时,就可以使用@Resource(name = "userDao")注解告诉Spring,Spring把创建好的userDao注入给Service即可。

1 // 注入userDao,从数据库中根据用户Id取出指定用户时需要用到
2 @Resource(name = "userDao")
3 private BaseDao<User> userDao;

  

@Scope

标记在类和方法,标记上述Spring Bean的作用域:singleton、prototype、request、session、application

  • singleton:单例,无论getBean多少次得到的都是同一个实例
  • prototype:每次getBean都创建一个新的实例
  • request:每个http请求创建一个实例,该实例仅在该请求内有效且该请求过程中用同一个实例,不同request创建的实例互不干扰;实例随请求结束而销毁
  • session:没当创建一个会话时创建一个实例,实例随着会话的结束而销毁

@Service、@Repository、@Component等默认即此

 

Java Singleton与Spring Singleton的区别:Java singleton class is per classloader and Spring’s singleton is per application context.  更多可参考https://javabeat.net/spring-singleton-java-singleton/

 

 

接收请求参数和页面传参

转自:http://blog.csdn.net/z69183787/article/details/41653875

2、接收请求请求

(1)使用HttpServletRequest获取,如request.getParameter("name") 

(2)@RequestParam("pass")String password,或@Param("pass")String password。表单参数也可以用这种方式获取,Spring会自动将表单参数注入到方法参数,和表单的name属性保持一致。

(3)自动注入Bean属性

3、向页面传参

(1)使用HttpServletRequest 和 Session  然后setAttribute(),就和Servlet中一样

(2)使用ModelAndView对象

(3)使用ModelMap对象

(4)使用@ModelAttribute注解

 

4、异常捕获处理

@ExceptionHandler:捕获特定异常进行处理,仅对当前Controller有效

@ControllerAdvice:修饰类,捕获全局各个Controller抛出的异常。ControllerAdvice注解只拦截Controller不会拦截Interceptor的异常。

示例:

@Controller
@RequestMapping("/exception")
@ControllerAdvice
public class ExceptionController {
    @ExceptionHandler({ ArithmeticException.class }) // 单用@ExceptionHandler时只对当前controller有效即只捕获当前Controller抛出的异常,结合@ControllerAdvice可对全局有效。
    // @ResponseStatus(value = HttpStatus.NOT_FOUND)
    @ResponseBody
    public String handleArithmeticException(Exception e) {
        e.printStackTrace();
        return "some arighmetric error";
    }

    @RequestMapping(value = "e/{id}", method = { RequestMethod.GET })
    @ResponseBody
    public String testExceptionHandle(@PathVariable(value = "id") Integer id) {
        System.out.println(10 / id);
        return id.toString();
    }
}

@Controller
class TTT {
    @RequestMapping(value = "e/{id}", method = { RequestMethod.GET })
    @ResponseBody
    public String testExceptionHandle(@PathVariable(value = "id") Integer id) {
        System.out.println(10 / id);
        return id.toString();
    }
}
View Code

该示例中,若没有@ControllerAdvice注解,则分别访问 http://localhost:8081/exception/e/0 、 http://localhost:8081/e/0 前者会被@ExceptionController指定的方法捕获处理、后者则不会被捕获。若加上@ControllerAdvice注解则访问两个路径都会被处理。

另外注意,类需要在能被spring扫描到的包下。

 

5、request body传数组

public Integer deleteAdminAnStus(@RequestBody List<String> studentIdList, HttpServletRequest request) {} //前端请求时直接在body传类似如下格式的数据即可:  ["a","b"]

6、页面重定向

假设当前在 /api/v1,则

response.sendRedirect("/test"); 将导向 /test

response.sendRedirect("test"); 将导向 /api/test

 

7、手动返回错误信息

response.sendError(400, "some error");

 

8、返回数据的统一包装

 1 @ControllerAdvice // 拦截所有Controller的处理结果
 2 public class ControllerResponseWrapper implements ResponseBodyAdvice<Object> {
 3     private static final Logger logger = LoggerFactory.getLogger(ControllerResponseWrapper.class);
 4 
 5     private final Set<MediaType> jsonMediaType = new HashSet<>(
 6             Arrays.asList(MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON_UTF8));
 7 
 8     // 对于哪些请求要执行beforeBodyWrite,返回true执行,返回false不执行
 9     @Override
10     public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> converterType) {
11         return true;
12     }
13 
14     @Override
15     public Object beforeBodyWrite(Object obj, MethodParameter returnType, MediaType mediaType,
16             Class<? extends HttpMessageConverter<?>> converterType, ServerHttpRequest request,
17             ServerHttpResponse response) {
18 
19         // 不是需要验证的路径,不处理
20         // String reqURLPath = request.getURI().getPath();
21         // if (!reqURLPath.startsWith(WebSecurityConfig.AUTH_PATH_PREFIX)) {//发送500时reqURL被重定向到/error了,所以此法不可行
22         // }
23         // 类型 不属于 需要处理的包头的时候,不处理
24         if (!jsonMediaType.contains(mediaType)) {
25         }
26         // 当类型 是属于需要处理的时候 并且 obj不是ReturnMsg的时候 进行格式化处理
27         else if (obj == null || !(obj instanceof ReturnMsg)) {
28             if (isExceptionOccur(obj)) {// 1.说明请求失败,返回了默认错误处理DefaultHandlerExceptionResolver的结果
29                 Map<String, Object> res = (Map<String, Object>) obj;
30                 int status = (int) res.get("status");
31                 String errorMsg = (String) res.get("error") + ": " + res.get("message");
32                 String path = (String) res.get("path");
33 
34                 obj = ReturnMsg.fail(status, errorMsg, path, request.getMethodValue());
35 
36             } else {// 2.说明请求成功
37                 ReturnMsg newRes = ReturnMsg.success(obj, request.getURI().getPath(), request.getMethodValue());
38                 if (obj instanceof String) {// 对于String类型默认会用org.springframework.http.converter.StringHttpMessageConverter处理,所以ReturnMsg类型obj会出错
39                     try {
40                         obj = new ObjectMapper().writeValueAsString(newRes);
41                     } catch (JsonProcessingException e) {
42                         // TODO Auto-generated catch block
43                         e.printStackTrace();
44                     }
45                 } else {
46                     obj = newRes;
47                 }
48             }
49         }
50         // 返回结果已经是包装格式
51         else {
52         }
53 
54         logger.info("Req from {}: {}", request.getRemoteAddress(), obj);
55         return obj;
56     }
57 
58     /** 根据返回结果判断是否是发ere生异常返回的,正常结果也可能是这个格式,所以检查得严格! */
59     private static boolean isExceptionOccur(Object obj) {
60         /**
61          * 默认的Spring异常处理DefaultHandlerExceptionResolver返回格式为:<br>
62          * { <br>
63          * "timestamp":"2018-06-19T06:19:57.757+0000",<br>
64          * "status":400,<br>
65          * "error":"Bad Request",<br>
66          * "message":"Failed to convert value of type 'java.lang.String' to required type 'java.lang.Integer'",<br>
67          * "path":"/api/v1/student/recent_experiment"<br>
68          * }
69          */
70 
71         // 不是Map则肯定不是异常
72         if ((obj == null) || !(obj instanceof Map<?, ?>)) {
73             return false;
74         }
75 
76         // 不符合异常的格式,则不是异常。由于正常返回数据也有可能这几个字段,所以检查要严格点
77         @SuppressWarnings("unchecked")
78         Map<String, Object> mapObj = (Map<String, Object>) obj;
79         if (mapObj.get("path") == null && mapObj.get("timestamp") == null || mapObj.get("status") == null) {
80             return false;// 发生异常时 error 和 message 有可能为null
81         }
82         // 发生异常时status肯定不是200
83         if ((Integer) (mapObj.get("status")) == 200) {
84             return false;
85         }
86         return true;
87     }
88 }
View Code

通过实现ResponseBodyAdvice接口的beforeBodyWrite方法来完成,在方法里包装Controller返回数据srcRes得到 自定义统一数据格式ApiResMsg的数据finalRes 后返回。(注:只能捕获到Controller类里方法返回的结果

Controller方法返回的srcRes为String类型时是种特殊情况,需特殊处理:

beforeBodyWrite方法返回的数据将会被converterType所表示的MessageConverter处理后输出给前端,在SpringMVC中定义了9个MessageConverter来处理返回数据。其中:

org.springframework.http.converter.json.MappingJackson2HttpMessageConverter:可以处理任意类型数据:若是基本数据类型则原样输出、对于Map、Java Bean则会转成JSON输出(对于Map输出所有键值对、Java Bean输出所有getXxx方法的xxx即输出的参数名为xxx而不是field名故可以输出Bean里不存在的属性名

class org.springframework.http.converter.StringHttpMessageConverter:只能处理String类型的数据,若输入非String类型显然会报错。

对于一个Controller中的方法来说,其返回值srcRes会被哪种MessageConverter处理取决于srcRes的类型:

对于@RestController(@Controller+@ResponseBody)下的方法来说,通常(除了String类型)其返回的content-type=application/json,beforeBodyWrite返回值finalRes将被converterType=MappingJackson2HttpMessageConverter处理

然而当srcRes为String类型时是特例,其content-type=text/plain, converterType=StringHttpMessageConverter(猜测不与上述一样的原因是SpringMVC中返回String类型的srcRes可以代表一个view?)

可见,srcRes是String时converterType为StringHttpMessageConverter,若直接在beforeBodyWrite里将其包装为ApiResMsg类型的finalRes,则会报错。解决:此时可以在beforeBodyWrite里进一步将finalRes转为String类型,然而仍存在问题,因为srcRes为null时无法知道srcRes的类型,终极解法:如示例代码所示,根据converterType确定是否将finalRes转为String类型。

相关见:https://my.oschina.net/u/1757225/blog/1543715

 

9、@Value注入静态域

默认@Value是注入到实例域的,要注入到静态域,可以通过如下方法:

@Service //或者@Compoent、@Controller等,以便使@Value生效
class GlobalValue {

    public static String hostname;

    @Value("${sensestudy.redis.host}")
    public void setMyHost(String h) {
        hostname = h;
    }

}

 

10、上传文件并附带额外参数

其实就是借助form表单来实现。

后端代码:

    @PostMapping(value = "/api/v1/admin/students")
    @PreAuthorize(("hasRole('ROLE_ADMIN')"))
    public List<String> addAdminStudents(@RequestParam UserEntity userEntity, @RequestParam("stuFile") List<MultipartFile> files,
            @RequestParam("isOverride") boolean isOverride, HttpServletRequest request) throws Exception {

        // List<MultipartFile> files = ((MultipartHttpServletRequest) request).getFiles("stuFile");

        String adminId = controllerUtils.getUserIdFromJwt(request);
        List<StudentBeanForAddAccount> stuBeanList = resolveStudentAccountFile(files);
        return studentService.addStudentAccounts(adminId, stuBeanList, isOverride);
    }

前端逻辑:构造个form表单放所需参数(包括stuFile、isOverride)提交即可;若有很多个非文件域参数,则后端可以声明个@RequestParam UserEntity userEntity来接收这些非文件域参数。

进阶:有时候额外参数可能是个JSON,怎么办?

可以将JSON转成String然后作为form表单的一个字段;

或者将JSON里的各字段拿出来分别作为form表单的一个字段传输,但若JSON有多层则此法行不通了。

另外须注意:form表单只能用POST方法

 

 

11、@Autoweird、@Resource、@Inject都可以用于依赖注入,其区别

https://www.sourceallies.com/2011/08/spring-injection-with-resource-and-autowired/#more-2350

 ‘@Autowired’ and ‘@Inject’ annotation behave identically. Both of these annotations use the ‘AutowiredAnnotationBeanPostProcessor’ to inject dependencies. ‘@Autowired’ and ‘@Inject’ can be used interchangeable to inject Spring beans. However the ‘@Resource’ annotation uses the ‘CommonAnnotationBeanPostProcessor’ to inject dependencies. Even though they use different post processor classes they all behave nearly identically. 

 

12、事务@Transational

Java Throwable分为Error和Exception,Exception分为Unchecked Exception(包括RuntimeException类及其子类)和Checked Exception(包括Exception类自身)。

Spring的AOP即声明式事务管理@Transactional默认是遇到运行异常(RuntimeException)和程序错误(Error)才会回滚,对checked Exception即Exception可try{}捕获的不会回滚。 因此若在API中实现自定义异常则最好继承RuntimeException以在出错时能进行回滚。若想针对Checked Exception进行事务回滚,可在@Transactional 注解里使用 rollbackFor 属性明确指定异常,如: @Transactional(rollbackFor = Exception.class) 

@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。

虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为只有在使用基于接口的代理时它才会生效。

@Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。

默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。

可参考:https://www.jianshu.com/p/380a9d980ca5

 

13、代码中添加静态资源

在 Spring MVC 中,资源的查找、处理使用的是责任链设计模式(Filter Chain):

图片

其思路为如果当前 resolver 找不到资源,则转交给下一个 resolver 处理。 当前 resolver 找到资源则立即返回给上级 resovler(如果存在),此时上级 resolver 又可以选择对资源进一步处理或再次返回给它的上级(如果存在)。
配置方法为重写 WebMvcConfigurerAdapter 类的 addResourceHandlers:

 

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
    @Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/webjars/**")
                .addResourceLocations(
                        "classpath:/META-INF/resources/webjars/");
}

这段代码实际上是添加了一个 PathResourceResolver来完成对资源的查找,该 resolver 的作用是将 url 为 /webjars/** 的请求映射到 classpath:/META-INF/resources/webjars/。
比如请求 http://localhost:8080/webjars/jquery/3.1.0/jquery.js 时, Spring MVC 会查找路径为 classpath:/META-INF/resources/webjars/jquery/3.1.0/jquery.js 的资源文件。

使用了 @EnableWebMvc 注解后 WebMvcAutoConfiguration 提供的默认配置会失效,必须提供全部配置。想要使用默认配置,无需使用 @EnaleWebMvc 注解。

 

参考:深入 Spring 系列之静态资源处理

 

 

 

14、过滤器(Filter)与拦截器(Interceptor)

区别:

Filter 接口定义在 javax.servlet 包中;HandlerInterceptor接口定义在org.springframework.web.servlet 包中
Filter在 Servlet 规范中定义,依赖于Servlet容器,被Servlet容器(如Tomcat)调用;Interceptor不依赖于Servlet容器,是Spring框架的一个组件,归Spring IoC容器管理调用。因此可以通过注入等方式来获取其他Bean的实例,使用更方便。
Filter是基于函数回调的,而Interceptor则是基于动态代理(Java反射)的。
Filter只能在请求的前后使用,而Interceptor可以详细到每个方法(即handler)
Filter对几乎所有的请求起作用(init、doFilter、destroy),而Interceptor只能对handler请求起作用(preHandle、postHandle、afterCompletion
Interceptor可以访问handler的上下文,值栈里的对象,而Filter不能。 

注:SpringBoot(SpringMVC)里的Handler特指@Controller注解的类里每个处理HTTP请求的public method

 

执行顺序:过滤前-拦截前-handler执行-拦截后-过滤后

示例:

//这里通过如下两个注解使过滤器生效。也可以不用注解,通过FilterRegistrationBean添加过滤器
@Component
@WebFilter(urlPatterns = "/Blogs", filterName = "blosTest")
class TestFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.err.println("filter .. init");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        System.out.println("filter 请求前");
        filterChain.doFilter(request, response);

        System.out.println("filter 请求后");
    }

    @Override
    public void destroy() {
        System.err.println("filter .. destroy");
    }
}
Filter Demo
 1 import javax.servlet.http.HttpServletRequest;
 2 import javax.servlet.http.HttpServletResponse;
 3 
 4 import org.springframework.context.annotation.Configuration;
 5 import org.springframework.web.servlet.HandlerInterceptor;
 6 import org.springframework.web.servlet.ModelAndView;
 7 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 8 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 9 
10 @Configuration
11 public class LoginInterceptorConfigurer implements WebMvcConfigurer {
12     @Override
13     public void addInterceptors(InterceptorRegistry registry) {
14         // 多个拦截器组成一个拦截器链
15         // addPathPatterns 用于添加拦截规则
16         // excludePathPatterns 用户排除拦截
17         registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**");
18         registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**");
19     }
20 }
21 
22 class MyInterceptor1 implements HandlerInterceptor {
23 
24     @Override
25     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
26             throws Exception {
27         System.out.println(">>>MyInterceptor1>>>>>>>在请求处理之前进行调用(Controller方法调用之前)");
28 
29         return true;// 只有返回true才会继续向下执行,返回false取消当前请求
30     }
31 
32     @Override
33     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
34             ModelAndView modelAndView) throws Exception {
35         System.out.println(">>>MyInterceptor1>>>>>>>请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)");
36     }
37 
38     @Override
39     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
40             throws Exception {
41         System.out.println(">>>MyInterceptor1>>>>>>>在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)");
42     }
43 
44 }
45 
46 class MyInterceptor2 implements HandlerInterceptor {
47 
48     @Override
49     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
50             throws Exception {
51         System.out.println(">>>MyInterceptor2>>>>>>>在请求处理之前进行调用(Controller方法调用之前)");
52 
53         return true;// 只有返回true才会继续向下执行,返回false取消当前请求
54     }
55 
56     @Override
57     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
58             ModelAndView modelAndView) throws Exception {
59         System.out.println(">>>MyInterceptor2>>>>>>>请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)");
60     }
61 
62     @Override
63     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
64             throws Exception {
65         System.out.println(">>>MyInterceptor2>>>>>>>在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)");
66     }
67 
68 }
69 
70 
71 
72 //输出如下
73 
74 >>>MyInterceptor1>>>>>>>在请求处理之前进行调用(Controller方法调用之前)
75 >>>MyInterceptor2>>>>>>>在请求处理之前进行调用(Controller方法调用之前)
76 2018-12-07 17:43:48.043 [http-nio-8080-exec-1] INFO  c.s.s.r.ControllerResponseWrapper - Req from /0:0:0:0:0:0:0:1:34712: {  errorCode:1000, errorMsg:ok, path:/api/v1/admin_super/courses, method:GET, timestamp:2018-12-07 17:43:48 }
77 >>>MyInterceptor2>>>>>>>请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
78 >>>MyInterceptor1>>>>>>>请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
79 >>>MyInterceptor2>>>>>>>在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
80 >>>MyInterceptor1>>>>>>>在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
InterceptorDemo

 

参考资料:

https://segmentfault.com/a/1190000012072060
http://einverne.github.io/post/2017/08/spring-interceptor-vs-filter.html

 

过滤器触发两次

将Filter定义成一个Bean并通手动注册到SpringSecurity时( .addFilterBefore(myFilter, UsernamePasswordAuthenticationFilter.class) //这里 myFilter通过 @Autowired MyFilter myFilter; 定义 ),此时如果调用一个handler一次则会触发两次该Filter。

原因:SpringBoot对于任何一个bean都会自动注册,加上我们手动注册的,这样该Filter就被注册了两次。解决:

法1:不让Filter成为一个Bean:若该Filter里面没有依赖需要自动注入的Bean,则可以不将该Filter定义为Bean,这样往SpringSecurity注册的filter 通过new Filter()产生而不是Autowired,从而不会注册两次。

法2:禁止SpringBoot自动注册该Bean,可以在该Filter里加上如下代码:

    @Bean
    public FilterRegistrationBean registration(MyFilter filter) {// 本filter将手动注册到SpringSecurity。但SpringBoot会自动注册任何一个bean组件,这样导致注册两次从而每次调用都会触发两次,故通过此来让SpringBoot别自动注册此filter
        FilterRegistrationBean registration = new FilterRegistrationBean(filter);
        registration.setEnabled(false);
        return registration;
    }

法3:继承OncePerRequestFilter来实现Filter,这样虽然注册了两次,但可以保证只执行一次。implements Filter 和 extends OncePerRequestFilter两种写法的示例分别如下:

 1     @Component
 2     @WebFilter(urlPatterns = "/Blogs", filterName = "blosTest")
 3     class TestFilter1 extends OncePerRequestFilter {
 4 
 5         @Autowired
 6         CustomerCourseService customerCourseService;
 7 
 8         @Override
 9         protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
10                 FilterChain filterChain) throws ServletException, IOException {
11             // you business
12             // customerCourseService.getByCustomerId("");
13         }
14 
15         @Override
16         public void destroy() {
17             System.err.println("filter .. destroy");
18         }
19     }
20 
21     @Component
22     @WebFilter(urlPatterns = "/Blogs", filterName = "blosTest")
23     class TestFilter2 implements Filter {
24 
25         @Autowired
26         CustomerCourseService customerCourseService;
27 
28         @Override
29         public void init(FilterConfig filterConfig) throws ServletException {
30 
31         }
32 
33         @Override
34         public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
35                 throws IOException, ServletException {
36             // you business
37             // customerCourseService.getByCustomerId("");
38 
39         }
40 
41         @Override
42         public void destroy() {
43 
44         }
45     }
View Code

 

 

 

 

SpringBoot基础

配置文件语法

配置文件可以用yml或properties, 若是yml,则键与值间的冒号后需要有空格!,如 :

sensestudy:
  redis:
    host: 172.20.6.88
    port: 6379

 

配置文件优先级

代码中的初始配置 < 配置文件中的配置 < 操作系统环境变量 < 命令行启动参数中的配置

若一个模块依赖另一个模块,则被依赖module里配置文件中的配置项  <  当前项目配置文件中的配置项

 

Spring Security

基于表达式的权限控制

hasRole、hasAnyRole

 

log配置

SpringBoot默认采用logback日志框架,可以配置采用其他框架。

可以直接在application.yml配置(日志级别、输出文件等),如:

logging:
  level:
    root: info
  file: ./logs/log.log  #输出位置
#config: classpath:logback-spring.xml

但是此对于定期输出一个文件等较复杂的配置无能为力,可以借助日志配置文件:

如上述配置中指定了配置文件,在项目resources文件夹下。也可以不指定,只要配置文件名按默认风格命名即可。配置示例:

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true" scan="false" scanPeriod="30 seconds">
	<contextName>sensestudy</contextName>
	
	<!-- 文件输出路径 -->
	<springProperty scope="context" name="LOG_FILE_PATH" source="logging.path" />
	<!-- <property name="LOG_FILE_PATH" value="/sensestudy_logs/javaserver_logs" /> -->

	<!-- 日志级别 -->
	<springProperty scope="context" name="LOG_LEVEL" source="logging.level.root" />
	<!-- <property name="LOG_LEVEL" value="info" /> -->

	<!-- 输出格式 -->
	<!-- 格式化输出:%date表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符 -->
	<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />


	<appender name="CONSOLE_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
		<encoder>
			<Pattern>${LOG_PATTERN}</Pattern>
		</encoder>
		<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
			<level>TRACE</level>
		</filter>
	</appender>

	<appender name="FILE_APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">

		<!-- <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <OnMismatch>DENY</OnMismatch> <OnMatch>ACCEPT</OnMatch> </filter> -->

		<file>${LOG_FILE_PATH}/sensestudyserver.log</file>

		<encoder>
			<pattern>${LOG_PATTERN}</pattern>
		</encoder>

		<append>true</append>

		<!-- <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>warn</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> -->

		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
			<fileNamePattern>${LOG_FILE_PATH}/sensestudyserver.%d{yyyy-MM-dd}.%i.log.zip
			</fileNamePattern>

			<!-- keep 30 days' files -->
			<!-- <maxHistory>30</maxHistory> -->
			<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
				<maxFileSize>20MB</maxFileSize>
			</timeBasedFileNamingAndTriggeringPolicy>

			<!-- 超出删除老文件 -->
			<totalSizeCap>20GB</totalSizeCap>
		</rollingPolicy>

	</appender>


	<!-- root是默认的logger,输出到console -->
	<root level="${LOG_LEVEL}">
		<appender-ref ref="CONSOLE_APPENDER" />
	</root>

	<!-- 另外专门定义一个logger用来输出埋点信息到特定文件 -->
	<logger name="fileLogger" additivity="false" level="${LOG_LEVEL}">
		<appender-ref ref="FILE_APPENDER" />
		<appender-ref ref="CONSOLE_APPENDER" /><!-- 同时也输出到console -->
	</logger>

</configuration>
View Code

配置文件中的配置优先级优于application.yml,在配置文件指定日志输出位置后,application.yml中的file(输出位置)就失效了。

更多可参考:SpringBoot logback日志配置

 

SpringBoot 静态资源

Spring Boot 默认“约定”从资源目录的这些子目录读取静态资源:

  • src/main/resources/META-INF/resources
  • src/main/resources/static (推荐)
  • src/main/resources/public

 

自定义handler参数转换器

如将 int 请求参数转为对应的枚举值:

    // add request parameter converters to current controller's handler
    @InitBinder
    public void initBinder(WebDataBinder dataBinder) {
        // DevelopStateEnum converter
        dataBinder.registerCustomEditor(DevelopStateEnum.class, new PropertyEditorSupport() {
            @Override
            public void setAsText(String text) throws IllegalArgumentException {
                Integer devState = Integer.parseInt(text);
                setValue(DevelopStateEnum.myValueOf(devState));
            }
        });
    }

上述方法位于一个Controller内,只对当前Controller有效,若要对全局有效,可以借助@ControllerAdvice放在@ControllerAdvice所在的类中。

此外,进行该配置GET或POST中的请求参数均生效。

参考资料:https://stackoverflow.com/questions/4617099/spring-3-0-mvc-binding-enums-case-sensitive

 

转载于:https://www.cnblogs.com/z-sm/p/4621261.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
前言&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.2 第1章 ASP简介&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.&hellip;..1 1.1ASP的特点&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.1 1.2ASP的优势&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.2 1.3 ASP与HTML&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.3 1.4 ASP的内置对象&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;..4 1.4.1 Request对象&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.4 1.4.2 Response对象&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;..4 第2章 为什么要开发一个新闻发布系统&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.6 第3章 Access数据库&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;8 3.1 数据库概念&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.8 3.2 Access数据库特点&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.8 3.3
Spring、Spring MVC 和 Spring Boot 都是基于 Java 开发的框架,它们各自有着不同的优势。 Spring框架是一个企业级的开源框架,它提供了一个全面的编程和配置模型,可以帮助开发者快速构建各种类型的应用程序。Spring框架的优势包括: 1. 模块化设计:Spring框架是一个模块化的设计,可以根据需要选择不同的模块来构建应用程序。 2. 松耦合:Spring框架通过依赖注入和面向接口编程等方式,实现了组件之间的松耦合。 3. 事务管理:Spring框架提供了强大的事务管理功能,可以轻松处理数据库事务。 4. AOP支持:Spring框架支持面向切面编程,可以将横切关注点(如日志记录、安全性、事务管理等)从核心业务逻辑中分离出来。 5. 整合其他框架:Spring框架可以与其他框架(如Hibernate、MyBatis等)无缝整合,提供更强大的功能。 Spring MVC框架是Spring框架的一个模块,它是一个基于MVC架构的Web框架,具有以下优势: 1. 简单易用:Spring MVC框架提供了简单易用的API,可以快速构建Web应用程序。 2. 松耦合:Spring MVC框架通过依赖注入和面向接口编程等方式,实现了组件之间的松耦合。 3. 可扩展性:Spring MVC框架提供了灵活的配置选项和可扩展的API,可以满足不同应用程序的需求。 4. 整合其他框架:Spring MVC框架可以与其他框架(如Spring、Hibernate、MyBatis等)无缝整合,提供更强大的功能。 Spring Boot框架是Spring框架的一个扩展,它具有以下优势: 1. 快速开发:Spring Boot框架提供了快速开发的功能,可以快速创建可独立运行的Spring应用程序。 2. 自动配置:Spring Boot框架提供了自动配置的功能,可以根据应用程序的需要自动配置Spring框架。 3. 简化配置:Spring Boot框架通过简化配置,减少了开发者的工作量。 4. 可嵌入性:Spring Boot框架可以将应用程序嵌入到Web容器中,或者作为独立的应用程序运行。 总之,Spring、Spring MVC 和 Spring Boot 都是非常优秀的框架,可以帮助开发者快速构建各种类型的应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值