【java学习】Spring MVC(Model View Controller)、ApplicationRunner

2024-02-20 12:48

本文主要是介绍【java学习】Spring MVC(Model View Controller)、ApplicationRunner,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1,概念

SpringMVC:
Spring推出的基于Servlet标准的MVC框架实现

1)Spring MVC特性

  1. Spring MVC提供了一种绑定机制(请求参数名称与Java类的属性相匹配即可),通过该机制可以从用户请求中提取数据,然后将数据转换为预定义的数据格式,最后映射到一个模型类,从而创建一个对象。
  2. Spring MVC还是非侵入式的,因为业务逻辑代码与框架本身是分离的。

2,Spring MVC Annotation

注解场景说明备注
@EnableWebMvc在配置类中开启Web MVC的配置支持。
@Controller
@RequestMapping用于映射web请求,包括访问路径和参数。
@ResponseBody支持将返回值放到response内,而不是一个页面,通常用户返回json数据。
@RequestBody允许request的参数在request体中,而不是在直接连接的地址后面。(放在参数前)
@PathVariable用于接收路径参数
@RestController组合注解,相当于@Controller和@ResponseBody的组合,注解在类上,意味着,该Controller的所有方法都默认加上了@ResponseBody。Spring4之后加入的注解
@ControllerAdvice全局异常处理;全局数据绑定;全局数据预处理
@ExceptionHandler用于全局处理控制器里的异常。
@InitBinder用来设置WebDataBinder,WebDataBinder用来自动绑定前台请求参数到Model中。
@ModelAttribute用于定义Controller方法执行之前,对数据模型的操作。
@ResponseStatus在接口使用@ResponseStatus,在处理方法正确执行的前提下,后台返回HTTP响应的状态码为@ResponseStatus指定的状态码,但是浏览器依然可以正常渲染视图(在不使用@ResponseStatus的reson属性情况下)。加在@ExceptionHandler下方,该方法在捕获异常后,后台返回HTTP响应的状态码为@ResponseStatus指定的状态码,但是浏览器依然可以正常渲染视图(在不使用@ResponseStatus的reson属性情况下)。@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = “这是一个异常捕获”)

1)@Controller(控制组件)

用于标记在一个类上,使用它标记的类就是一个springmcv Controller对象,分发处理器将会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping注解。@Controller只是定义了一个控制器类,而使用了@RequestMapping注解的方法才是真正处理请求的处理器。

1>与@RestControlle区别

@Controller:它把用户请求的数据经过业务处理层处理之后封装成一个Model ,然后再把该Model返回给对应的View进行展示,即该注解返回的是一个页面。
@RestControlle:返回的是json数据。

2>跨域:@CrossOrigin

跨域:
请求url的协议、域名、端口三者之间任意一个与当前页面url不同。
浏览器支持的是同源策略,不允许跨域是浏览器基本的安全策略。

@CrossOrigin中的2个参数:

  • origins : 允许可访问的域列表
  • maxAge:准备响应前的缓存持续的最大时间(以秒为单位)。

如果您正在使用Spring Security,请确保在Spring安全级别启用CORS,并允许它利用Spring MVC级别定义的配置。

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.cors().and()...}
}
//为整个controller启用@CrossOrigin
@CrossOrigin(origins = "http://domain2.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {@GetMapping("/{id}")public Account retrieve(@PathVariable Long id) {// ...}
}@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {//为单个方法启用跨域@CrossOrigin(origins = "http://domain2.com")@GetMapping("/{id}")public Account retrieve(@PathVariable Long id) {// ...}@DeleteMapping("/{id}")public void remove(@PathVariable Long id) {// ...}
}

2)@RequestMapping

用于标记在一个方法或类上,用来处理请求地址映射的注解,用于类上,表示类中所有响应请求处理的方法都是以该地址作为父路径,返回值会通过视图解析器解析为实际的物理视图,然后做转发操作。

属性说明备注
value指定请求的实际地址多个url映射:@GetMapping(value = {"/check-list/{type}", "/check-map/{type}"})
method指定请求的method类型
consumes指定处理请求的内容提交类型(Content-Type),例如application/josn,text/html
produces指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回
param指定request中必须包含的参数值
headers指定request中必须包含某些指定的header值,才能让改方法处理请求

spring4.3引入:@GetMapping、@PostMapping、@PutMapping、@DeleteMapping、@PatchMapping,@RequestMapping是他们的父类注解。

