欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

自定义springmvc

程序员文章站 2022-05-30 20:33:23
...

写在前面

本文使用JAVA类库完成一个迷你版的springmvc框架,旨在深入理解springmvc的内部实现原理。

源码地址https://github.com/Jacwo/myspringmvc.git 喜欢就fork me

目录结构

自定义springmvc

自定义注解

元注解:就是注解的注解

@Retention

@Retention(RetentionPolicy.SOURCE)   //注解仅存在于源码中,在class字节码文件中不包含
@Retention(RetentionPolicy.CLASS)     // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
@Retention(RetentionPolicy.RUNTIME)  // 注解会在class字节码文件中存在,在运行时可以通过反射获取到

@Target

  @Target(ElementType.CONSTRUCTOR)	 //用于描述构造器
  @Target(ElementType.FIELD)		 //成员变量、对象、属性(包括enum实例)
  @Target(ElementType.LOCAL_VARIABLE)    //用于描述局部变量
  @Target(ElementType.METHOD)		 //用于描述方法
  @Target(ElementType.PACKAGE)		 //用于描述包
  @Target(ElementType.PARAMETER)	 //用于描述参数
  @Target(ElementType.TYPE)		 //用于描述类、接口(包括注解类型) 或enum声明

@Documented

标记注解表示是否将注解信息加入JAVA文档中

controller

@Documented //javadoc
@Target(ElementType.TYPE) //注解作用在类上
@Retention(RetentionPolicy.RUNTIME) //限制注解的生命周期
public @interface Controller {
	/**
	 * 作用于该类上的注解有一个value属性,其实就是controller
	 * @return
	 */
	public String value();
}

Qualifier

@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Qualifier {
	public String value();
}

Repository

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Repository {
	public String value();
}

RequestMapping

@Documented
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
	public String value();
}

Service

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
	public String value();
}	

DispatcherServlet

dispacherServlet是springmvc的核心控制器,是一个HttpServlet的子类,下面我们来实现它;

@WebServlet(name="dispatcherServlet",urlPatterns="/*",loadOnStartup=1,
initParams={@WebInitParam(name="base-package",value="com.flashhold")})
public class DispatcherServlet extends HttpServlet{
	//扫描的基包
	private String basePackage="";
	//基包下面所有带包路径权限定类名
	private List<String> packageNames=new ArrayList<String>();
	//注解实例化 注解上的名称:实例化对象
	private Map<String,Object>instanceMap=new HashMap<>();
	//带包路径的权限定名称:注解的名称
	private Map<String,String>nameMap=new HashMap<>();
	//url地址和方法的映射关系
	private Map<String,Method>urlMethodMap=new HashMap<>();
	//method和权限定名映射关系
	private Map<Method,String>methodPackageMap=new HashMap<>();

@WebServlet 以前我们定义的servlet需要在Web.xml中配置,Servlet3.0以后出现了基于注解的servlet,在springmvc中DispacherServlet启动需要配置扫描包,比如:   本文为了方便直接在初始化时引入。

<context:component-scan base-package="com.flashhold.myspringmvc">
</context:component-scan>

初始化dispacherServlet

初始化完成的动作:

