自定義MVC框架中的一些元素
一些註解
@Documented
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
String value () default "";
}
@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
String value () default "";
}
@Documented
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
String value () default "";
}
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
String value () default "";
}
處理對象封裝
public class Handler {
private Object controller;//Controller對應的類
private Method method;//執行業務的方法
private Pattern pattern;//uri
private Map<String,Integer> paramIndexMapping;//參數和位置的映射
public Handler(Object controller, Method method, Pattern pattern) {
this.controller = controller;
this.method = method;
this.pattern = pattern;
this.paramIndexMapping = new HashMap<>();
}
public Object getController() {
return controller;
}
public void setController(Object controller) {
this.controller = controller;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public Pattern getPattern() {
return pattern;
}
public void setPattern(Pattern pattern) {
this.pattern = pattern;
}
public Map<String, Integer> getParamIndexMapping() {
return paramIndexMapping;
}
public void setParamIndexMapping(Map<String, Integer> paramIndexMapping) {
this.paramIndexMapping = paramIndexMapping;
}
}
自定義DisptchServelet處理代碼
public class DispatchServelet extends HttpServlet {
private Properties properties = new Properties();
private List<String> classNames = new ArrayList<>();
private Map<String,Object> ioc =new HashMap<>();
private List<Handler> handlerMapping = new ArrayList<>();
@Override
public void init(ServletConfig config) throws ServletException {
//加載配置文件
String contextConfigLocation = config.getInitParameter("contextConfigLocation");
doLoadConfig(contextConfigLocation);
//掃描相關的類,掃描註解
doScan(properties.getProperty("scanPackage"));
//初始化bean,基於註解
doInstance();
//實現依賴注入
doAutoWired();
//實現處理器映射器,將url和method進行關聯
initHandlerMapping();
System.out.println("mvc 初始化完成");
}
//執行的是方法和url方法映射
private void initHandlerMapping() {
if (ioc.isEmpty()){
return;
}
for (Map.Entry<String,Object> entry :ioc.entrySet()){
Class<?> aClass = entry.getValue().getClass();
if (!aClass.isAnnotationPresent(Controller.class)){
continue;
}
String baseUrl = "";
if (aClass.isAnnotationPresent(RequestMapping.class)){
RequestMapping requestMapping = aClass.getAnnotation(RequestMapping.class);
baseUrl=requestMapping.value();
}
Method[] methods= aClass.getMethods();
for (int i=0;i<methods.length;i++){
Method method = methods[i];
if (!method.isAnnotationPresent(RequestMapping.class)){
continue;
}
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
String methodUrl = requestMapping.value();
String url = baseUrl+methodUrl;
Handler handler = new Handler(entry.getValue(),method, Pattern.compile(url));
Parameter[] parameters = method.getParameters();
for (int j=0;j<parameters.length;j++){
Parameter parameter = parameters[j];
if (parameter.getType().equals(HttpServletRequest.class)||parameter.getType().equals(HttpServletResponse.class)){
handler.getParamIndexMapping().put(parameter.getType().getSimpleName(),j);
}else {
handler.getParamIndexMapping().put(parameter.getName(),j);
}
}
//完成方法和url的映射關係
handlerMapping.add(handler);
}
}
}
//執行注入部分,同樣是做的ioc的部分功能
private void doAutoWired() {
if (ioc.isEmpty()){
return;
}
for (Map.Entry<String,Object> entry :ioc.entrySet()){
Field[] declareFields = entry.getValue().getClass().getDeclaredFields();
for (int i=0;i<declareFields.length;i++){
Field declareField = declareFields[i];
if (!declareField.isAnnotationPresent(Autowired.class)){
continue;
}
Autowired autowired = declareField.getAnnotation(Autowired.class);
String beanName = autowired.value();
if ("".equals(beanName.trim())){
beanName=declareField.getType().getName();
}
declareField.setAccessible(true);
try {
//直接將這個字段的值設置爲ioc中已經示例化的類,
// 即是完成了ioc中的實例化交給容器來管理的情況
declareField.set(entry.getValue(),ioc.get(beanName));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
//執行的是符合要求的類的初始化,實際上是實現的一部分ioc的功能
private void doInstance() {
if (classNames.size()==0){
return;
}
try {
for (int i=0;i<classNames.size();i++){
String className = classNames.get(i);
Class<?> clazz = Class.forName(className);
if (clazz.isAnnotationPresent(Controller.class)){
String simpleName = clazz.getSimpleName();
String lowerFirst = lowerFirst(simpleName);
Object o = clazz.newInstance();
//因爲controller無別名,所以簡單設置成首字母小寫就行
ioc.put(lowerFirst,o);
}else if (clazz.isAnnotationPresent(Service.class)){
Service service = clazz.getAnnotation(Service.class);
String beanName =service.value();
if (!"".equals(beanName.trim())){
ioc.put(beanName,clazz.newInstance());
}else {
beanName = lowerFirst(clazz.getSimpleName());
ioc.put(beanName,clazz.newInstance());
}
Class<?>[] interfaces = clazz.getInterfaces();
for (int j=0;j<interfaces.length;j++){
Class<?> ainterface = interfaces[j];
System.out.println(ainterface.getName());
//將實現類和接口進行綁定
ioc.put(ainterface.getName(),clazz.newInstance());
}
}else {
continue;
}
}
}catch (Exception e){
e.printStackTrace();
}
}
private String lowerFirst(String className){
char[] chars = className.toCharArray();
if ('A'<chars[0]&&chars[0]<'Z'){
chars[0]+=32;
}
return new String(chars);
}
private void doScan(String basePackage) {
//獲取到指定包下的所有類的類名
String scanPackagePath= Thread.currentThread().getContextClassLoader().getResource("").getPath()+basePackage.replaceAll("\\.","/");
File pack = new File(scanPackagePath);
File [] files = pack.listFiles();
for (File file:files){
if (file.isDirectory()){
doScan(basePackage+"."+file.getName());
}else if (file.getName().endsWith(".class")){
String className = basePackage+"."+file.getName().replaceAll(".class","");
classNames.add(className);
}
}
}
//實現加載web.xml中配置的文件的路徑
private void doLoadConfig(String contextConfigLocation) {
InputStream inputStream =this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
try {
properties.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
@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 {
Handler handler = getHander(req);
if (handler==null){
resp.getWriter().write("404 not found");
return;
}
Class<?> [] parameterTypes = handler.getMethod().getParameterTypes();
Object[] paraValues = new Object[parameterTypes.length];
Map<String,String[]> parameterMap = req.getParameterMap();
for (Map.Entry<String,String[]> param:parameterMap.entrySet()){
String value = StringUtils.join(param.getValue(),",");
if (!handler.getParamIndexMapping().containsKey(param.getKey())){
continue;
}
//對應實際參數的位置
Integer index = handler.getParamIndexMapping().get(param.getKey());
paraValues[index]=value;
}
//對應上req,和resp參數的位置
int reqIndex = handler.getParamIndexMapping().get(HttpServletRequest.class.getSimpleName());
paraValues[reqIndex]=req;
int respIndex = handler.getParamIndexMapping().get(HttpServletResponse.class.getSimpleName());
paraValues[respIndex]=resp;
try {
//實際執行的是controller中的方法
handler.getMethod().invoke(handler.getController(),paraValues);
System.out.println("執行controller方法成功");
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
private Handler getHander(HttpServletRequest req) {
if (handlerMapping.isEmpty()){
return null;
}
String url =req.getRequestURI();
for (Handler handler:handlerMapping){
Matcher matcher = handler.getPattern().matcher(url);
if (!matcher.matches()){
continue;
}
return handler;
}
return null;
}
}
測試代碼
public interface DemoService {
String getName(String name);
}
@Service("demoService")
public class DemoServiceImpl implements DemoService {
@Override
public String getName(String name) {
return name;
}
}
@Controller
@RequestMapping("/demo")
public class DemoController {
@Autowired
private DemoService demoService;
@RequestMapping("/query")
public String query(HttpServletRequest req, HttpServletResponse resp,String name){
return demoService.getName(name);
}
}
web.xml配置
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>mvc</servlet-name>
<servlet-class>com.zhao.mvcframework.servelet.DispatchServelet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>mvc.properties</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>mvc</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
mvc.properties的配置
scanPackage=com.zhao.mvcdemo
pom文件的配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zhao</groupId>
<artifactId>mvc</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>mvc Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.10</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8080</port>
<path>/</path>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>utf-8</encoding>
<compilerArgs>-parameters</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</project>
測試時訪問具體的http://localhost:8080/demo/query?name=zhaozhen 無問題
代碼地址爲https://github.com/zhendiao/deme-code/tree/main/mvc