同步操作将从 Orange_Zh/OZSpringMvcDemo 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
OZSpringDataDemo:(作业2)
实现登录页面(简易版即可),实现登录验证功能、登录之后跳转到列表页,查询出 tb_resume 表【表数据和课上保持一致】的所有数据(列表不要求分页,在列表右上方有“新增”按钮,每一行后面有“编辑”和“删除”按钮,并实现功能),如果未登录就访问url则跳转到登录页面,用户名和密码固定为admin/admin
技术要求:根据SSM整合的思路,进行SSS整合(Spring+SpringMVC+SpringDataJPA),登录验证使用SpringMVC拦截器实现
OZSpringMvcDemo:(作业1)
手写MVC框架,实现简单的Spring的Ioc容器,包含容器初始化,Bean实例化,实现@Service、@Autowired、@Transactional、@Bean、@Value等注解
###作业思路 ####题目1:
1)定义注解@Security(有value属性,接收String数组),该注解用于添加在Controller类或者Handler方法上,表明哪些用户拥有访问该Handler方法的权限(注解配置用户名)
2)访问Handler时,用户名直接以参数名username紧跟在请求的url后面即可,比如http://localhost:8080/demo/handle01?username=zhangsan
3)程序要进行验证,有访问权限则放行,没有访问权限在页面上输出
解决思路:
1、 首先创建@Security注解类
2、 在IOC容器初始化完成后再解析@Controller相关的类,将@Security的值加入到Handler类Set<String> securityUser;中进行保存
3、 当用户发起请求时,判断securityUser是否为空,为空就表示没有权限限制;如果不为空,就检查是否包含此用户,包含则放行
主要代码: com.ozdemo.mvcdemo.framework.servlet.DispatcherServlet#initHandlerMapping
示例链接:
http://localhost:8080/demo/demo1?username=user1
http://localhost:8080/demo/demo1?username=user3
http://localhost:8080/demo/demo1?username=user4
http://localhost:8080/demo/demo2?username=user3
http://localhost:8080/demo/demo2?username=user3
http://localhost:8080/demo/demo2?username=user4
####题目2:
实现登录页面(简易版即可),实现登录验证功能、登录之后跳转到列表页,查询出 tb_resume 表【表数据和课上保持一致】的所有数据(列表不要求分页,在列表右上方有“新增”按钮,每一行后面有“编辑”和“删除”按钮,并实现功能),如果未登录就访问url则跳转到登录页面,用户名和密码固定为admin/admin
技术要求:根据SSM整合的思路,进行SSS整合(Spring+SpringMVC+SpringDataJPA),登录验证使用SpringMVC拦截器实现
解决思路:
1、 首先引入必要的spring-webmvc包
2、 创建登录拦截器并加入到配置文件中
3、 实现resume的增删查改操作
也就是我们常说的web 层。它负责接收客户端请求,向客户端响应结果,通常客户端使⽤http 协议请求web 层,web 需要接收 http 请求,完成 http 响应
表现层包括展示层和控制层:控制层负责接收请求,展示层负责结果的展示
表现层依赖业务层,接收到客户端请求⼀般会调⽤业务层进⾏业务处理,并将处理结果响应给客户端
表现层的设计⼀般都使⽤ MVC 模型(MVC 是表现层的设计模型,和其他层没有关系)
service 层,它负责业务逻辑处理,web 层依赖业务层,但是业务层不依赖 web 层
业务层在业务处理时可能会依赖持久层,如果要对数据持久化需要保证事务⼀致性。(事务应该放到业务层来控制)
dao 层,负责数据持久化,包括数据层即数据库和数据访问层,数据库是对数据进⾏持久化的载体,数据访问层是业务层和持久层交互的接⼝,业务层需要通过数据访问层将数据持久化到数据库中,持久层就是和数据库交互,对数据库表进⾏增删改查的
MVC 全名是 Model View Controller,是 模型(model)-视图(view)-控制器(controller) 的缩写, 是⼀种⽤于设计创建 Web 应⽤程序表现层的模式
MVC 中每个部分各司其职:
MVC提倡:每⼀层只编写⾃⼰的东⻄,不编写任何其他的代码,分层是为了解耦,解耦是为了维护⽅便和分⼯协作
SpringMVC 全名叫 Spring Web MVC,是⼀种基于 Java 的实现 MVC 设计模型的请求驱动类型的轻量级Web 框架,属于 SpringFrameWork 的后续产品
流程说明:
SpringMvc九大组件:
HandlerMapping 是⽤来查找 Handler 的,也就是处理器,具体的表现形式可以是类,也可以是⽅法;⽐如,标注了@RequestMapping的每个⽅法都可以看成是⼀个Handler;Handler负责具体实际的请求处理,在请求到达后,HandlerMapping 的作⽤便是找到请求相应的处理器Handler 和 Interceptor
HandlerAdapter 是⼀个适配器,因为 Spring MVC 中 Handler 可以是任意形式的,只要能处理请求即可,但是把请求交给 Servlet 的时候,由于 Servlet 的⽅法结构都是
doService(HttpServletRequest req,HttpServletResponse resp)形式的,要让固定的 Servlet 处理⽅法调⽤ Handler 来进⾏处理,便是 HandlerAdapter 的职责
HandlerExceptionResolver ⽤于处理 Handler 产⽣的异常情况,它的作⽤是根据异常设置ModelAndView,之后交给渲染⽅法进⾏渲染,渲染⽅法会将 ModelAndView 渲染成⻚⾯
ViewResolver即视图解析器,⽤于将String类型的视图名和Locale解析为View类型的视图,只有⼀个resolveViewName()⽅法;从⽅法的定义可以看出,Controller层返回的String类型视图名viewName 最终会在这⾥被解析成为View;View是⽤来渲染⻚⾯的,也就是说,它会将程序返回的参数和数据填⼊模板中,⽣成html⽂件;
ViewResolver 在这个过程主要完成两件事情:ViewResolver 找到渲染所⽤的模板和所⽤的类型(其实也就是找到视图的类型,如JSP)并填⼊参数;默认情况下,Spring MVC会⾃动为我们配置⼀个InternalResourceViewResolver,是针对 JSP 类型视图的
RequestToViewNameTranslator 组件的作⽤是从请求中获取 ViewName,因为 ViewResolver 根据ViewName 查找 View,但有的 Handler 处理完成之后,没有设置 View,也没有设置ViewName,便要通过这个组件从请求中查找 ViewName
ViewResolver 组件的 resolveViewName ⽅法需要两个参数,⼀个是视图名,⼀个是 Locale,LocaleResolver ⽤于从请求中解析出 Locale,⽐如中国 Locale 是 zh-CN,⽤来表示⼀个区域,这个组件也是 i18n 的基础
ThemeResolver 组件是⽤来解析主题的,主题是样式、图⽚及它们所形成的显示效果的集合;
Spring MVC 中⼀套主题对应⼀个 properties⽂件,⾥⾯存放着与当前主题相关的所有资源,如图⽚、CSS样式等。创建主题⾮常简单,只需准备好资源,然后新建⼀个“主题名.properties”并将资源设置进去,放在classpath下,之后便可以在⻚⾯中使⽤了。SpringMVC中与主题相关的类有ThemeResolver、ThemeSource和Theme。ThemeResolver负责从请求中解析出主题名,ThemeSource根据主题名找到具体的主题,其抽象也就是Theme,可以通过Theme来获取主题和具体的资源。
MultipartResolver ⽤于上传请求,通过将普通的请求包装成 MultipartHttpServletRequest 来实现。
MultipartHttpServletRequest 可以通过 getFile() ⽅法 直接获得⽂件。如果上传多个⽂件,还可以调⽤ getFileMap()⽅法得到Map<FileName,File>这样的结构,MultipartResolver 的作⽤就是封装普通的请求,使其拥有⽂件上传的功能。
FlashMap ⽤于重定向时的参数传递,⽐如在处理⽤户订单时候,为了避免重复提交,可以处理完post请求之后重定向到⼀个get请求,这个get请求可以⽤来显示订单详情之类的信息。这样做虽然可以规避⽤户重新提交订单的问题,但是在这个⻚⾯上要显示订单的信息,这些数据从哪⾥来获得呢?因为重定向时么有传递参数这⼀功能的,如果不想把参数写进URL(不推荐),那么就可以通过FlashMap来传递。只需要在重定向之前将要传递的数据写⼊请求(可以通过ServletRequestAttributes.getRequest()⽅法获得)的属性OUTPUT_FLASH_MAP_ATTRIBUTE中,这样在重定向之后的Handler中Spring就会⾃动将其设置到Model中,在显示订单信息的⻚⾯上就可以直接从Model中获取数据。FlashMapManager 就是⽤来管理 FalshMap 的。
请求流程源码分析
1、进入源码前准备 demo demo目录结构
DemoController
@Controller
@RequestMapping("/demo")
public class DemoController {
@RequestMapping("/handle01")
public String handle01(String name,Map<String,Object> model) {
System.out.println("++++++++handler业务逻辑处理中....");
Date date = new Date();
model.put("date",date);
return "success";
}
}
springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
">
<!--开启controller扫描-->
<context:component-scan base-package="com.ozdemo.test.controller"/>
<!--配置springmvc的视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
2、断点查看请求到Handler的调用栈
由上图我们可以看到整个过程调用的方法栈
3、org.springframework.web.servlet.DispatcherServlet#doDispatch 关建方法,SpringMVC处理请求的流程就在这个方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//1、检查是否是⽂件上传的请求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
//2、获取到能够处理当前请求的执⾏链 HandlerExecutionChain(Handler+拦截器)
//取得处理当前请求的Controller,这⾥也称为Handler,即处理器这⾥并不是直接返回 Controller,⽽是返回 HandlerExecutionChain 请求处理链对象该对象封装了Handler和Interceptor
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 如果 handler 为空,则返回404
noHandlerFound(processedRequest, response);
return;
}
//3、 获取处理请求的处理器适配器 HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 处理 last-modified 请求头
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 4、实际处理器处理请求,返回结果视图对象ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//结果视图对象的处理
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
//5、跳转⻚⾯,渲染视图
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
//最终会调⽤HandlerInterceptor的afterCompletion ⽅法
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
//最终会调⽤HandlerInterceptor的afterCompletion ⽅法
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
4、org.springframework.web.servlet.DispatcherServlet#getHandler 获取到能够处理当前请求的执⾏链 HandlerExecutionChain(Handler+拦截器)
5、org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter 获取处理请求的处理器适配器 HandlerAdapter
6、org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal 实际处理器处理请求,返回结果视图对象ModelAndView
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// 如果需要,在synchronized块中执行invokeHandlerMethod,同步执行
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
//当前session生成一个唯一的锁key
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
//handlerMethod参数适配,并调用目标handler
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// 没有HttpSession可用->不需要互斥
//handlerMethod参数适配,并调用目标handler
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// 不需要同步调用
//handlerMethod参数适配,并调用目标handler
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
7、org.springframework.web.servlet.DispatcherServlet#processDispatchResult 跳转⻚⾯,渲染视图
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// 视图解析器解析出View视图对象
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isTraceEnabled()) {
logger.trace("Rendering view [" + view + "] ");
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
//封装View视图对象之后,调⽤了view对象的render⽅法,渲染数据
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}
@Override
protected View createView(String viewName, Locale locale) throws Exception {
// If this resolver is not supposed to handle the given view,
// return null to pass on to the next resolver in the chain.
if (!canHandle(viewName, locale)) {
return null;
}
// Check for special "redirect:" prefix.
if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
RedirectView view = new RedirectView(redirectUrl,
isRedirectContextRelative(), isRedirectHttp10Compatible());
String[] hosts = getRedirectHosts();
if (hosts != null) {
view.setHosts(hosts);
}
return applyLifecycleMethods(REDIRECT_URL_PREFIX, view);
}
// Check for special "forward:" prefix.
if (viewName.startsWith(FORWARD_URL_PREFIX)) {
String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
InternalResourceView view = new InternalResourceView(forwardUrl);
return applyLifecycleMethods(FORWARD_URL_PREFIX, view);
}
// Else fall back to superclass implementation: calling loadView.
return super.createView(viewName, locale);
}
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
Class<?> viewClass = getViewClass();
Assert.state(viewClass != null, "No view class");
AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(viewClass);
//逻辑视图名解析为物理视图名 加前缀后后缀
view.setUrl(getPrefix() + viewName + getSuffix());
String contentType = getContentType();
if (contentType != null) {
view.setContentType(contentType);
}
view.setRequestContextAttribute(getRequestContextAttribute());
view.setAttributesMap(getAttributesMap());
Boolean exposePathVariables = getExposePathVariables();
if (exposePathVariables != null) {
view.setExposePathVariables(exposePathVariables);
}
Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes();
if (exposeContextBeansAsAttributes != null) {
view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);
}
String[] exposedContextBeanNames = getExposedContextBeanNames();
if (exposedContextBeanNames != null) {
view.setExposedContextBeanNames(exposedContextBeanNames);
}
return view;
}
@Override
public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("View " + formatViewName() +
", model " + (model != null ? model : Collections.emptyMap()) +
(this.staticAttributes.isEmpty() ? "" : ", static attributes " + this.staticAttributes));
}
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
prepareResponse(request, response);
//渲染数据
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
@Override
protected void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 把modelMap中的数据暴露到request域中,这也是为什么后台model.add之后在jsp中可以从请求域取出来的根本原因
exposeModelAsRequestAttributes(model, request);
// 将数据设置到请求域中
exposeHelpers(request);
// Determine the path for the request dispatcher.
String dispatcherPath = prepareForRendering(request, response);
// Obtain a RequestDispatcher for the target resource (typically a JSP).
RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
if (rd == null) {
throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
"]: Check that the corresponding file exists within your web application archive!");
}
// If already included or response already committed, perform include, else forward.
if (useInclude(request, response)) {
response.setContentType(getContentType());
if (logger.isDebugEnabled()) {
logger.debug("Including [" + getUrl() + "]");
}
rd.include(request, response);
}
else {
// Note: The forwarded resource is supposed to determine the content type itself.
if (logger.isDebugEnabled()) {
logger.debug("Forwarding to [" + getUrl() + "]");
}
rd.forward(request, response);
}
}
九⼤组件的初始化 1、org.springframework.web.servlet.DispatcherServlet#onRefresh DispatcherServlet中的onRefresh(),该⽅法中初始化了九⼤组件
@Override
protected void onRefresh(ApplicationContext context) {
//初始化策略
initStrategies(context);
}
2、org.springframework.web.servlet.DispatcherServlet#initStrategies 初始化策略
/**
* 初始化这个servlet使用的策略对象
* <p>May be overridden in subclasses in order to initialize further strategy objects.
*/
protected void initStrategies(ApplicationContext context) {
//初始化多文件上传组件
initMultipartResolver(context);
//初始化本地语言环境
initLocaleResolver(context);
//初始化模板处理器
initThemeResolver(context);
//初始化HandlerMapping
initHandlerMappings(context);
//初始化参数适配器
initHandlerAdapters(context);
//初始化异常拦截器
initHandlerExceptionResolvers(context);
//初始化视图处理器
initRequestToViewNameTranslator(context);
//初始化视图转换器
initViewResolvers(context);
//初始化 FlashMap 管理器
initFlashMapManager(context);
}
首先回顾一下:自定义IOC容器和AOP框架,我们在自定义IOC和AOP的框架上进行扩展开发
1、创建@Controller、@RequestMapping注解类
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
String value() default "";
}
@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
String value() default "";
}
2、创建Handler类,封装handler方法相关的信息
public class Handler {
private Object controller; // method.invoke(obj,)
private Method method;
private Pattern pattern; // spring中url是支持正则的
private Map<String,Integer> paramIndexMapping; // 参数顺序,是为了进行参数绑定,key是参数名,value代表是第几个参数 <name,2>
private Set<String> securityUser; //允许访问的用户 为null代表不限制用户的访问,配置了用户才限制在此范围内的用户访问
public Handler(Object controller, Method method, Pattern pattern,Set<String> securityUser) {
this.controller = controller;
this.method = method;
this.pattern = pattern;
this.paramIndexMapping = new HashMap<>();
this.securityUser = securityUser;
}
public Object getController() {
return controller;
}
public void setController(Object controller) {
this.controller = controller;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public Pattern getPattern() {
return pattern;
}
public void setPattern(Pattern pattern) {
this.pattern = pattern;
}
public Map<String, Integer> getParamIndexMapping() {
return paramIndexMapping;
}
public void setParamIndexMapping(Map<String, Integer> paramIndexMapping) {
this.paramIndexMapping = paramIndexMapping;
}
public Set<String> getSecurityUser() {
return securityUser;
}
public void setSecurityUser(Set<String> securityUser) {
this.securityUser = securityUser;
}
}
3、创建DispatcherServlet类
@WebServlet(name = "ozmvc", urlPatterns = "/*")
public class DispatcherServlet extends HttpServlet {
// handlerMapping
//存储url和Method之间的关联,就是Handler
private List<Handler> handlerMapping = new ArrayList<>();
@Override
public void init(ServletConfig servletConfig) throws ServletException {
initHandlerMapping(servletConfig);
}
/**
* 构造一个HandlerMapping处理器映射器
* 最关键的环节
* 目的:将url和method建立关联
*/
private void initHandlerMapping(ServletConfig servletConfig) {
//存储url和Method之间的映射关系
Map<String, Object> beanMap = BeanFactory.getBeans();
if (beanMap.isEmpty()) {
return;
}
for (Map.Entry<String, Object> entry : beanMap.entrySet()) {
// 获取ioc中当前遍历的对象的class类型
Class<?> clazz = entry.getValue().getClass();
if (!clazz.isAnnotationPresent(Controller.class)) {
continue;
}
String baseUrl = "";
if (clazz.isAnnotationPresent(RequestMapping.class)) {
RequestMapping annotation = clazz.getAnnotation(RequestMapping.class);
baseUrl = annotation.value(); // 等同于/demo
}
//处理@Security
Set<String> securityUserCl = new HashSet<>();
if (clazz.isAnnotationPresent(Security.class)) {
Security annotation = clazz.getAnnotation(Security.class);
String value[] = annotation.value();
securityUserCl.addAll(new HashSet<>(Arrays.asList(value)));
}
// 获取方法
Method[] methods = clazz.getMethods();
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
// 方法没有标识LagouRequestMapping,就不处理
if (!method.isAnnotationPresent(RequestMapping.class)) {
continue;
}
// 如果标识,就处理
RequestMapping annotation = method.getAnnotation(RequestMapping.class);
String methodUrl = annotation.value(); // /query
String url = baseUrl + methodUrl; // 计算出来的url /demo/query
//处理@Security
Set<String> securityUser = new HashSet<>();
if (method.isAnnotationPresent(Security.class)) {
Security securityAnnotation = method.getAnnotation(Security.class);
String[] value = securityAnnotation.value();
securityUser.addAll(new HashSet<>(Arrays.asList(value)));
securityUser.addAll(securityUserCl);
}
// 把method所有信息及url封装为一个Handler
Handler handler = new Handler(entry.getValue(), method, Pattern.compile(url), securityUser);
// 计算方法的参数位置信息 // query(HttpServletRequest request, HttpServletResponse response,String name)
Parameter[] parameters = method.getParameters();
for (int j = 0; j < parameters.length; j++) {
Parameter parameter = parameters[j];
if (parameter.getType() == HttpServletRequest.class || parameter.getType() == HttpServletResponse.class) {
// 如果是request和response对象,那么参数名称写HttpServletRequest和HttpServletResponse
handler.getParamIndexMapping().put(parameter.getType().getSimpleName(), j);
} else {
handler.getParamIndexMapping().put(parameter.getName(), j); // <name,2>
}
}
// 建立url和method之间的映射关系(map缓存起来)
handlerMapping.add(handler);
}
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 根据uri获取到能够处理当前请求的hanlder(从handlermapping中(list))
Handler handler = getHandler(req);
if (handler != null) {
//判断是否允许访问
if (handler.getSecurityUser() != null && handler.getSecurityUser().size() > 0) {
String username = req.getParameter("username");
if (!handler.getSecurityUser().contains(username)) {
resp.getWriter().write("Permission denied;");
return;
}
}
// 参数绑定
// 获取所有参数类型数组,这个数组的长度就是我们最后要传入的args数组的长度
Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
// 根据上述数组长度创建一个新的数组(参数数组,是要传入反射调用的)
Object[] paraValues = new Object[parameterTypes.length];
// 以下就是为了向参数数组中塞值,而且还得保证参数的顺序和方法中形参顺序一致
Map<String, String[]> parameterMap = req.getParameterMap();
// 遍历request中所有参数 (填充除了request,response之外的参数)
for (Map.Entry<String, String[]> param : parameterMap.entrySet()) {
// name=1&name=2 name [1,2]
String value = StringUtils.join(param.getValue(), ","); // 如同 1,2
// 如果参数和方法中的参数匹配上了,填充数据
if (!handler.getParamIndexMapping().containsKey(param.getKey())) {
continue;
}
// 方法形参确实有该参数,找到它的索引位置,对应的把参数值放入paraValues
Integer index = handler.getParamIndexMapping().get(param.getKey());//name在第 2 个位置
paraValues[index] = value; // 把前台传递过来的参数值填充到对应的位置去
}
int requestIndex = handler.getParamIndexMapping().get(HttpServletRequest.class.getSimpleName()); // 0
paraValues[requestIndex] = req;
int responseIndex = handler.getParamIndexMapping().get(HttpServletResponse.class.getSimpleName()); // 1
paraValues[responseIndex] = resp;
// 最终调用handler的method属性
try {
handler.getMethod().invoke(handler.getController(), paraValues);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
private Handler getHandler(HttpServletRequest req) {
if (handlerMapping.isEmpty()) {
return null;
}
String url = req.getRequestURI();
for (Handler handler : handlerMapping) {
Matcher matcher = handler.getPattern().matcher(url);
if (!matcher.matches()) {
continue;
}
return handler;
}
return null;
}
}
4、进行测试
测试相关源码请参见项目源码
项目源码:去看源码
SSM = Spring + SpringMVC + Mybatis = (Spring + Mybatis)+ SpringMVC
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。