org.springframework.web.bind.annotation

@GetMapping相当于@RequestMapping(method = RequestMethod.GET)
@PostMapping相当于@RequestMapping(method = RequestMethod.POST)

同时又引入了三个参数注解:

1>@PathVaribale

对应http访问的路径参数。

@GetMapping(value = "/user/{id}")
public ResponseMessage getUser(@PathVariable Long id){
}

2>@RequestParam

适合参数较少,参数固定的情况。
对应http访问的query参数。

属性说明
value请求参数名(必须配置)
required是否必需,默认为 true,即 请求中必须包含该参数,如果没有包含,将会抛出异常(可选配置)
defaultValue默认值,如果设置了该值,required 将自动设为 false,无论你是否配置了required,配置了什么值,都是 false(可选配置)

3>@RequestBody

org.springframework.web.bind.annotation

适用于复杂的数据结构,比如用xml或者json定义的复杂对象。
对应的是http request payload,数据在http请求的body上,也就是在HttpServletRequest.getInputStream里面。

@PostMapping(value = "")
public ResponseMessage modifyUser(@RequestBody User user){
}

4>@RequestHeader

org.springframework.web.bind.annotation
@RequestMapping("/getSingleHeader")
public Map<String, Object> getSingleHeader(@RequestHeader("user-id") String userId){Map<String, Object> result = new HashMap<>();result.put("code", 0);result.put("msg", "success");result.put("userId", userId);return result;
}//一次性获取所有请求头--MultiValueMap
@RequestMapping("/listHeaders")
public Map<String, Object> listHeaders(@RequestHeader MultiValueMap<String, String> headers) {Map<String, Object> result = new HashMap<>();headers.forEach((key, value) -> {// 日志中输出所有请求头System.out.println(String.format("Header '%s' = %s", key, value));});result.put("code", 0);result.put("msg", "success");result.put("headers", headers);return result;
}//一次性获取所有请求头--HttpHeaders (底层就是MultiValueMap实现的)
@RequestMapping("/getAllHttpHeaders")
public Map<String, Object> getAllHttpHeaders(@RequestHeader HttpHeaders headers) {headers.forEach((key, value) -> {// 日志中输出所有请求头System.out.println(String.format("getAllHttpHeaders '%s' = %s", key, value));});Map<String, Object> result = new HashMap<>();result.put("code", 0);result.put("msg", "success");result.put("headers", headers);return result;
}

5>@CookieValue

作用:用来获取Cookie中的值;

@RequestMapping("/testCookieValue")public String testCookieValue(@CookieValue("JSESSIONID") String sessionId) {System.out.println("JSESSIONID = " + sessionId);return "success";}

6>@SessionAttributes

将值放到session作用域中,写在class上面。

@SessionAttributes(value = {"user"}, types = {String.class})

3)@ModelAttribute

  1. 通常被用来填充一些公共需要的属性或数据;
    比如权限的验证(也可以使用Interceptor)等。
  2. 先执行@ModelAttribute方法,再执行Controller方法。

1>实现

  1. 注释方法并定义模型数据
    在没有@RequestMapping标注的方法上建立模型数据,抽象一个公共BaseController。
abstract public class BaseController{@ModelAttribute("account")public User newUser(@RequestHeader HttpHeaders header){User user = new User();user.setName("myUser");user.setId(1);return user;}
}
  1. 注释参数引用模型数据
@Controller
public class MyController extends BaseController {  @RequestMapping("user")@ResponseBodypublic void getUser(@ModelAttribute("account")User user){System.out.println(user.toString());}
}

4)@Transactional

5)@ControllerAdvice及@ExceptionHandler

@ControllerAdvice就是@Controller 的增强版。@ControllerAdvice主要用来处理全局数据,一般搭配@ExceptionHandler、@ModelAttribute以及@InitBinder使用。

6)@InitBinder

用于在@Controller中标注于方法上,表示为当前控制器注册一个属性编辑器,只对当前的Controller有效。@InitBinder标注的方法必须有一个参数WebDataBinder。所谓的属性编辑器可以理解就是帮助我们完成参数绑定。

		//例如当请求是/test?name=%20zero%20&date=2018-05-22时,会把zero绑定到name,再把时间串格式化为Date类型,再绑定到date。@ResponseBody@RequestMapping(value = "/test")public String test(@RequestParam String name,@RequestParam Date date) throws Exception {System.out.println(name);System.out.println(date);return name;}@InitBinderpublic void initBinder(WebDataBinder binder){//把String类型的参数先trim再绑定binder.registerCustomEditor(String.class,new StringTrimmerEditor(true));//对于Date类型的参数会先格式化在绑定binder.registerCustomEditor(Date.class,new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), false));}

