目录
实现过程思路分析
1、创建Maven工程
2、创建控制层、业务层代码(Controller、Service、自定义注解)、准备SpringMvc核心配置文件
3、准备前端控制器 ,创建一个Servlet,同时在web.xml文件中声明该前端控制器
4、创建Spring容器,通过DOM4J解析springmvc的XML文件
5、扫描springmvc中的控制器以及service类并实例化对象放入容器中
6、实现容器中对象的注入,比如将服务层对象注入至控制层
7、建立请求映射地址与控制器以及方法之间的映射关系
8、接收用户请求并进行分发操作
9、用户请求参数处理
10、控制器方法调用以及不同返回值数据处理
作业:封装用户请求参数
Springmvc核心配置文件:springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<!-- 配置创建容器时要扫描的包-->
<component-scan base-package="com.baiqi.controller,com.baiqi.service"></component-scan>
</beans>
web项目核心配置文件:web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--配置前端控制器-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>com.springmvc.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--Web服务器一旦启动,Servlet就会实例化创建对象,然后初始化(预备创建对象)-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
自定义注解
Controller.java
package cn.huangyan.spring.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Controller {
String value() default "";
}
Service.java
package cn.huangyan.spring.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Service {
String value() default "";
}
Autowired.java
package cn.huangyan.spring.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Autowired {
String value() default "";
}
RequestMapping.java
package cn.huangyan.spring.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Retention(RetentionPolicy.RUNTIME) //
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface RequestMapping {
String value() default "";
}
注意:
- ElementType.TYPE表示定义的这个注解以后可以用在类上;
- ElementType.METHOD表示这个注解可以用在方法上;
- @Retention(RetentionPolicy.RUNTIME) 表示保留到运行时,运行时能通过反射获取到注解的相关信息
中央控制器DispatcherServlet
package com.springmvc.servlet;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.springmvc.annotation.Controller;
import com.springmvc.annotation.RequestMapping;
import com.springmvc.annotation.ResponseBody;
import com.springmvc.context.WebApplicationContext;
import com.springmvc.handler.MyHandler;
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;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class DispatcherServlet extends HttpServlet {
//指定SpringMvc容器
private WebApplicationContext webApplicationContext;
//创建集合 用于存放 映射关系 映射地址 与 控制器.方法,用于发送请求直接从该集合中进行匹配
List<MyHandler> handList = new ArrayList<>();
@Override
public void init() throws ServletException {
//1、加载初始化参数 classpath:springmvc.xml
String contextConfigLocation = this.getServletConfig().getInitParameter("contextConfigLocation");
//2、创建Springmvc容器
webApplicationContext = new WebApplicationContext(contextConfigLocation);
//3、进行初始化操作
webApplicationContext.onRefresh();
//4、初始化请求映射关系 /findUser ===》控制器.方法
initHandlerMapping();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//进行请求分发处理
doDispatcher(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
//进行请求分发处理
public void doDispatcher(HttpServletRequest req, HttpServletResponse resp){
//根据用户的请求地址 /findUser 查找Handler|Controller
MyHandler myHandler = getHandler(req);
try{
if(myHandler == null){
resp.getWriter().print("<h1>404 NOT FOUND!</h1>");
}else{
//调用处理方法之前 进行参数的注入
//调用目标方法
Object result = myHandler.getMethod().invoke(myHandler.getController());
if(result instanceof String){
//跳转JSP
String viewName=(String)result;
// forward:/success.jsp
if(viewName.contains(":")){
String viewType=viewName.split(":")[0];
String viewPage=viewName.split(":")[1];
if(viewType.equals("forward")){
req.getRequestDispatcher(viewPage).forward(req,resp);
}else{
// redirect:/user.jsp
resp.sendRedirect(viewPage);
}
}else{
//默认就转发
req.getRequestDispatcher(viewName).forward(req,resp);
}
}else{
//返回JSON格式数据
Method method = myHandler.getMethod();
if(method.isAnnotationPresent(ResponseBody.class)){
//将返回值转换成 json格式数据
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(result);
resp.setContentType("text/html;charset=utf-8");
PrintWriter writer = resp.getWriter();
writer.print(json);
writer.flush();
writer.close();
}
}
}
}catch(Exception e){
e.printStackTrace();
}
}
//根据用户请求查找对应的Handler
/***
* 获取请求对应的handler
* @param req
* @return
*/
public MyHandler getHandler(HttpServletRequest req) {
// /findUser
String requestURI = req.getRequestURI();
for (MyHandler myHandler : handList) {
//从容器的Handle取出URL 和 用户的请求地址进行匹配,找到满足条件的Handler
if (myHandler.getUrl().equals(requestURI)) {
return myHandler;
}
}
return null;
}
//初始化请求映射关系
public void initHandlerMapping(){
for (Map.Entry<String, Object> entry : webApplicationContext.iocMap.entrySet()) {
//获取bean的class类型
Class<?> clazz = entry.getValue().getClass();
if(clazz.isAnnotationPresent(Controller.class)){
//获取bean中所有的方法,为这些方法建立映射关系
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
if(method.isAnnotationPresent(RequestMapping.class)){
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
//获取注解中的值 /findUser
String url = requestMapping.value();
//建立 映射地址 与 控制器.方法
MyHandler myHandler = new MyHandler(url,entry.getValue(),method);
handList.add(myHandler);
}
}
}
}
}
}
package com.springmvc.context;
import com.springmvc.annotation.AutoWired;
import com.springmvc.annotation.Controller;
import com.springmvc.annotation.Service;
import com.springmvc.xml.XmlPaser;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* springMvc容器
*/
public class WebApplicationContext {
//classpath:springmvc.xml
String contextConfigLocation;
//定义集合 用于存放 bean 的权限名|包名.类名
List<String> classNameList = new ArrayList<String>();
//创建Map集合用于扮演IOC容器: key存放bean的名字 value存放bean实例
public Map<String,Object> iocMap = new ConcurrentHashMap<>();
public WebApplicationContext() {
}
public WebApplicationContext(String contextConfigLocation) {
this.contextConfigLocation = contextConfigLocation;
}
/**
* 初始化Spring容器
*/
public void onRefresh(){
//1、进行解析springmvc配置文件操作 ==》 com.baiqi.controller,com.baiqi.service
String pack = XmlPaser.getbasePackage(contextConfigLocation.split(":")[1]);
String[] packs = pack.split(",");
//2、进行包扫描
for(String pa : packs){
excuteScanPackage(pa);
}
//3、实例化容器中bean
executeInstance();
//4、进行 自动注入操作
executeAutoWired();
}
//进行自动注入操作
public void executeAutoWired(){
try {
//从容器中 取出 bean ,然后判断 bean中是否有属性上使用 AutoWired,如果使用了搞注解,就需要进行自动注入操作
for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
//获取容器中的bean
Object bean = entry.getValue();
//获取bean中的属性
Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
if(field.isAnnotationPresent(AutoWired.class)){
//获取注解中的value值|该值就是bean的name
AutoWired autoWiredAno = field.getAnnotation(AutoWired.class);
String beanName = autoWiredAno.value();
//取消检查机制
field.setAccessible(true);
field.set(bean,iocMap.get(beanName));
}
}
}
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 实例化容器中的bean
*/
public void executeInstance(){
try{
// com.baiqi.controller.UserController com.baiqi.service.impl.UserServiceImpl
for (String className : classNameList) {
Class<?> clazz = Class.forName(className);
if(clazz.isAnnotationPresent(Controller.class)){
//控制层 bean
String beanName = clazz.getSimpleName().substring(0,1).toLowerCase()+ clazz.getSimpleName().substring(1);
iocMap.put(beanName,clazz.newInstance());
}else if(clazz.isAnnotationPresent(Service.class)){
//Service层 bean
Service serviceAn = clazz.getAnnotation(Service.class);
String beanName = serviceAn.value();
iocMap.put(beanName,clazz.newInstance());
}
}
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 扫描包
*/
public void excuteScanPackage(String pack){
// com.baiqi.controller ==> com/baiqi/controller
URL url = this.getClass().getClassLoader().getResource("/" + pack.replaceAll("\\.", "/"));
String path = url.getFile();
// /com/bruce/service
File dir=new File(path);
for(File f:dir.listFiles()){
if(f.isDirectory()){
//当前是一个文件目录 com.baiqi.service.impl
excuteScanPackage(pack+"."+f.getName());
}else{
//文件目录下文件 获取全路径 UserController.class ==> com.baiqi.controller.UserController
String className=pack+"."+f.getName().replaceAll(".class","");
classNameList.add(className);
}
}
}
}
URL url = getClass().getClassLoader().getResource(path); // 注意path最前面没有/,path = cn/huangyan/spring
String filePath = url.getFile();
File[] files = new File(filePath).listFiles();
package com.springmvc.xml;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
/**
* @BelongsProject: SpringMvc
* @Description: 解析springmvc.xml
*/
public class XmlPaser {
public static String getbasePackage(String xml){
try {
SAXReader saxReader=new SAXReader();
InputStream inputStream = XmlPaser.class.getClassLoader().getResourceAsStream(xml);
//XML文档对象
Document document = saxReader.read(inputStream);
Element rootElement = document.getRootElement();
Element componentScan = rootElement.element("component-scan");
Attribute attribute = componentScan.attribute("base-package");
String basePackage = attribute.getText();
return basePackage;
} catch (DocumentException e) {
e.printStackTrace();
} finally {
}
return "";
}
}
用于存储url和处理器的映射关系的基础组件
package com.springmvc.handler;
import java.lang.reflect.Method;
/**
* @BelongsProject: SpringMvc
* @Description: TODO
*/
public class MyHandler {
//请求URL地址
private String url;
//后台控制器
private Object controller;
//控制器中指定的方法
private Method method;
public MyHandler() {
super();
// TODO Auto-generated constructor stub
}
public MyHandler(String url, Object controller, Method method) {
super();
this.url = url;
this.controller = controller;
this.method = method;
}
// 省略getter/setter方法
}
总结
- 学习如何通过包名,扫描定位到包名下的具体一个个的类文件,并对这些类文件进行解析
- 学习如何通过反射获取字段的注解属性值,并通过反射给bean字段设值值