1.关于spring ioc容器:
spring ioc容器是什么呢? 我们可以理解为将东西存放起来的东西。比如将java对象存放在ioc容器中。
简单的说就是 ioc容器等于 ====>>> Map<String,Object> 大集合
了解了容器,接下里就可以写代码了。用过Spring的都知道这些注解。这里不过多讲解。
这里我们主要是自己手写以上几个注解.通过在tomcat启动时创建 dispatcherServlet init初始化时,完成注入;接下里会详细讲解。
2.关于注解:
算啦算啦,直接上代码. 有注释.
@Controller:
package com.springmvc.wh.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;
/**
* Controller控制类注解
* 命名可以自定义比如(WHController),这里为了可以更加直观体现
* @author 文浩
*
*/
@Target(ElementType.TYPE) // 该注解表示: 此注解作用范围只能在类上面
@Retention(RetentionPolicy.RUNTIME) // 该注解表示: 在系统运行时通过反射获取信息
@Documented // javaDoc
public @interface Controller {
String value() default ""; // 表示可以在注解内传入参数,参数类型为String 默认为"";
}
@Autowired:
package com.springmvc.wh.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;
/**
* 自动注入注解
*
* @author 文浩
*
*/
@Target(ElementType.FIELD) // 表示作用范围只限于成员变量上
@Retention(RetentionPolicy.RUNTIME) // 系统运行时加载
@Documented // javaDoc
public @interface Autowired {
String value() default "";
}
@RequestMapping:
package com.springmvc.wh.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;
/**
* 映射路径
*
* @author 文浩
*
*/
@Target({ ElementType.TYPE, ElementType.METHOD }) // 表示作用范围为类,和方法
@Retention(RetentionPolicy.RUNTIME) // 运行加载
@Documented
public @interface RequestMapping {
String value() default "";
}
@RequestParam :
package com.springmvc.wh.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;
/**
* 参数注解
*
* @author 文浩
*
*/
@Target(ElementType.PARAMETER) // 作用范围为方法参数里
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
String value() default "";
}
@Service
package com.springmvc.wh.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;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Service {
String value() default "";
}
3.到这里,注解已经写完了。接下里编写测试的代码.Service和Controller
比较懒,还是直接上代码好了。
Controller:
package com.springmvc.wh.controller;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.springmvc.wh.annotation.Autowired;
import com.springmvc.wh.annotation.Controller;
import com.springmvc.wh.annotation.RequestMapping;
import com.springmvc.wh.annotation.RequestParam;
import com.springmvc.wh.service.DomeService;
@Controller()
@RequestMapping("/wh")
public class DemoController {
@Autowired("domeService")
private DomeService domeService;
@RequestMapping("/Test")
public void Test(HttpServletRequest request, HttpServletResponse response,
@RequestParam("name") String name,
@RequestParam("age") String age) {
PrintWriter out;
try {
out = response.getWriter();
String result = domeService.Test(name, age);
out.write(result);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Service:
package com.springmvc.wh.service;
public interface DomeService {
public String Test(String name, String age);
}
package com.springmvc.wh.service.impl;
import com.springmvc.wh.annotation.Service;
import com.springmvc.wh.service.DomeService;
@Service("domeService")
public class DomeServiceImpl implements DomeService {
public String Test(String name, String age) {
return "name====>>" + name + " ; age===>>" + age;
}
}
这里没有使用持久层框架. 所有直接return 了,不然应该连接Dao层。不多解释。
关键来了,注解写了,Controller写了,service也写了。
能映射了吗,能注入了吗。答:当然不行
因为我们缺少了最关键的部分,servlet呢?.
没错,下一步,dispatcherServlet, 在tomcat启动时,完成注入。
4. dispatcherServlet
要想在tomcat启动时完成注入应该怎么写呢。
关于servlet生命周期,这里不在过多解释。
当然是init()方法; 好了废话有点多,还是上代码吧。
package com.springmvc.wh.servlet;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
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 javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.springmvc.wh.annotation.Autowired;
import com.springmvc.wh.annotation.Controller;
import com.springmvc.wh.annotation.RequestMapping;
import com.springmvc.wh.annotation.RequestParam;
import com.springmvc.wh.annotation.Service;
import com.springmvc.wh.controller.DemoController;
public class DispatcherServlet extends HttpServlet {
// doScan()用于存储所有class路径
private List<String> className = new ArrayList<String>();
// doInstance() 存储实例化后的bean
private Map<String, Object> beans = new HashMap<String, Object>();
private Map<String, Object> handerMap = new HashMap<String, Object>();
// <load-on-startup> 启动时调用
/**
*
*/
public void init(ServletConfig config) {
// 扫描 com.spring.mvc.wh 下路径 获取所有class文件并创建实例
// 得到Class<?>
doScan("com.springmvc.wh");
// 得到所有Class文件后,对这些class文件进行实例化
doInstance();
// 为实例化实例变量进行注入
doAutowired();
// 遍历方法获得映射路径
doUrlMapping();
}
/**
* 遍历实例,得到@RequstMapping(...)里的路径
*/
public void doUrlMapping() {
for (Map.Entry<String, Object> entry : beans.entrySet()) {
Object insetance = entry.getValue();
Class<?> clazz = insetance.getClass();
if (clazz.isAnnotationPresent(Controller.class)) {
RequestMapping reqMap = clazz.getAnnotation(RequestMapping.class);
String calssPath = reqMap.value();
// 得到类中所有方法
Method[] methods = clazz.getMethods();
// 遍历所有方法
for (Method method : methods) {
if (method.isAnnotationPresent(RequestMapping.class)) {
RequestMapping reqMap1 = method.getAnnotation(RequestMapping.class);
// 获取@ReuqstMapping注解中的value值
String methodPath = reqMap1.value();
handerMap.put(calssPath + methodPath, method);
} else {
continue;
}
}
}
}
}
/**
* 遍历实例化的实例,为成员变量进行注入
*/
public void doAutowired() {
try {
for (Map.Entry<String, Object> entry : beans.entrySet()) {
Object instance = entry.getValue();
Class<?> clazz = instance.getClass();
// 如果是Controller 类下
if (clazz.isAnnotationPresent(Controller.class)) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
Autowired aotuwired = field.getAnnotation(Autowired.class);
String key = aotuwired.value();
Object value = beans.get(key);
// 打开私有变量的权限
field.setAccessible(true);
field.set(instance, value);
}
} else {
continue;
}
}
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 遍历calssName集合中的文件,并且实例化
*/
public void doInstance() {
// 遍历className里的所有calss文件
for (String className : className) {
String cn = className.replace(".class", "");
try {
Class<?> clazz = Class.forName(cn);
// 判断calss文件上的注解是不是Controller注解
if (clazz.isAnnotationPresent(Controller.class)) {
Object value = clazz.newInstance();
// map.put(key,value);
RequestMapping reqMap = clazz.getAnnotation(RequestMapping.class);
String key = reqMap.value();
beans.put(key, value);
} else if (clazz.isAnnotationPresent(Service.class)) {
// 判断class文件上的注解是不是 Service
Object value = clazz.newInstance();
Service service = clazz.getAnnotation(Service.class);
String key = service.value();
beans.put(key, value);
} else {
continue;
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
// mvc.xml ----》 basePackage
public void doScan(String basePackage) {
// basePackage == com.springmvc
// 扫描编译好的所有的类路径
URL url = this.getClass().getClassLoader().getResource("/" + basePackage.replaceAll("\\.", "/"));
// 路径地址
String fileStr = url.getFile();
// 转换为文件类型,用于判断 是属于文件还是文件夹
File file = new File(fileStr);
// 获取basePackage 下所有的.calss
String[] filesStr = file.list();
for (String path : filesStr) {
File filePath = new File(fileStr + path);
// 判断路径是不是文件夹
if (filePath.isDirectory()) {
// 如果是路径 递归继续扫描
doScan(basePackage + "." + path);
} else {
className.add(basePackage + "." + filePath.getName());
}
}
}
// 源码采用策略模式,这里粗略简写
public static Object[] hand(HttpServletRequest req, HttpServletResponse resp, Method method) {
// 拿到当前待执行的方法有哪些参数
Class<?>[] paramClazzs = method.getParameterTypes();
Object[] args = new Object[paramClazzs.length];
int args_i = 0;
int index = 0;
for (Class<?> paramClazz : paramClazzs) {
if (ServletRequest.class.isAssignableFrom(paramClazz)) {
args[args_i++] = req;
}
if (ServletResponse.class.isAssignableFrom(paramClazz)) {
args[args_i++] = resp;
}
// 从0-3判断有没有RequestParam注解,很明显paramClazz为0和1时不是
// 当为2和3时为@requestParam,需要解析
// [@Con.xxx.xxx.ReuqstParam(value = value)]
Annotation[] paramAns = (Annotation[]) method.getParameterAnnotations()[index];
if (paramAns.length > 0) {
for (Annotation paramAn : paramAns) {
if (RequestParam.class.isAssignableFrom(paramAn.getClass())) {
RequestParam rp = (RequestParam) paramAn;
// 找到注解的参数
args[args_i++] = req.getParameter(rp.value());
}
}
}
index++;
}
return args;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
// 获取请求路径
String uri = req.getRequestURI();
String context = req.getContextPath();
String path = uri.replace(context, ""); // 路径下所有key
Method method = (Method) handerMap.get(path);
DemoController insetance = (DemoController) beans.get("/" + path.split("/")[1]);
Object[] args = hand(req, resp, method);
method.invoke(insetance, args);
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
到这里就完成了。 可以测试下 :
好了。告辞