这里的@InitBinder方法只对当前Controller生效,要想全局生效,可以使用@ControllerAdvice。通过@ControllerAdvice可以将对于控制器的全局配置放置在同一个位置,注解了@ControllerAdvice的类的方法可以使用@ExceptionHandler,@InitBinder,@ModelAttribute注解到方法上,这对所有注解了@RequestMapping的控制器内的方法有效。

@ControllerAdvice
public class GlobalControllerAdvice {@InitBinderpublic void initBinder(WebDataBinder binder) {binder.registerCustomEditor(String.class,new StringTrimmerEditor(true));binder.registerCustomEditor(Date.class,new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), false));}
}

3,Dispatcher Servlet(前端控制器)

Dispatcher Servlet是Spring web的核心元素,跟之前的Servlet作用相似,都是用来接收用户请求,然后去后台匹配合适的handler。

Tomcat:
The Apache Tomcat software is an open source implementation of the Java Servlet, JavaServer Pages, Java Expression Language and Java WebSocket technologies.

Servlet:
1.Servlet不是框架,它是java库里面的一个类,Servlet是服务器端运行的一个程序。
2.当web容器启动的时候并且执行的时候,Servlet类就会被初始化。
3.用户通过浏览器输入url时,请求到达Servlet来接收并且根据servlet配置去处理。
通常项目中会用到不同的web容器,我这里用到是比较常见的Tomcat。在eclipse里面创建一个java web项目,会有一个WEB-INF的文件夹,为了不轻易被外界访问到,这个文件夹底下的文件都是受保护的。文件夹中包括了一个很重要的配置文件,web.xml,我们要实现的不同Servlet也要在这里配置才能使用。

1)主要工作流

Controller接受了一个Request,分发给了一个RequestHandler,并返回一个Response对象。
在这里插入图片描述

①前端请求到达DispatcherServlet

②DispatcherServlet请求HandlerMappering(处理器映射) 查找Handler

DispatcherServlet 对请求URL进行解析,得到请求资源标识符(URI),然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括一个Handler处理器对象、多个HandlerInterceptor拦截器对象),最后以HandlerExecutionChain对象的形式返回。

Dispatcher Servlet扫描被@Controller注解的类,从而将web请求映射到被@RequestMapping注解的方法上。
@PathVariable注解相关参数,将一个方法参数绑定到一个url模板。

//@PathVariable用法
@RequestMapping(value = "/test/{id}",method = RequestMethod.DELETE)
public Result test(@PathVariable("id")String id) //@RequestParam用法,注意这里请求后面没有添加参数
@RequestMapping(value = "/test",method = RequestMethod.POST)
public Result test(@RequestParam(value="id",required=false,defaultValue="0")String id) 
注意上面@RequestParam用法当中的参数。

@ExceptionHandler 可以定义方法来处理控制器类中发生的异常。

③DispatcherServlet 执行handler

根据获得的Handler,选择一个合适的HandlerAdapter。提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:

HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
数据转换:对请求消息进行数据转换。如String转换成IntegerDouble等
数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResultError

④Handler执行完成后,返回ModelAndView对象

根据返回的ModelAndView,选择一个适合的ViewResolver返回给DispatcherServlet;
ViewResolver(视图解析器) 结合Model和View,来渲染视图,最后将渲染结果返回给客户端。

⑦DispatcherServlet将模型传递给视图,并在浏览器中展示视图

4,过滤器(Filter)

1)概念

过滤器是JavaWeb的三大组件之一,是实现Filter接口的Java类。

过滤器是实现对请求资源(jsp、servlet、html)的过滤功能,是一个运行在服务器的程序,优先于请求资源(jsp、servlet、html)之前执行。

当浏览器发送请求给服务器的时候,先执⾏过滤器,然后才访问Web的资源。服务器响应Response,从Web资源抵达浏览器之前,也会途径过滤器。

在很多Web开发中,都会用到过滤器(Filter),如参数过滤、防止SQL注入、防止页面攻击、过滤敏感字符、解决网站乱码、空参数矫正、Token验证、Session验证、点击率统计等。

