springmvc在项目中的作用
- 处理请求,把请求分发到不同的类和方法中
- ioc 依赖注入,创建实例。配置的方式和annotation的方式,今天实现annotation的方式
- aop 动态代理,事物控制,让程序员只专注于写自己的业务代码
步骤
定义包结构
com.annotation com.controller com.service com.service.impl com.servlet
自定义注解
元注解的作用就是负责注解其他注解 1.@Target, :作用目标,作用在方法上、变量上、还是类上 2.@Retention,用于描述注解的生命周期: SOURCE:在源文件中有效 CLASS:在class文件中有效 RUNTIME:在运行时有效 3.@Documented,javadoc此类的工具文档化 自定义4个注解:Controller,Service,RequestMapping,Qualifier 注解就是存储信息的,跟xml功能差不多,存储了信息后,程序读到注解上面的信息做相应的操作
3、包扫描
包扫描的目的 根据basepackage,就是基包(代码中指的是com),扫描下面的子包以及子包下的类 拿到包下的所有类文件后,我们就可以得到文件名 有包名有文件名,我们就可以通过反射new出这些类的实例 目的就是扫描基包下的所有的类文件,根据文件获取类的完整类名:包名+类名 通过文件的方式去扫描
4、把所有的类new出实例后,我们就要把类中的依赖关系注入进去
拿到类的Class对象 拿到field对象 拿到field上面的annotation对象 根据annotation对象拿annotation对象的属性 把属性当key拿到map中的实例 然后field。set把实例设置进去
5、建立一个url与类中方法的映射关系
其实是同样的做法 拿到类的Class对象 拿到Method对象 拿到Method上面的annotation对象 把url和method对象存到map中
代码
自定义注解
package com.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 Controller {
String value() default "";
}
package com.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.FIELD }) // 注解在字段上
@Retention(RetentionPolicy.RUNTIME)
@Documented // 可以不写 用来文档打包的
public @interface Qualifier {
String value() default "";
}
package com.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.METHOD }) // 注解在方法上
@Retention(RetentionPolicy.RUNTIME)
@Documented // 可以不写 用来文档打包的
public @interface ResquestMapping {
String value() default "";
}
package com.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 "";
}
包扫描 IOC 和 handlerMapping
package com.servlet;
import java.io.File;
import java.io.IOException;
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 java.util.Map.Entry;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.annotation.Controller;
import com.annotation.Qualifier;
import com.annotation.ResquestMapping;
import com.annotation.Service;
public class DispatcherServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
// 定义基包下的文件的全限定名集合
List<String> packageNames = new ArrayList<>();
// 定义实例化对象的map key为注解的value value为类的实例
Map<String, Object> instanceMap = new HashMap<>();
// 定义路径与方法映射集合
Map<String, Object> handlerMap = new HashMap<>();
@Override
public void init() throws ServletException {
/* 包扫描 */
// 根据basepackage,就是基包(代码中指的是com),扫描下面的子包以及子包下的类
// 拿到包下的所有类文件后,我们就可以得到文件名
// 有包名有文件名,我们就可以通过反射new出这些类的实例
// 目的就是扫描基包下的所有的类文件,根据文件获取类的完整类名:包名+类名
// 通过文件的方式去扫描
System.out.println(this.getClass().getClassLoader().getResource("/"));// null
System.out.println(this.getClass().getClassLoader().getResource(""));// file:/D:/Users/workspace-sts-3.7.1.RELEASE/SpringMVC/target/classes/
System.out.println(this.getClass().getClassLoader().getResource("com"));// file:/D:/Users/workspace-sts-3.7.1.RELEASE/SpringMVC/target/classes/com
System.out.println(DispatcherServlet.class.getClassLoader().getResource("/com"));// null
scanPackage("com");// spring中 是在xml中配置的
try {
// 拿到类的Class对象
filterAndInstance();
// 依赖注入
// 拿到field对象
// 拿到field上面的annotation对象
// 根据annotation对象拿annotation对象的属性
// 把属性当key拿到map中的实例
// 然后field。set把实例设置进去
ioc();
// 建立路径方法映射
handlerMap();
// 打印实例化对象的map
for (Entry<String, Object> entry : instanceMap.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
// 打印路径与方法映射集合map
for (Entry<String, Object> entry : handlerMap.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @Title: ioc
* @Description: 路径方法映射
*/
private void handlerMap() {
if (instanceMap.isEmpty()) {
return;
}
// 遍历 实例集合 取得方法 包含 RequestMapping 注解
for (Entry<String, Object> entry : instanceMap.entrySet()) {
if (entry.getValue().getClass().isAnnotationPresent(Controller.class)) {
Controller controllerAn = entry.getValue().getClass().getAnnotation(Controller.class);
String ctvalue = controllerAn.value();
Method[] methods = entry.getValue().getClass().getMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(ResquestMapping.class)) {
ResquestMapping annotation = method.getAnnotation(ResquestMapping.class);
String rmvalue = annotation.value();
handlerMap.put("/" + ctvalue + "/" + rmvalue, method);
}
}
}
}
}
/**
* @Title: ioc
* @Description: 属性依赖注入
*/
private void ioc() throws IllegalArgumentException, IllegalAccessException {
// 如果 map 为空 说明没有实例化对象 返回
if (instanceMap.isEmpty()) {
return;
}
// 遍历 实例集合
for (Entry<String, Object> entry : instanceMap.entrySet()) {
if (entry.getValue().getClass().isAnnotationPresent(Controller.class)) {
// 获取 当前实例的字段
Field[] fields = entry.getValue().getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
// 如果包含指定注解 那么通过该注解的 value 向正在遍历的实例化对象中注入 指定对象
if (field.isAnnotationPresent(Qualifier.class)) {
Qualifier annotation = field.getAnnotation(Qualifier.class);
String value = annotation.value();
field.set(entry.getValue(), instanceMap.get(value));
}
}
}
}
}
/**
* @Title: filterAndInstance
* @Description: 定义过滤方法 添加需要实例化的类到instanceMap
*/
private void filterAndInstance() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
if (packageNames.isEmpty()) {
return;
}
for (String className : packageNames) {
// 反射 获取对象 className去掉.class
Class<?> clazz = Class.forName(className.replace(".class", ""));
// 根据注解判断是否需要实例化
if (clazz.isAnnotationPresent(Controller.class)) {
Object instance = clazz.newInstance();
Controller annotation = clazz.getAnnotation(Controller.class);
String key = annotation.value();
instanceMap.put(key, instance);
} else if (clazz.isAnnotationPresent(Service.class)) {
Object instance = clazz.newInstance();
Service annotation = clazz.getAnnotation(Service.class);
String key = annotation.value();
instanceMap.put(key, instance);
}
}
}
/**
* @Title: scanPackage
* @Description: 定义包扫描方法
*/
private void scanPackage(String basepackage) {
// 将基包中的.转换为/ 才能遍历其下的文件
URL url = this.getClass().getClassLoader().getResource(replaceTo(basepackage));
String pathFile = url.getFile();// 得到绝对路径的字符串
File file = new File(pathFile);
String[] files = file.list();
for (String path : files) {
File eachFile = new File(pathFile + "/" + path);
if (eachFile.isDirectory()) {
// 递归迭代
scanPackage(basepackage + "." + eachFile.getName());
} else {
// 是文件 添加其全类名
// 返回 com.xxx.class
System.out.println("初始加载基包下的所有文件: " + basepackage + "." + eachFile.getName());
packageNames.add(basepackage + "." + eachFile.getName());
}
}
}
/**
* @Title: replaceTo
* @Description: 将路径中的 "." 转换为 "/"
*/
private String replaceTo(String path) {
return path.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 uri = req.getRequestURI();
// 获取工程名字
String contextPath = req.getContextPath();
// 截取uri 去掉工程名字 /index/insert
String path = uri.replace(contextPath, "");
Method method = (Method) handlerMap.get(path);
try {
// path.split("/")=["",index,insert]
method.invoke(instanceMap.get(path.split("/")[1]), new Object[] { req, resp, null });
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
controller 和 service
package com.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.annotation.Controller;
import com.annotation.Qualifier;
import com.annotation.ResquestMapping;
import com.service.IndexService;
import com.service.MyService;
//设计这里的index 也是相当于springMVC中RequestMapping的作用
@Controller("index")
public class IndexController {
@Qualifier("myServiceImpl")
private MyService myService;
@Qualifier("indexServiceImpl")
private IndexService indexService;
@ResquestMapping("insert") // 不能带有"/" 设计不带
public String insert(HttpServletRequest request, //
HttpServletResponse response, //
String param) {
// URL: http://localhost:8080/SpringMVC/index/update
// URI: /SpringMVC/index/update
System.err.println("URL: " + request.getRequestURL());
System.err.println("URI: " + request.getRequestURI());
myService.insert();
indexService.insert();
return null;
}
@ResquestMapping("delete") // 不能带有"/" 设计不带
public String delete(HttpServletRequest request, //
HttpServletResponse response, //
String param) {
System.err.println("URL: " + request.getRequestURL());
System.err.println("URI: " + request.getRequestURI());
myService.delete();
indexService.delete();
return null;
}
@ResquestMapping("update") // 不能带有"/" 设计不带
public String update(HttpServletRequest request, //
HttpServletResponse response, //
String param) {
System.err.println("URL: " + request.getRequestURL());
System.err.println("URI: " + request.getRequestURI());
myService.update();
indexService.update();
return null;
}
@ResquestMapping("select") // 不能带有"/" 设计不带
public String select(HttpServletRequest request, //
HttpServletResponse response, //
String param) {
System.err.println("URL: " + request.getRequestURL());
System.err.println("URI: " + request.getRequestURI());
myService.select();
indexService.select();
return null;
}
}
package com.service;
public interface IndexService {
int insert();
int delete();
int update();
int select();
}
package com.service;
public interface MyService {
int insert();
int delete();
int update();
int select();
}
package com.service.impl;
import com.annotation.Service;
import com.service.IndexService;
@Service("indexServiceImpl")
public class IndexServiceImpl implements IndexService {
@Override
public int insert() {
System.out.println("IndexServiceImpl的insert");
return 0;
}
@Override
public int delete() {
System.out.println("IndexServiceImpl的delete");
return 0;
}
@Override
public int update() {
System.out.println("IndexServiceImpl的update");
return 0;
}
@Override
public int select() {
System.out.println("IndexServiceImpl的select");
return 0;
}
}
package com.service.impl;
import com.annotation.Service;
import com.service.MyService;
@Service("myServiceImpl")
public class MyServiceImpl implements MyService {
@Override
public int insert() {
System.out.println("MyServiceImpl的insert");
return 0;
}
@Override
public int delete() {
System.out.println("MyServiceImpl的delete");
return 0;
}
@Override
public int update() {
System.out.println("MyServiceImpl的update");
return 0;
}
@Override
public int select() {
System.out.println("MyServiceImpl的select");
return 0;
}
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- 自定义前端控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>com.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- 1:*.do *.action 拦截以.do结尾的请求 (不拦截 jsp png jpg .js .css) 2:/ 拦截所有请求 (不拦截.jsp) 建议使用此种 方式 (拦截 .js.css .png) (放行静态资源) 3:/* 拦截所有请求(包括.jsp) 此种方式 不建议使用 -->
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
OK 大功告成!!!