  • 扫描基包下的类拿到信息
  • 拿到@Controller、@Service、@Repository注解相应的名称,初始化修饰的类
  • 扫描类中的字段,如果有@Qualifier注解,我们需要完成注入
  • 扫描@RequestMapping注解,如果有完成到Controller的关系映射。
@Override
    public void init(ServletConfig config) throws ServletException {
        // TODO Auto-generated method stub
        basePackage=config.getInitParameter("base-package");
        try {
            //扫描基包得到全部的带包路径全限定名
            scanBasePackage(basePackage);
            //把所有带有注解的类实例化放入map中key为注解的名称
            instance(packageNames);
            //注入
            springIOC();
            //完成url地址和方法的映射关系
            HandlerUrlMethodMap();
        } catch (Exception e) {
            // TODO: handle exception
        }
        
    }

扫描包

private void scanBasePackage(String basePackage) {
		URL url=this.getClass().getClassLoader().getResource(basePackage.replaceAll("\\.", "/"));
		File basePackageFile=new File(url.getPath());
		System.out.println("scan"+basePackageFile);
		File[]cFiles=basePackageFile.listFiles();
		for (File file : cFiles) {
			if(file.isDirectory()){
				scanBasePackage(basePackage+"."+file.getName());
			}else if(file.isFile()){
				packageNames.add(basePackage+"."+file.getName().split("\\.")[0]);
			}
		}
		
	}

实例化

 
private void instance(List<String> packageNames) throws ClassNotFoundException, 
InstantiationException, IllegalAccessException {
		// TODO Auto-generated method stub
		if(packageNames.size()<1){
			return;
		}
		for (String string : packageNames) {
			Class c=Class.forName(string);
			if(c.isAnnotationPresent(Controller.class)){
				Controller controller=(Controller) c.getAnnotation(Controller.class);
				String controllerName=controller.value();
				instanceMap.put(controllerName, c.newInstance());
				nameMap.put(string, controllerName);
				System.out.println("Controller:"+string+"value:"+controller.value());
			}else if(c.isAnnotationPresent(Service.class)){
				Service service=(Service) c.getAnnotation(Service.class);
				String serviceName=service.value();
				instanceMap.put(serviceName, c.newInstance());
				nameMap.put(string, serviceName);
				System.out.println("Service:"+string+"value:"+service.value());
			}else if(c.isAnnotationPresent(Repository.class)){
				Repository repository=(Repository) c.getAnnotation(Repository.class);
				String repositoryName=repository.value();
				instanceMap.put(repositoryName, c.newInstance());
				nameMap.put(string, repositoryName);
				System.out.println("Controller:"+string+"value:"+repository.value());
			}
		}
	}

注入

	private void springIOC() throws IllegalArgumentException, IllegalAccessException {
		// TODO Auto-generated method stub
		for (Map.Entry<String,Object>entry: instanceMap.entrySet()) {
			Field[] fields=entry.getValue().getClass().getDeclaredFields();
			for (Field field : fields) {
				if(field.isAnnotationPresent(Qualifier.class)){
					String name=field.getAnnotation(Qualifier.class).value();
					field.setAccessible(true);
					field.set(entry.getValue(), instanceMap.get(name));
				}
			}
		}
	}

URL映射

	private void HandlerUrlMethodMap() throws ClassNotFoundException {
		// TODO Auto-generated method stub
		if(packageNames.size()<1){
			return;
		}
		for (String string : packageNames) {
			Class c=Class.forName(string);
			if(c.isAnnotationPresent(Controller.class)){
				Method[]methods=c.getMethods();
				StringBuffer baseUrl=new StringBuffer();
				if(c.isAnnotationPresent(RequestMapping.class)){
					RequestMapping requestMapping=(RequestMapping) c.getAnnotation(RequestMapping.class);
					baseUrl.append(requestMapping.value());
				}
				for (Method method : methods) {
					if(method.isAnnotationPresent(RequestMapping.class)){
						RequestMapping requestMapping=method.getAnnotation(RequestMapping.class);
						baseUrl.append(requestMapping.value());
						urlMethodMap.put(baseUrl.toString(), method);
						methodPackageMap.put(method, string);
					}
				}
			}
		}
		
	}

doGet和doPost

@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		// TODO Auto-generated method stub
		 doPost(req, resp);
	}
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		String uri=req.getRequestURI();
		String contextPath=req.getContextPath();
		String path=uri.replaceAll(contextPath, "");
		Method method=urlMethodMap.get(path);
		if(method!=null){
			String packageName=methodPackageMap.get(method);
			String controllerName=nameMap.get(packageName);
			UserController userController=(UserController) instanceMap.get(controllerName);
			try {
				method.setAccessible(true);
				method.invoke(userController);
			} catch (Exception e) {
				// TODO: handle exception
			}
					
		}
	}
	

Controller层

@Controller("userController")
@RequestMapping("/user")
public class UserController {
	@Qualifier("userServiceImpl")
	private UserService userService;
	@RequestMapping("/insert")
	public void insert(){
		userService.insert();
	}
}

Service接口和实现类

public interface UserService {
	public void insert();
 }

@Service("userServiceImpl")
public class UserServiceImpl implements UserService{
    @Qualifier("userDaoImpl")
    private UserDao userDao;
    @Override
    public void insert() {
        // TODO Auto-generated method stub
        userDao.insert();
    }

}



Dao层接口和实现类

public interface UserDao {
	public void insert();
}


@Repository("userDaoImpl")
public class UserDaoImpl implements UserDao{
    @Override
    public void insert() {
        // TODO Auto-generated method stub
        System.out.println("我是dao");
    }
    
}
 
相关标签: springmvc 自定义