手写springMVC
参考视频:https://www.bilibili.com/video/BV18K4y1v7f7
代码可以参考本博客,具体编写还是参考视频
本文最后有本人手写springMVC后的代码下载
编写springmvc的目的:
- 对springMVC原理有进一步的了解
- 深化java se的高级知识点(注解,反射,xml解析)
- 通过自定义框架来深化java内功,提高面试通过率
- 养成规范化,标准化的代码编写习惯和良好的技术文档习惯
一,整备工作
1,Tomcat启动时加载SpringMVC开发的流程是什么?
启动阶段:
- Tomcatxx加载xxx.war (springmvc: com.xx.xxx.OrderService.class)
- 创建容器:创建Map iocMap = new HashMap();
- ScanbasePackage::扫描war下的@Controller,@Service注解的类
- 实例化:将扫描到的类通过反射实例化,并存入到iocMap容器中
- 依赖注入:将存在依赖的bean进入注入
- UrlMapping :http请求路径与Method建立映射关系
运行阶段:
- 发送http请求,调用servlet的doGet/doPost方法
- 找到从UrlMapping中找到对应的Method方法对象;
- 找到Method方法对象后,直接调用
- 响应返回结果
2,mvc模式回顾:
MVC( 模型-视图-控制器)设计创建 Web 应用程序的模式::
- Model(模型)是应用程序中用于处理应用程序数据逻辑的部分(dao和service层),通常模型对象负责在数据库中存取数据。
- View(视图)是应用程序中处理数据显示的部分(jsp和HTML页面),通常视图是依据模型数据创建的。
- Controller(控制器)是应用程序中处理用户交互的部分(controller层),通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。
优点:
- MVC 分层有助于管理复杂的应用程序,因为您可以在一个时间内专门关注一个方面。例如,您可以在不依赖业务逻辑的情况下专注于视图设计。同时也让应用程序的测试更加容易。
- MVC 分层同时也简化了分组开发。不同的开发人员可同时开发视图、控制器逻辑和业务逻辑。
3,SpringMVC执行的流程
一个请求匹配前端控制器 DispatcherServlet 的请求映射路径(在 web.xml中指定), WEB 容器将该请求转交给 DispatcherServlet 处理
DispatcherServlet 接收到请求后, 将根据 请求信息 交给 处理器映射器 (HandlerMapping)
HandlerMapping 根据用户的url请求 查找匹配该url的 Handler,并返回一个执行链
DispatcherServlet 再请求 处理器适配器(HandlerAdapter) 调用相应的 Handler(controller) 进行处理并返回 ModelAndView 给 DispatcherServlet
DispatcherServlet 将 ModelAndView 请求 ViewReslover(视图解析器)解析,返回具体 View
DispatcherServlet 对 View 进行渲染视图(即将模型数据填充至视图中)
DispatcherServlet 将页面响应给用户
二,开始编写
1,创建一个maven工程。导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>SpringMVC</groupId>
<artifactId>SpringMVC</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>SpringMVC Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>10.0.1</maven.compiler.source>
<maven.compiler.target>10.0.1</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--springMvc是对servlet的进一步封装-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<!--需要对配置文件进行解析-->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<!-- 导入一些工具类-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
<!--为了不写get set方法引入lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.18</version>
<scope>provided</scope>
</dependency>
<!--使用RequestBody注解时,需要把字符串bean对象转成json集合-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.1</version>
</dependency>
</dependencies>
<build>
<finalName>SpringMVC</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>10.0.1</source>
<target>10.0.1</target>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
2,完善目录结构,
- web.xml文件//用来告诉tomcat服务器,启动服务器时要调用前端控制器的位置,和springmvc.xml文件的位置,一些初始化操作,所有访问tomcat的请求都要经过前端控制器
- springmvc.xml文件//配置前端控制器要扫描的包,该包下的对象要注入到spring的ioc容器中
- pojo层:user类 //用来存放数据
- service层:UserService接口 UserServiceImpl实现类 //用来写业务功能
- controller层:UserController类 //用来处理请求
- 注解接口:@service @controller @RequestMapping @RequestParam @AutoWired//这些注解只是一个标记的作用,存储value参数的作用
- 前端控制器:DispatcherServlet类
- Spring容器:WebApplicationContext类
- 处理器类:MyHandler类 //用来存放 (请求url,对应控制器对象controller,处理url请求的方法method)
- xml文件解析类:XMLParse类//用来对springmvc.xml文件进行解析,得到要扫描的包
- 自定义异常:ContextException类//包扫描不到自定义抛出自定义异常提醒自己
3,具体代码的编写和解释
1,web.xml文件:用来告诉tomcat服务器,启动服务器时要调用前端控制器的位置,和springmvc.xml文件的位置,一些初始化操作,所有访问tomcat的请求都要经过前端控制器
<!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>
<!-- 把我们刚刚编写的DispatcherServlet进行配置,告诉服务器它的位置-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>com.springMVC.servlet.DispatcherServlet</servlet-class>
<init-param>
<!-- springmvc的配置文件-->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--web服务器一旦启动,servlet就实例化创建对象,进行初始化-->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 我们访问的任何请求都会进入DispatcherServlet-->
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
2,springmvc.xml文件:配置前端控制器要扫描的包,该包下的对象要注入到spring的ioc容器中
<?xml version="1.0" encoding="utf-8" ?>
<beans>
<!-- 开启使用注解类扫描的jar包-->
<component-scan base-package="com.bruce.service,com.bruce.controller"></component-scan>
</beans>
3,pojo层:user类,用来存放数据
package com.bruce.POJO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String name;
private String password;
}
4,service层:UserService接口 UserServiceImpl实现类 //用来写业务功能
UserService接口:
package com.bruce.service;
import com.bruce.POJO.User;
import java.util.List;
public interface UserService {
List<User> findUser(String name);
String getUserMassage(String name);
}
UserServiceImpl实现类:
package com.bruce.service;
import com.bruce.POJO.User;
import com.springMVC.annotation.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Override
public List<User> findUser(String name) {
System.out.println("查询的参数是:"+name);
//模拟一些数据
List<User> users=new ArrayList<User>();
users.add(new User(1,"建江","123"));
users.add(new User(2,"小米","111"));
return users;
}
@Override
public String getUserMassage(String name) {
return "我是getUserMassage方法: "+name;
}
}
5,controller层:UserController类 //用来处理请求
package com.bruce.controller;
import com.bruce.POJO.User;
import com.bruce.service.UserService;
import com.bruce.service.UserServiceImpl;
import com.springMVC.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
@Controller
public class UserController {
@AutoWired
private UserServiceImpl userServiceImpl;
@RequestMapping("/user/query")
public void findUser(HttpServletRequest request, HttpServletResponse response,
String name){
System.out.println(userServiceImpl);
response.setContentType("text/html;charset=utf-8");//处理响应的中文乱码
List<User> users = userServiceImpl.findUser(name);
try {
PrintWriter out = response.getWriter();
out.print("<h1>SpringMVC容器"+name+"</h1>");
} catch (IOException e) {
e.printStackTrace();
}finally {
System.out.println("我执行了");
}
}
/**
* 返回到一个新的页面
* @param request
* @param response
* @param name
* @return
*/
@RequestMapping("/user/query1")
public String findUser1(HttpServletRequest request, HttpServletResponse response, String name){
response.setContentType("text/html;charset=utf-8");//处理响应的中文乱码
String userMassage = userServiceImpl.getUserMassage(name);
request.setAttribute("userMassage",userMassage);
//转发到user.jsp
return "forward:/user.jsp";
}
/**
* 返回一个json数据
*/
@RequestMapping("/user/query2")
@RequestBody
public List<User> findUser2(HttpServletRequest request, HttpServletResponse response, String name){
List<User> user = userServiceImpl.findUser(name);
return user;
}
}
6,注解接口:@service @controller @RequestMapping @RequestParam @AutoWired//这些注解只是一个标记的作用,存储value参数的作用
@service:(创建过程和从句接口过程类似,选择创建注解Annotation)
package com.springMVC.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.*;
/**
* 自定义注解
*/
@Target(ElementType.TYPE)//元注解。TYPE表示该这个自定义注解的作用范围是在类上
@Retention(RetentionPolicy.RUNTIME)//表示运行时这个注解起作用
@Documented//生成文档注释,可以不用加
public @interface Service{
String value() default "";//添加service注解的参数value
}
@controller:
package com.springMVC.annotation;
import java.lang.annotation.*;
/**
* 自定义注解
*/
@Target(ElementType.TYPE)//元注解。TYPE表示该这个自定义注解的作用范围是在类上
@Retention(RetentionPolicy.RUNTIME)//表示运行时这个注解起作用
@Documented//生成文档注释,可以不用加
public @interface Controller {
String value() default "";//添加service注解的参数value
}
@RequestMapping:
package com.springMVC.annotation;
import java.lang.annotation.*;
/**
* 自定义注解
*/
@Target(ElementType.METHOD)//元注解。METHOD表示该这个自定义注解的作用范围是在方法上
@Retention(RetentionPolicy.RUNTIME)//表示运行时这个注解起作用
@Documented//生成文档注释,可以不用加
public @interface RequestMapping {
String value() default "";//添加service注解的参数value
}
@RequestParam:
package com.springMVC.annotation;
import java.lang.annotation.*;
/**
* 自定义注解
*/
@Target(ElementType.PARAMETER)//元注解。PARAMETER表示该这个自定义注解的作用范围是在参数上
@Retention(RetentionPolicy.RUNTIME)//表示运行时这个注解起作用
@Documented//生成文档注释,可以不用加
public @interface RequestParam {
String value() default "";//添加service注解的参数value
}
@AutoWired
package com.springMVC.annotation;
import java.lang.annotation.*;
/**
* 自定义注解
*/
@Target(ElementType.FIELD)//元注解。FIELD表示该这个自定义注解的作用范围是在字段上
@Retention(RetentionPolicy.RUNTIME)//表示运行时这个注解起作用
@Documented//生成文档注释,可以不用加
public @interface AutoWired {
String value() default "";//添加service注解的参数value
}
7,前端控制器:DispatcherServlet类:这个类功能比较复杂(做好准备)
注解:执行的流程
/**
* 初始化:
* 去web.xml文件中得到springmvc.xml配置文件的名字,传给spring容器,进行初始化
* spring容器初始化过程:
* spring容器会调用xml解析器,对springmvc.xml文件进行解析,得到要扫描的包,把包下面的java文件名存到classNameList中
* 遍历classNameList中的每个类,判断他们是否有@controller和@Service注解,如果有,就把它们的该类的名字,和实例化对象存到iocMap中
* 遍历IocMap集合中的实例化对象,得到它的属性集合,遍历属性,判断属性上是否有@AutoWired注解,如果有就获取它的类型名
* 根据类型名去IOCMap容器找到对应的实例化对象,进行赋值(对象的注入)
*
* 初始化请求映射:
* 如IOCMap容器中得到实例化的Controller类
* 获取到这个类的所有方法,遍历方法集合
* 判断方法上是否有RequestMapping注解,如果有就得到它的value值也就是请求路径url
* 把url,method,controller三个传给Handler进行初始化(记录三者的关系,由此可以根据请求路径定位到该方法)
* 把Handler存到handlerList
*
* 处理请求:
* 根据url来获取对应的处理器类,判断它是否为空,为空报404
* 不为空,根据Handler找到该url要调用的方法,遍历方法的参数,把参数存到数组Params中,
* 存储参数时需要进行判断,是HttpServletRequest类型的还是HttpServletResponse类型的,存放在对应位置
* 如果是String类型的,进需要判断请求参数,与方法参数是否一样,如果一样才能调用方法,还需要找到有RequestParam注解
* 判断它的value值是否与请求参数的名称一致,不一致不能调用处理请求的方法。
*
* 判断返回值类型是不是String类型的
* 如果是String类型,可能要跳转的jsp页面
* 如果该方法上有RequestBody注解,返回的是json集合
* 需要使用Jackson工具类,对bean对象进行转化成json类型后,以输出流的形式发送到浏览器
*
* @throws ServletException
*/
package com.springMVC.servlet;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.springMVC.annotation.Controller;
import com.springMVC.annotation.RequestBody;
import com.springMVC.annotation.RequestMapping;
import com.springMVC.annotation.RequestParam;
import com.springMVC.context.WebApplicationContext;
import com.springMVC.exception.ContextException;
import com.springMVC.handler.MyHandler;
import javax.servlet.ServletException;
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.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* DispatcherServlet是SpringMVC的核心控制器
*/
public class DispatcherServlet extends HttpServlet {
private WebApplicationContext webApplicationContext;
//存储url和对象方法的映射关系
List<MyHandler> handlerList=new ArrayList<MyHandler>();
//需要重写HttpServlet的两个方法
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//super.doGet(req, resp);
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//super.doPost(req, resp);
//请求的分发处理
executeDispatch(req,resp);
}
/**
* 初始化
* @throws ServletException
*/
@Override
public void init() throws ServletException {
//1,Servlet初始化的时候,去web.xml文件读取初始化的参数contextConfigLocation的值:classpath:springmvc.xml
String contextConfigLocation = this.getServletConfig().getInitParameter("contextConfigLocation");
//2,创建spring容器:将值classpath:springmvc.xml传进去,创建spring的容器
webApplicationContext=new WebApplicationContext(contextConfigLocation);
//初始化spring容器
webApplicationContext.refresh();
//4,初始化请求映射:/user/query------》Controller----->method-----param
initHandlerMapping();
System.out.println("请求地址和控制器方法的映射关系:"+handlerList);
//super.init();
}
/**
* 初始化请求映射
*/
void initHandlerMapping(){
//判断iocMap中是否有bean对象
if(webApplicationContext.iocMap.isEmpty()){
throw new ContextException("spring容器为空");
}
for (Map.Entry<String,Object> entry:webApplicationContext.iocMap.entrySet()){
Class<?> clazz =entry.getValue().getClass();
if (clazz.isAnnotationPresent(Controller.class)){
Method[] methods = clazz.getDeclaredMethods();
for (Method method:methods){
if (method.isAnnotationPresent(RequestMapping.class)){
RequestMapping requestMappingAnnotation = method.getAnnotation(RequestMapping.class);
String url = requestMappingAnnotation.value();
MyHandler myHandler=new MyHandler(url,entry.getValue(),method);
handlerList.add(myHandler);
}
}
}
}
}
/**
* 请求分发
*/
void executeDispatch(HttpServletRequest req, HttpServletResponse resp){
MyHandler handler = getHandler(req);
try {
if (handler==null){
resp.getWriter().print("<h1> 404 not found</h1>");
}else {
Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
Object []params=new Object[parameterTypes.length];//定义一个参数数组,来存放请求的参数
for (int i=0;i<parameterTypes.length;i++){
Class<?> parameterType=parameterTypes[i];
if ("HttpServletRequest".equals(parameterType.getSimpleName())){
params[i]=req;
}else if ("HttpServletResponse".equals(parameterType.getSimpleName())){
params[i]=resp;
}
}
//获取请求中的参数集合
Map<String,String[]> parameterMap = req.getParameterMap();
for (Map.Entry<String,String []> entry:parameterMap.entrySet()){
String value = entry.getValue()[0];
String name = entry.getKey();
//写死了,默认第三个参数是name
System.out.println("请求的参数是:"+name+"---->"+value);
int index = hasRequestParam(handler.getMethod(), name);//把该处理器的方法,和参数key传进去
if (index!=-1){//如果找到下标
params[index]=value;
}else {
List<String> names = getParameterNames(handler.getMethod());
System.out.println(names);//[request, response, name]
for(int i=0;i<names.size();i++){
if (name.equals(names.get(i))){//如果请求中的参数和方法中参数列表中的名字相同就进行赋值
params[i]=value;
break;
}
}
}
//params[2] =value;
}
//调用控制器中的方法,得到它的返回值用来转发到对应的页面
Object result = handler.getMethod().invoke(handler.getController(), params);
if (result instanceof String){
//跳转到jsp页面
String viewName=(String)result;
if (viewName.contains(":")){//说明是forward:/user.jsp语法
String viewType=viewName.split(":")[0];
String viewPage=viewName.split(":")[1];
if (viewType.equals("forward")){
//转发
req.getRequestDispatcher(viewPage).forward(req,resp);
}else {
//重定向
resp.sendRedirect(viewPage);
}
}else {
//默认是转发
req.getRequestDispatcher(viewName).forward(req,resp);
}
}
else {
//返回json数据
Method method=handler.getMethod();
if (method.isAnnotationPresent(RequestBody.class)){
//调用返回值用Json转化的工具类,把它转化成json字符串
ObjectMapper objectMapper=new ObjectMapper();
String json = objectMapper.writeValueAsString(result);
resp.setContentType("text/html;charset=utf-8");//返回数据解决中文乱码
PrintWriter out = resp.getWriter();//把response请求写入输出流
out.print(json);//将json数据显示在浏览器
out.flush();//情况流中的数据
out.close();//关闭输出流
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取请求对应的控制器Handler
*/
MyHandler getHandler(HttpServletRequest req){
// /user/query路径
String requestURI = req.getRequestURI();
for (MyHandler handler:handlerList){//遍历处理器列表,对比有没有相同url的处理器,有就返回
if (handler.getUrl().equals(requestURI)){
return handler;
}
}
return null;
}
/**
* 判断控制器的参数是否有,RequestMapping注解,并且比较对应的value值是否相等,如果相等返回下标
*/
public int hasRequestParam(Method method,String name){
Parameter[] parameters = method.getParameters();
for (int i=0;i<parameters.length;i++){
Parameter parameter=parameters[i];
boolean b = parameter.isAnnotationPresent(RequestParam.class);//参数是否有RequestParam注解
if (b){
RequestParam requestParam = parameter.getAnnotation(RequestParam.class);//得到RequestParam注解,判断value值
String value = requestParam.value();
if (name.equals(value)){//如果传入的参数name和value值相同就返回参数的下标位置
return i;
}
}
}
return -1;//没有找到
}
/**
* 获取参数的名字列表,需要在pom.xml文件配置maven编译插件,依赖于jdk1.8
*/
public List<String> getParameterNames(Method method){
List<String> list=new ArrayList<String>();//存放参数
Parameter[] parameters = method.getParameters();
for (Parameter parameter:parameters){
list.add(parameter.getName());
}
return list;
}
}
8,Spring容器:WebApplicationContext类
package com.springMVC.context;
import com.springMVC.annotation.AutoWired;
import com.springMVC.annotation.Controller;
import com.springMVC.annotation.Service;
import com.springMVC.exception.ContextException;
import com.xml.XmlParser;
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;
/**
* spring容器
*/
public class WebApplicationContext {
private String contextConfigLocation;//内容是classpath:springmvc.xml
List<String> classNameList=new ArrayList<String>();//存放要扫描的包
//spring的ioc容器,用来存放bean对象
public Map<String,Object> iocMap=new ConcurrentHashMap<String,Object>();
public WebApplicationContext(String contextConfigLocation) {
this.contextConfigLocation = contextConfigLocation;
}
/**
* 初始化spring容器
*/
public void refresh(){
//1,解析spring.xml文件,使用dom4j
//把springmvc.xml传到getBasePackage方法中,该方法会实例化springmvc.xml对象,把对应要扫描包的值取出来
//使用basePackage的值是com.bruce.service,com.bruce.controller
String basePackage = XmlParser.getBasePackage(contextConfigLocation.split(":")[1]);
String []basePackages=basePackage.split(",");//把它们分割开来
if (basePackage.length()>0){//如果有值,分割里面的报,进行初始化
for (String pack:basePackages){
executeScanPackage(pack);//这个方法在下面,获取包的路径存到list中
}
}
System.out.println("扫描后内容是:"+classNameList);
//实例化spring容器中的bean
executeInstance();
System.out.println("IOCMap容器中的对象"+iocMap);
//实现spring容器中对象的注入
executeAutoWired();
}
/**
* 根据配置文件中的路径信息,扫描对应的包,把这些包的路径存到list集合中
*/
public void executeScanPackage(String pack){
URL url = this.getClass().getClassLoader().getResource("/" + pack.replaceAll("\\.", "/"));
String path=url.getFile();
File dir=new File(path);
for (File f:dir.listFiles()){
if (f.isDirectory()){
//是一个文件目录
executeScanPackage(pack+"."+f.getName());//继续进入这个文件夹中寻找文件
}
else {
//是一个文件,获取它的全路径,把它存到list集合中
String className=pack+"."+f.getName().replaceAll(".class","");
classNameList.add(className);
}
}
}
/**
* 实例化spring容器中的bean对象
* 主要是service层和controller层的类
*/
public void executeInstance(){
if(classNameList.size()==0){
//没有扫描到要实例化的类,抛出自定义的异常
throw new ContextException("没有实例化的class");
}
for (String className:classNameList){
try {
Class<?> clazz=Class.forName(className);
if (clazz.isAnnotationPresent(Controller.class)){
//判断该类是否有@Controller 注解,如果有说明他是控制层的类
//得到这个类的名字,第一个字母转小写。作为这个类对象的名字
String beanName=clazz.getSimpleName().substring(0,1).toLowerCase()+ clazz.getSimpleName().substring(1);
iocMap.put(beanName,clazz.newInstance());//把该对象的名字和实例存到ioc容器中
}
else if (clazz.isAnnotationPresent(Service.class)){
//如果有@Controller 注解,说明他是业务层的类
Service serviceAnnotation=clazz.getAnnotation(Service.class);//得到@Service
String beanName =serviceAnnotation.value();//得到里面的value值
if("".equals(beanName)){//如果我们没有使用value值去设置对象的名字
Class<?> []interfaces= clazz.getInterfaces();//就得到该类实现接口的名字
for (Class<?> c1:interfaces){//遍历接口,把接口名作为该类对象的名字,和该类的实例一起存到ioc容器中
String beanName1=clazz.getSimpleName().substring(0,1).toLowerCase()+ clazz.getSimpleName().substring(1);
iocMap.put(beanName1,clazz.newInstance());
}
}else {
iocMap.put(beanName,clazz.newInstance());//把value作为对象名字,与对象实例存入ioc集合
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
*实现spring容器中的对象的依赖注入:userService
*/
public void executeAutoWired(){
try {
if(iocMap.isEmpty()){//如果容器为空
throw new ContextException("没有找到初始化的bean对象");
}
for (Map.Entry<String,Object> entry:iocMap.entrySet()){
String key = entry.getKey();
Object bean = entry.getValue();//对象
Field[] declaredFields = bean.getClass().getDeclaredFields();//获取bean对象上的所有字段
for (Field declaredField:declaredFields){//遍历所有字段
if (declaredField.isAnnotationPresent(AutoWired.class)){//判断该字段上是否有AutoWired注解
AutoWired autoWiredAnnotation = declaredField.getAnnotation(AutoWired.class);//把这个AutoWired注解取出来
String beanName=autoWiredAnnotation.value();//看看该注解上的value值有没有被赋值
if ("".equals(beanName)){//如果value没有被赋值
Class<?> type=declaredField.getType();//得到type的名称:UserService
//把第一个字母变小写后赋值给beanName
beanName=type.getSimpleName().substring(0,1).toLowerCase()+ type.getSimpleName().substring(1);
}
System.out.println("bean:"+bean);
System.out.println("beanName:"+iocMap.get(beanName));
declaredField.setAccessible(true);//暴力反射,如果该对象是私有的,就需要开启它的访问权限
//属性注入,调用反射给属性注入值
//注入失败,接口名,与实现类对象名不一致(因为存在IOCMap中的key是实现类的名,不是接口名)
declaredField.set(bean,iocMap.get(beanName));
}
}
}
}catch (Exception e){
e.printStackTrace();
}
}
}
9,处理器类:MyHandler类 //用来存放 (请求url,对应控制器对象controller,处理url请求的方法method)
package com.springMVC.handler;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.lang.reflect.Method;
@AllArgsConstructor
@NoArgsConstructor
@Data
public class MyHandler {
private String url;
private Object controller;
private Method method;
}
10,xml文件解析类:XMLParse类//用来对springmvc.xml文件进行解析,得到要扫描的包
package com.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;
/**
* 解析springMVC.xml
*/
public class XmlParser {
public static String getBasePackage(String xml){
SAXReader saxReader=new SAXReader();
InputStream inputStream = XmlParser.class.getClassLoader().getResourceAsStream(xml);
//xml文档对象
try {
Document document = saxReader.read(inputStream);
//获取到根节点
Element rootElement = document.getRootElement();
//找到扫描包的配置节点component-scan
Element componentScan = rootElement.element("component-scan");
//去取出来属性为base-package的值
Attribute attribute = componentScan.attribute("base-package");
String basePackage = attribute.getText();
return basePackage;//包得到的com.bruce.service返回
} catch (DocumentException e) {
}
return "";
}
}
11,自定义异常:ContextException类//包扫描不到自定义抛出自定义异常提醒自己
package com.springMVC.exception;
public class ContextException extends RuntimeException {
public ContextException(String message) {
super(message);
}
public ContextException(Throwable cause) {
super(cause);
}
@Override
public String getMessage() {
return super.getMessage();
}
}
12,跳转后的页面user.jsp
<%--
Created by IntelliJ IDEA.
User: jianjiang
Date: 2021/4/4
Time: 11:12
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>跳转页面</title>
</head>
<body>
<h1>我是user.jsp页面</h1>
<h1>${requestScope.userMassage}</h1>
</body>
</html>
还没有评论,来说两句吧...