2)场景

针对所有接口做全局过滤。

3)方法

Filter抽象类包含三个方法:

1> init()

该方法在容器启动初始化过滤器时被调用,它在Filter的整个生命周期只会被调用一次,这个方法必须执行成功,否则过滤器会不起作用。

2>doFilter()

容器中的每一次请求都会调用该方法,FilterChain用来调用下一个过滤器Filter。

@Override
public void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException{//请求(request)处理逻辑//请求(request)封装逻辑//chain重新写回request和response
}

其中,参数FilterChain是一个接口,里面又定义了doFilter()方法,这是因为在Java中使⽤了链式结构。把所有的过滤器都放在FilterChain⾥边,如果符合条件,就执⾏下⼀个过滤器(如果没有过滤器了,就执⾏⽬标资源)。

3>destroy()

容器销毁时被调用。一般在方法中销毁或关闭资源,也只会被调用一次。

4>demo

@Slf4j
@Order(1)  //如果有多个Filter,则序号越小,越早被执行
//@Component//无需添加此注解,在启动类添加@ServletComponentScan注解后,会自动将带有@WebFilter的注解进行注入!
//这里的urlPatterns为接口里的路径过滤条件
@WebFilter(filterName = "timeFilter", urlPatterns = "/api/filter/*")
public class TimeFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {log.info("初始化过滤器:{}", filterConfig.getFilterName());}@Overridepublic void destroy() {log.info("销毁过滤器");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {log.info("开始执行");long startTime = System.currentTimeMillis();filterChain.doFilter(servletRequest, servletResponse);long endTime = System.currentTimeMillis();log.info("请求:{},耗时:{}ms", getUrlFrom(servletRequest), (endTime - startTime));log.info("结束执行");}private String getUrlFrom(ServletRequest servletRequest) {if (servletRequest instanceof HttpServletRequest) {return ((HttpServletRequest) servletRequest).getRequestURL().toString();}return "";}
}

在启动类添加一个注解,找到定义的拦截器:

@ServletComponentScan(basePackages = "com.luo.filter")

测试接口:

@RestController
@Slf4j
@RequestMapping("/api/filter")
public class FilterUserController {@GetMapping("/getUserList")public List<String> getUser() {log.info("开始业务逻辑处理。");List<String> list = new ArrayList<>();list.add("张三");list.add("李四");list.add("王五");log.info("业务逻辑处理结束。");return list;}
}

5,拦截器(Interceptor)

1)概念

SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。

拦截器它是链式调用,一个应用中可以同时存在多个拦截器Interceptor,一个请求也可以触发多个拦截器,而每个拦截器的调用会依据它的声明顺序依次执行。

2)场景

3)api

HandlerInterceptor 抽象类;包含三个方法:

1>preHandle()

这个方法将在请求处理之前进行调用。「注意」:如果该方法的返回值为false ,将视为当前请求结束,不仅自身的拦截器会失效,还会导致其他的拦截器也不再执行。

2>postHandle()

只有在 preHandle() 方法返回值为true 时才会执行。会在Controller 中的方法调用之后,DispatcherServlet 返回渲染视图之前被调用。「有意思的是」:postHandle() 方法被调用的顺序跟 preHandle() 是相反的,先声明的拦截器 preHandle() 方法先执行,而postHandle()方法反而会后执行。

3>afterCompletion()

只有在 preHandle() 方法返回值为true 时才会执行,在整个请求结束之后, DispatcherServlet 渲染了对应的视图之后执行。

4)demo

SpringBoot实现一个登录拦截器:用户在访问首页接口,先判断一下session,如果session中有user的信息,说明用户已经登录过了,能正常访问首页接口,否则跳转到登录页面,让用户进行登录。

拦截器类:实现handlerInterceptor接口。

@Component
@Slf4j
public class UserInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//业务拦截相关规则//从session中获取用户的信息InterceptorUserEntity user = (InterceptorUserEntity) request.getSession().getAttribute("user");//判断用户是否登录if (null == user) {response.sendRedirect(request.getContextPath() + "/api/interceptor/login");return false;}//需要返回true,否则请求不会被控制器处理return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {log.info("请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后),如果异常发生,则该方法不会被调用");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {log.info("在整个请求结束之后被调用,也就是在DispatcherServlet渲染了对应的视图之后执行(主要是用于进行资源清理工作)");}
}

