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.ArrayList;
import java.util.HashMap;
import java.util.List;
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 CustomDispatcherServletByDesignMode extends HttpServlet {

	// 保存application.properties配置文件中的内容
	private Properties contextConfig = new Properties();

	// 保存描到的类名称
	private List<String> classNames = new ArrayList<String>();

	// ioc容器,主要关注设计思想和原理,简化实现,不使用ConcurrentHashMap
	private Map<String, Object> ioc = new HashMap<String, Object>();

	// 保存url与Method的映射关系
	private Map<String, Method> handlerMapping = new HashMap<String, Method>();

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

	/**
	 * 使用了委派模式,委派模式的具体逻辑在doDispath中实现
	 */
	@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");
		}
	}

	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.handlerMapping.containsKey(url)){
			resp.getWriter().write("404");
			return;
		}
		Method method = (Method) this.handlerMapping.get(url);
		Map<String, String[]> params = req.getParameterMap();
		String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
		// 虽然完成了动态委派并进行了反射调用,但是对url参数的处理还是静态的。
		method.invoke(ioc.get(beanName), new Object[]{req,resp,params.get("name")[0]});
	}

	/**
	 * 生成初始类对象
	 */
	@Override
	public void init(ServletConfig config) throws ServletException {

		// 加载配置文件
		doLoadConfig(config.getInitParameter("contextConfigLocation"));

		// 扫描相关类
		doScanner(contextConfig.getProperty("scanPackage"));
		
		// 初始化扫描到的类,并将他们放入IOC容器中
		doInstance();
		
		// 完成依赖注入
		doAutowired();

		// 初始化HandlerMapping
		initHandlerMapping();
	}

	/**
	 * 用于加载配置文件
	 * @param contextConfigLocation
	 */
	private void doLoadConfig(String contextConfigLocation){

		InputStream fis = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);

		try {
			contextConfig.load(fis);
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}finally {
			if(null != fis){
				try {
					fis.close();
				} catch (Exception e2) {
					// TODO: handle exception
					e2.printStackTrace();
				}
			}
		}
	}

	/**
	 * 扫描相关类
	 * @param scanPackage
	 */
	private void doScanner(String scanPackage){

		// 转换为文件路径,就是把.替换成/
		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", ""));
				classNames.add(clazzName);
			}
		}
	}

	/**
	 * 工厂模式的具体实现
	 */
	private void doInstance() {
		// 初始化,为DI做准备
		if(classNames.isEmpty()){
			return;
		}
		try {
			for(String className : classNames){

				Class<?> clazz = Class.forName(className);
				/**
				 * 根据注解判断哪些类需要进行初始化
				 */
				if(clazz.isAnnotationPresent(CustomController.class)) {
					Object instance = clazz.newInstance();
					// Spring默认类名首字母小写
					String beanName = toLowerFirstCase(clazz.getSimpleName());
					ioc.put(beanName, instance);
				}else if(clazz.isAnnotationPresent(CustomService.class)){
					// 自定义beanName
					CustomService service = clazz.getAnnotation(CustomService.class); 
					String beanName = service.value();
					// Spring默认类名首字母小写
					if("".equals(beanName.trim())){
						beanName = toLowerFirstCase(clazz.getSimpleName());
					}
					Object instance = clazz.newInstance();
					ioc.put(beanName, instance);
					// 根据类型自动赋值
					for(Class<?> i: clazz.getInterfaces()){
						if(ioc.containsKey(i.getName())){
							throw new Exception(i.getName()+"不存在!");
						}
						// 接口类型直接作为key,具体实现类的实例作为value
						ioc.put(i.getName(), instance);
					}
				}else{
					continue;
				}

			}
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}

	/**
	 * 将类名首字母改成小写,Spring默认类名首字母小写
	 * @return
	 */
	private String toLowerFirstCase(String beanName) {
		char[] chars = beanName.toCharArray();
		// 大小字母ASCII码比小写字母ASCII码小32
		chars[0] +=32;
		return String.valueOf(chars);
	}

	// 自动进行依赖注入
	private void doAutowired() {

		if(ioc.isEmpty()){
			return;
		}
		// 获取所有的属性,包括private、protected、default
		// 正常来说只获得public
		for(Map.Entry<String, Object> entry : ioc.entrySet()) {
			Field[] fields = entry.getValue().getClass().getDeclaredFields();
			for(Field field:fields){
				if(!field.isAnnotationPresent(CustomAutowired.class)){
					continue;
				}
				CustomAutowired autowired = field.getAnnotation(CustomAutowired.class);
				// 若没有定义CustomAutowired的value,则默认根据类型注入
				String beanName = autowired.value().trim();
				if("".equals(beanName)){
					// 获取接口的类型,作为key到ioc容器中取value
					beanName = field.getType().getName();
				}

				// public以外的类型,只要加了@CustomAutowired的属性都强制进行赋值
				// 反射中叫暴力访问
				field.setAccessible(true);

				try {
					// 利用反射机制动态给属性赋值
					field.set(entry.getValue(), ioc.get(beanName));
				} catch (Exception e) {
					// TODO: handle exception
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 初始化url和Method的对应关系,是策略模式的应用案例
	 */
	private void initHandlerMapping() {
		if(ioc.isEmpty()){
			return;
		}

		for(Map.Entry<String, Object> entry : ioc.entrySet()){
			Class<?> clazz = entry.getValue().getClass();

			if(!clazz.isAnnotationPresent(CustomController.class)) {
				continue;
			}

			String baseUrl = "";

			if(clazz.isAnnotationPresent(CustomRequestMapping.class)){
				CustomRequestMapping requestMapping = clazz.getAnnotation(CustomRequestMapping.class);
				baseUrl = requestMapping.value();
			}

			// 默认获取所有public方法
			for(Method method : clazz.getMethods()) {
				// 没有@CustomRequestMapping标注的方法不处理
				if(!method.isAnnotationPresent(CustomRequestMapping.class)){
					continue;
				}
				CustomRequestMapping requestMapping = method.getAnnotation(CustomRequestMapping.class);

				String url = (baseUrl + requestMapping.value()).replace("/+", "/");

				handlerMapping.put(url, method);

			}
		}
	}

}


测试代码实现

实现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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值