spring框架的简单实现

IOC功能简单实现

创建自定义注解,功能与spring自带注解功能一致

元注解(负责注解其他注解)功能说明:

  • @Target:说明了Annotation所修饰的对象范围(被标注的注解可用在什么地方)。
    • ElementType.FIELD:注解可标注在属性上。
    • ElementType.METHOD:注解可标注在方法上。
    • ElementType.TYPE:注解可标注在类、接口(包括注解类型) 或enum声明上。
  • @Retention:该Annotation被保留的时间长短
    • SOURCE:在源文件中有效(即源文件保留)。
    • CLASS:在class文件中有效(即class保留)
    • RUNTIME:在运行时有效(即运行时保留)
  • @Documented:注解表明这个注释是由 javadoc记录的,在默认情况下也有类似的记录工具。 如果一个类型声明被注释了文档化,它的注释成为公共API的一部分。

创建自定义注解

  • @CustomController = @Controller

    package custom.annotation;
    
    import java.lang.annotation.*;
    
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface CustomController {
    	String value() default "";
    }
    
  • @CustomRequestMapping = @RequestMapping

    package custom.annotation;
    
    import java.lang.annotation.*;
    
    @Target({ElementType.TYPE,ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface CustomRequestMapping {
    	String value() default "";
    }
    
    
  • @CustomRequestParam = @RequestParam

    package custom.annotation;
    
    import java.lang.annotation.*;
    
    @Target({ElementType.PARAMETER})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface CustomRequestParam {
    	String value() default "";
    }
    
    
  • @CustomAutowired = @Autowired

    package custom.annotation;
    
    import java.lang.annotation.*;
    
    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface CustomAutowired {
    	String value() default "";
    }
    
    
  • @CustomService = @Service

    package custom.annotation;
    
    import java.lang.annotation.*;
    
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface CustomService {
    	String value() default "";
    }
    
    

创建IOC实现类

在这里插入图片描述

package custom.servlet;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import custom.annotation.CustomAutowired;
import custom.annotation.CustomController;
import custom.annotation.CustomRequestMapping;
import custom.annotation.CustomService;

/**
 * 简单版的IOC容器实现类
 * @author li
 *
 */
@SuppressWarnings("serial")
public class CustomDispatcherServlet extends HttpServlet {

	private Map<String, Object> mapping = new HashMap<String, Object>();

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		this.doPost(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		try {
			doDispath(req,resp);
		} catch (Exception e) {
			// 发生异常时,页面显示500错误
			resp.getWriter().write("500");
		}
	}

	/**
	 * 用于处理具体的调用请求,根据url在mapping中寻找对应方法
	 * @param req
	 * @param resp
	 * @throws Exception
	 */
	private void doDispath(HttpServletRequest req, HttpServletResponse resp) throws Exception{
		// 获取访问地址 例如:/custom-spring/demo/query
		String url = req.getRequestURI();
		// 获取属于哪个项目 /custom-spring
		String contextPath = req.getContextPath();
		// 获取具体调用地址 /demo/query
		url = url.replace(contextPath, "").replaceAll("/+", "/");
		if(!this.mapping.containsKey(url)){
			resp.getWriter().write("404");
			return;
		}
		Method method = (Method) this.mapping.get(url);
		Map<String, String[]> params = req.getParameterMap();
		method.invoke(this.mapping.get(method.getDeclaringClass().getName()), new Object[]{req,resp,params.get("name")[0]});
	}

	/**
	 * 生成初始类对象
	 */
	@Override
	public void init(ServletConfig config) throws ServletException {
		// TODO Auto-generated method stub
		InputStream is = null;
		try {
			Properties configContext = new Properties();
			is = this.getClass().getClassLoader().getResourceAsStream(config.getInitParameter("contextConfigLocation"));
			configContext.load(is);
			String scanPackage = configContext.getProperty("scanPackage");
			doScanner(scanPackage);
			/**
			 * 根据mapping中所有的key(项目中所有类的全限定名称)生成对应对象,并作为value加入到mapping中
			 */
			for(String className:mapping.keySet()){
				if(!className.contains(".")){
					continue;
				}
				// 根据全限定名获取类对象
				Class<?> clazz = Class.forName(className);
				// 对于@GPController注解标注的类做处理
				if(clazz.isAnnotationPresent(CustomController.class)){
					// 根据类对象生成具体实例放入到mapping中
					mapping.put(className, clazz.newInstance());
					// 映射地址
					String baseUrl = "";
					if(clazz.isAnnotationPresent(CustomRequestMapping.class)){
						CustomRequestMapping requestMapping = clazz.getAnnotation(CustomRequestMapping.class);
						baseUrl = requestMapping.value();
					}
					// Method 用于动态获取方法信息,根据类对象获取该类中所有方法
					Method[] methods = clazz.getMethods();
					for(Method method:methods){
						if(!method.isAnnotationPresent(CustomRequestMapping.class)){
							continue;
						}
						// 标注了@GPRequestMapping注解的方法,以类映射地址+方法映射地址形式作为key,方法对象作为value,存入mapping中
						CustomRequestMapping requestMapping = method.getAnnotation(CustomRequestMapping.class);
						String url = (baseUrl+requestMapping.value().replaceAll("/+", "/"));
						mapping.put(url, method);
						System.out.println("Mapped" + url + "," + method);
					}
				}else // 对于@GPService注解标注的类做处理
					if(clazz.isAnnotationPresent(CustomService.class)){
						CustomService service = clazz.getAnnotation(CustomService.class);
					// 获取@service标签中定义的属性value,若value为空,默认使用class的全限定名
					String beanName = service.value();
					if("".equals(beanName)){
						beanName = clazz.getName();
					}
					// 根据类对象创建具体实例
					Object instance = clazz.newInstance();
					// 创建具体实例放入mapping中
					mapping.put(beanName, instance);
					// clazz.getInterfaces()获取该对象所实现的所有接口 即以表示这个类实现了哪些接口
					for(Class<?> i : clazz.getInterfaces()){
						mapping.put(i.getName(), instance);
					}
				}else{
					continue;
				}
			}
			for(Object object : mapping.values()){
				/**
				 * 注解等类不会进行实例化
				 */
				if(object == null){
					continue;
				}
				// 获取类对象
				Class clazz = object.getClass();
				/**
				 * 示例方法中只有@GPController注解标识的方法内有@GPAutowired,所以没有对@GPService标示的类进行处理
				 */
				if(clazz.isAnnotationPresent(CustomController.class)){
					// Field:在程序运行状态中,动态地获取或设置字段信息
					Field[] fields = clazz.getDeclaredFields();
					for(Field field : fields){
						if(!field.isAnnotationPresent(CustomAutowired.class)){
							continue;
						}
						CustomAutowired autowired = field.getAnnotation(CustomAutowired.class);
						String beanName = autowired.value();
						if("".equals(beanName)){
							// 获取GPAutowired 标识字段对应的bean名称
							beanName = field.getType().getName();
							// public以外的类型,只要加了@CustomAutowired的属性都强制进行赋值
							// 反射中叫暴力访问
							field.setAccessible(true);
							try {
								// 向这个属性变量设置新值:field.set(具体对象实例,新的值) field类对象中的属性变量
								field.set(mapping.get(clazz.getName()), mapping.get(beanName));
							} catch (Exception e) {
								// TODO: handle exception
								e.printStackTrace();
							}
						}
					}
				}
			}
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		} finally{
			if(is != null){
				try{
					is.close();
				}catch (Exception e) {
					// TODO: handle exception
					e.printStackTrace();
				}
			}
		}
		System.out.println("GP MVC Framework is init");
	}

	/**
	 * 获取项目下的所有类文件,将类文件的全限定名做为容器的key,value暂时设置为null
	 * @param scanPackage
	 */
	private void doScanner(String scanPackage){

		// class文件对应地址
		URL url = this.getClass().getClassLoader().getResource("/"+scanPackage.replaceAll("\\.", "/"));

		File classDir = new File(url.getFile());

		for(File file : classDir.listFiles()){
			if(file.isDirectory()){
				doScanner(scanPackage+"."+file.getName());
			}else{
				if(!file.getName().endsWith(".class")){
					continue;
				}
				String clazzName = (scanPackage+"."+file.getName().replaceAll(".class", ""));
				mapping.put(clazzName, null);
			}
		}

	}

}

测试代码实现

实现controller

package custom.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import custom.annotation.CustomAutowired;
import custom.annotation.CustomController;
import custom.annotation.CustomRequestMapping;
import custom.annotation.CustomRequestParam;
import custom.service.CustomDemoService;

@CustomController
@CustomRequestMapping("/demo")
public class DemoAction {
	
	@CustomAutowired
	private CustomDemoService demoService;
	
	@CustomRequestMapping("/query")
	public void query(HttpServletRequest req,HttpServletResponse resp,@CustomRequestParam("name") String name){
		String result = demoService.get(name);
		try {
			resp.getWriter().write(result);
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}
	

}

实现service接口

package custom.service;

public interface CustomDemoService {
	
	String get(String name);

}

实现service 实现类

package custom.service.impl;

import custom.annotation.CustomService;
import custom.service.CustomDemoService;

@CustomService
public class DemoService implements CustomDemoService {
	
	public String get(String name){
		return "My name is" + name;
	}

}

配置配置文件 application.properties

放在src目录下:

# 扫描package名称
scanPackage=custom

配置web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>spring</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  
  <servlet>
  	<servlet-name>custommvc</servlet-name>
  	<servlet-class>custom.servlet.CustomDispatcherServlet</servlet-class>
  	<init-param>
  		<param-name>contextConfigLocation</param-name>
  		<param-value>application.properties</param-value>
  	</init-param>
  	// servlet init()执行时机,-1表示servlet被调用时执行,只执行一次,便于调试IOC容器加载实例过程
  	<load-on-startup>-1</load-on-startup>
  </servlet>
  <servlet-mapping>
  	<servlet-name>custommvc</servlet-name>
  	<url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值