使用@Configuration注解写一个拦截器的配置文件:

//.addPathPatterns表示作用范围。(只在这个interceptor下的所有接口进行拦截)
//.excludePathPatterns表示放行。这里把登录页面和已登录完成(setSession)放行。
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {@Autowiredprivate UserInterceptor userInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(userInterceptor).addPathPatterns("/api/interceptor/**").excludePathPatterns("/**/login", "/**/setSession");}
}

测试代码:

@Data
public class InterceptorUserEntity {private Integer id;private String name;
}@RestController
@Slf4j
@RequestMapping("/api/interceptor")
public class InterceptorUserController {@GetMapping("/setSession")@ResponseBodypublic Object setSession(HttpServletRequest request) {//将用户信息存放到session中InterceptorUserEntity user = new InterceptorUserEntity();user.setId(001);user.setName("张三");request.getSession().setAttribute("user", user);return "已进行登录!";}/*** 用户登录后跳转到首页** @return*/@GetMapping("/index")public Object index() {return "这里是首页!";}/*** 登录页面** @return*/@GetMapping("/login")public Object login() {return "请进行登录!";}
}

6,过滤器与拦截器的区别

1)相同点

过滤器与拦截器都体现了AOP的编程思想,都可以实现例如日志、登录鉴权等功能。

2)不同点

①:拦截器是基于java的反射机制(动态代理)的实现,而过滤器是基于函数的回调。
②:拦截器不依赖于servlet容器,而过滤器依赖于servlet容器。
③:拦截器只对Controller请求起作用,而过滤器则可以对几乎所有的请求起作用。
④:拦截器可以访问Controller上下文、值、栈里面的对象,而过滤器不可以。
⑤:在spring容器的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
⑥:拦截器可以获取IOC容器中的各个bean,而过滤器不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。

3)触发机制不同

在这里插入图片描述过滤器Filter是在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后。

拦截器 Interceptor 是在请求进入servlet后,在进入Controller之前进行预处理的,Controller 中渲染了对应的视图之后请求结束。

过滤器几乎可以对所有进入容器的请求起作用,而拦截器只会对Controller中请求或访问static目录下的资源请求起作用。

7,ApplicationRunner

@FunctionalInterface
public interface ApplicationRunner {void run(ApplicationArguments args) throws Exception;
}
  1. spring程序启动后,run方法会被自动调用。
  2. 执行顺序:
  3. 场景:初始化操作。
    实例化Kafka客户端,异步加载缓存。

1)使用


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;@Component
public class StartRegisterSchedule implements ApplicationRunner {@AutowiredRestTemplate restTemplate;@Overridepublic void run(ApplicationArguments args) {// 获取命令行参数String[] sourceArgs = args.getSourceArgs();List<String> nonOptionArgs = args.getNonOptionArgs();Set<String> optionNames = args.getOptionNames();}
}

2)问题

  1. ApplicationRunner没有调用?
    可能其他启动任务非异步调用被阻塞了。

8,幂等性

1>概念

某个操作,任意多次执行所产生的影响均与一次执行的影响相同。
举例:多次支付同一笔订单。

2>场景及处理方式

  1. 前端未做限制,导致用户重复提交
  2. 使用浏览器后退,或者按F5刷新,或者使用历史记录,重复提交表单
  3. 网络波动,引起重复请求
  4. 超时重试,引起接口重复调用
  5. 定时任务设置不合理,导致数据重复处理
  6. 使用消息队列时,消息重复消费

4>解决方案

  1. 前端重定向
    页面重定向(PRG),当用户进行表单提交时,会重定向到另外一个提交成功页面,而不是停留在原先的表单页面。这样就避免了用户刷新导致重复提交。同时防止了通过浏览器按钮前进/后退导致表单重复提交。
  2. 后端一锁二判三更新
    约定一个唯一ID作为幂等号。
    如主键、操作流水、状态机等。
    另外,在返回参数中标记是因为幂等成功(responseCode=DUPLICATED)。
    1)加锁
    对幂等号加锁。
    用redis分布式锁:非阻塞高效互斥锁。
    2)状态校验
    基于状态机、流水表、唯一索引等等进行重复操作的判断。
    3)更新时将数据进行持久化。
    数据库要定义好唯一约束,控制脏数据入库。

这篇关于【java学习】Spring MVC(Model View Controller)、ApplicationRunner的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/728262

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06