1、springmvc架构
2、相应注解详解
2.1、Controller注解 Controlle类上面的注解
2.2、Service注解 业务层对象上面的注解
2.33、Qualitify注解 在controller层里面,扫面含有此注解的字段,然后根据字段注入Service bean对象
2.44、HandlerMapping注解 处理方法上面的注解
3、编写流程(这里我们主要写框架的核心部分,所以不能做到像springmvc那样详尽):
1、先定义上面四个注解
2、全包扫描(在init()方法里面完成)
3、将扫描到含有Controller和service注解的类保存到一个map集合(classMap),key为当前注解的value值,value为当前类的实例对象。
4、将扫描到的Qualitify注解的字段,注入相应的值,值为classMap.get(key),其中key的值是扫描到Qualitify的value值。因为此key就是对应service注解的实例对象。
5、最后将要扫描Controller注解,然后在扫描handlerMapping注解,这一步为了构建请求地址,并且通过请求地址可以找到对应的方法的method对象和Controller注解对应类的实例对象。
首先,找到controller对象,通过集合(classmap)调用get(controller注解的value)方法得到对应的Controller类的实例对象,接下来扫描的是方法上面的注解,如果方法上面有HandlerMapping注解,然后构建ServletContext(项目名称,例如/上下文路径/资源具体路径)后面的url,如何构建呢?即Controller注解的value值加上HandllerMapping注解的value值,然后,在将此url作为key,然后扫描到的方法的Method对象作为value保存到一个Map(methodMap)集合,并且将此url作为key,controller注解对应的实例对象作为value,保存在classMap集合中。
6、此时就可以通过请求的url后面的路径映射到一个相应的method对象并且得到相应的controller类的实例。
代码部分:
包结构:
代码:
/**
* Controller注解
*
* */
@Target({ElementType.TYPE})//作用在类上
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Controller {
String value() default "";
}
/**
* Quatifier注解
*
* */
@Target({ElementType.FIELD})//作用在字段上
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Quatifier {
String value() default "";
}
/**
* RequestMapping注解
*
* */
@Target({ElementType.METHOD})//作用在方法上
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestMapping {
String value() default "";
}
/**
* Servic注解
*
* */
@Target({ElementType.TYPE})//作用在类上
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Service {
String value() default "";
}
@Controller("SpringMvcController")
public class SpringMvcController {
/**
* 把UserService实例对象注入进来
* */
@Quatifier("UserServiceImpl")
private UserService us;
@RequestMapping("insert.action")
public String insert(HttpServletRequest request, HttpServletResponse response, String param) {
us.insert(null);
try {
request.getRequestDispatcher("/index.jsp").forward(request, response);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ServletException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
@RequestMapping("delete.action")
public String delete(HttpServletRequest request, HttpServletResponse response, String param) {
us.delete(null);
return null;
}
@RequestMapping("update.action")
public String update(HttpServletRequest request, HttpServletResponse response, String param) {
us.update(null);
return null;
}
@RequestMapping("select.action")
public String select(HttpServletRequest request, HttpServletResponse response, String param) {
us.select(null);
return null;
}
}
public interface UserService {
int insert(Map map);
int delete(Map map);
int update(Map map);
int select(Map map);
}
@Service("UserServiceImpl")
public class UserServiceImpl implements UserService{
@Override
public int insert(Map map) {
System.out.println("调用了插入方法");
return 0;
}
@Override
public int delete(Map map) {
System.out.println("调用了删除方法");
return 0;
}
@Override
public int update(Map map) {
System.out.println("调用了更新方法");
return 0;
}
@Override
public int select(Map map) {
System.out.println("调用了查询方法");
return 0;
}
}
核心控制器,只需在web.xml文件中配置它就可以了。
public class DispatcherServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
//保存扫描到的所有包名
List<String> packageNames = new ArrayList<String>();
//所有类的实例,key是注解的value,value是注解对应的类的实例对象
Map<String, Object> instanceMap = new HashMap<String, Object>();
/**
* 处理器映射到的方法对象集合
* key是地址栏上项目上下文后面接上的地址
* value是对应的方法对象也就是Method对象
* */
Map<String, Object> handerMap = new HashMap<String, Object>();
@Override
public void init() throws ServletException {
//扫描包中的文件
scanPackage("com.bjsxt.springmvc");
try {
//将注解和对应的实例对象保存在集合当中
filterAndInstance();
} catch (Exception e) {
e.printStackTrace();
}
//建立映射关系
handderMap();
//依赖注入
ioc();
}
//建立映射关系
private void handderMap() {
if (instanceMap.size() <= 0) {
return;
}
for (Map.Entry<String, Object> entry : instanceMap.entrySet()) {
//判断类上是否有Controller注解,在做下一步操作
if (entry.getValue().getClass().isAnnotationPresent(Controller.class)) {
Controller controller = (Controller)entry.getValue().getClass().getAnnotation(Controller.class);
String cvalue = controller.value();
//拿到controller注解对应的对象
Object ctlObj = instanceMap.get(cvalue);
Method[] methods = entry.getValue().getClass().getMethods();
for (Method method : methods) {
//迭代方法,判断方法上面是否有requestMapping注解
if (method.isAnnotationPresent(RequestMapping.class)) {
String value = ((RequestMapping)method.getAnnotation(RequestMapping.class)).value();
String urlKey = "/"+cvalue+"/"+value;
//将地址和方法对象建立关系
handerMap.put(urlKey, method);
//将地址和对象也建立映射关系。
instanceMap.put(urlKey, ctlObj);
}else{
continue;
}
}
}
}
}
//属性的注入
private void ioc() {
//如果集合为空,直接跳出此方法的执行
if (instanceMap.isEmpty()) {
return;
}
for (Map.Entry<String, Object> entry : instanceMap.entrySet()) {
Field[] fields = entry.getValue().getClass().getDeclaredFields();
//迭代所有注解
for (Field field : fields) {
field.setAccessible(true);
//判断字段上面是否有Quatifier注解
if (field.isAnnotationPresent(Quatifier.class)) {
//拿到注解
Quatifier quatifier = field.getAnnotation(Quatifier.class);
//拿到注解对应的value值
String value = quatifier.value();
try {
//注入相应的值
field.set(entry.getValue(),instanceMap.get(value));
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
/**
* 扫描包下的所有文件
* */
private void scanPackage(String packagePath) {
String loc = "/"+replaceTo(packagePath);
System.out.println("扫描的地址:"+loc);
URL url = this.getClass().getClassLoader().getResource(loc);
System.out.println("url"+url);
String filePath = url.getFile();
File f = new File(filePath);
String[] fileList = f.list();
//迭代fileList
for (String path : fileList) {
File eachFile = new File(filePath+"/"+path);
//判断是否是一个目录
if (eachFile.isDirectory()) {
//到这里说明他是一个目录,我们还需要对子目录进行扫描
scanPackage(packagePath+"."+eachFile.getName());
}else{
//获得加后缀名的完整包名
String packName = packagePath+"."+eachFile.getName();
//去除文件后缀名,可得到包名+类名
String pName = packName.substring(0, packName.lastIndexOf(".class"));
packageNames.add(pName);
System.out.println("所有的包名加类名:"+packageNames);
}
}
}
//扫描
private void filterAndInstance()throws Exception {
if (packageNames.size() <=0) {
return;
}
for (String className : packageNames) {
//加载实例
Class<?> cName = Class.forName(className.replace(".class", "").trim());
//判断是否有Controller注解
if (cName.isAnnotationPresent(Controller.class)) {
Object instance = cName.newInstance();
//获得注解对象
Controller controller = (Controller) cName.getAnnotation(Controller.class);
String key = controller.value();
//把注解的value的值作为key,对应的实例对象作为value
instanceMap.put(key, instance);
//判断是否有Service注解
}else if(cName.isAnnotationPresent(Service.class)){
Object instance = cName.newInstance();
Service service = (Service)cName.getAnnotation(Service.class);
String key = service.value();
instanceMap.put(key, instance);
}
else{
continue;
}
}
}
//将所有的 . 转换成 /
private String replaceTo(String packagePath) {
return packagePath.replaceAll("\\.", "/");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//获得请求的全路径
String url = req.getRequestURI();
System.out.println("uri的路径"+url);
//获得上下文路径,通常是项目名
String context = req.getContextPath();
//去掉上下文
String path = url.replace(context, "");
System.out.println(path);
Method m = (Method) handerMap.get(path);
System.out.println(m);
//打印地址栏上去掉contextPath之后的url
System.out.println(path);
try {
//获得路径对应到的controller对象
Object obj = instanceMap.get(path);
System.out.println(obj);
//通过反射调用方法
m.invoke(obj, req,resp,null);
} 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();
}
}
}
web.xml
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>com.bjsxt.springmvc.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>