Spring手寫(xiě)簡(jiǎn)化版MVC流程詳解
spring是一個(gè)非常流行的技術(shù)框架,其中spring mvc組件在其中非常重要的地位,主要面要客戶端提供服務(wù),我們今天來(lái)手寫(xiě)一個(gè)簡(jiǎn)化版的mvc,且包括ioc部分,主要利用servlet機(jī)制來(lái)實(shí)現(xiàn),類的關(guān)系如下:

準(zhǔn)備注解類,類于spring的@Autowired、@Service、@Controller、@RequestMapping、@RequestParam
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CSAutowired {
String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CSController {
String value() default "";
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CSRequestMapping {
String value() default "";
}
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CSRequestParam {
String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CSService {
String value() default "";
}準(zhǔn)備service interface
public interface IDemoService {
public String get(String name);
}
準(zhǔn)備service實(shí)現(xiàn)類,利用@CSService
@CSService
public class DemoService implements IDemoService {
@Override
public String get(String name) {
return "My name is "+name;
}
}
準(zhǔn)備對(duì)外服務(wù)的類,主要利用@CSController注解
@CSController
@CSRequestMapping("/demo")
public class DemoAction {
@CSAutowired
private IDemoService demoService;
@CSRequestMapping("/query")
public void query(HttpServletRequest req, HttpServletResponse resp, @CSRequestParam("name") String name){
String result=demoService.get(name);
try {
resp.getWriter().write(result);
} catch (IOException exception) {
exception.printStackTrace();
}
}
@CSRequestMapping("/add")
public void add(HttpServletRequest req, HttpServletResponse resp, @CSRequestParam("aa") Integer a,@CSRequestParam("b") Integer b){
try {
resp.getWriter().write(a+"+"+b+"="+(a+b));
} catch (IOException exception) {
exception.printStackTrace();
}
}
@CSRequestMapping("/remove")
public void remove(HttpServletRequest req, HttpServletResponse resp, @CSRequestParam("id") Integer id){
try {
resp.getWriter().write("id="+id);
} catch (IOException exception) {
exception.printStackTrace();
}
}
}準(zhǔn)備servlet
主要實(shí)現(xiàn)了以下功能:
1).根據(jù)@CSController對(duì)外服務(wù)的url如何mapping到具體方法 doHandlerMap
2).service和controller bean的管理 iocBeans
3).如何實(shí)列化bean doInstance
4).如何獲取url中參數(shù)值 doDispatch中
5).找到需要加載的class doScanner
6).如何自動(dòng)autowired doAutoWried
public class CSDispatchServlet extends HttpServlet {
public static String urlPattern="/custom";
private void doDispatch(HttpServletRequest request,HttpServletResponse response) throws Exception{
String url=request.getRequestURI();
String contextPath=request.getContextPath();
url=url.replace(urlPattern,"");
if(!handlerMap.containsKey(url)){
response.getWriter().write("404 not found!");
return;
}
Method method=handlerMap.get(url);
Annotation[][] methodParameterAnnotations= method.getParameterAnnotations();
Parameter[] methodParameters= method.getParameters();
Annotation[][] paramerterAnnotations=method.getParameterAnnotations();
ArrayList<Object> methodParameterValues=new ArrayList<Object>();
Map<String,String[]> requestParams= request.getParameterMap();
int parmeterCnt=0;
for(Parameter parameter:methodParameters){
if(parameter.getType()==HttpServletRequest.class ){
methodParameterValues.add(request);
}else if(parameter.getType()==HttpServletResponse.class){
methodParameterValues.add(response);
}else {
String methodParamName="";
if(paramerterAnnotations[parmeterCnt].length>0) {
Annotation annotation= paramerterAnnotations[parmeterCnt][0];
if(annotation instanceof CSRequestParam) {
methodParamName = ((CSRequestParam) annotation).value();
}
}
if("".equals(methodParamName.trim())){
methodParamName=parameter.getName();
}
String value="";
//String value=Arrays.toString(requestParams.get(methodParamName));
if(requestParams.get(methodParamName).length>1)
value=Arrays.toString(requestParams.get(methodParamName));
else if(requestParams.get(methodParamName).length==1)
value= requestParams.get(methodParamName)[0];
else
value="999999";
if(parameter.getType()==String.class)
methodParameterValues.add(value);
else if(parameter.getType()==Integer.class) {
try {
methodParameterValues.add(Integer.parseInt(value));
} catch (Exception e){
methodParameterValues.add(99999999);
}
}else {
//可以擴(kuò)展復(fù)雜類型轉(zhuǎn)換
}
}
parmeterCnt++;
}
String beanName=this.genBeanName(method.getDeclaringClass().getSimpleName());
method.invoke(this.iocBeans.get(beanName), methodParameterValues.toArray());
}
private String genBeanName(String beanName){
if(beanName.length()>1)
beanName=beanName.substring(0,0).toLowerCase()+beanName.substring(1);
else
beanName=beanName.toLowerCase();
return beanName;
}
@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 {
this.doDispatch(req,resp);
} catch (Exception exception) {
exception.printStackTrace();
}
}
private ArrayList<String> classs=new ArrayList<String>();
private ConcurrentHashMap<String,Object> iocBeans=new ConcurrentHashMap<String,Object>();
private ConcurrentHashMap<String,Method> handlerMap=new ConcurrentHashMap<String,Method>();
private void doInstance() {
try {
for (String className : classs) {
if (!className.contains(".")) continue;
Class<?> clazz = Class.forName(className);
String beanName="";
if (clazz.isAnnotationPresent(CSController.class)) {
CSController controller = clazz.getAnnotation(CSController.class);
beanName=controller.value();
}else if(clazz.isAnnotationPresent(CSService.class)){
CSService service=clazz.getAnnotation(CSService.class);
beanName=service.value();
}else {
continue;
}
Object instance=clazz.newInstance();
if("".equals(beanName.trim()))
beanName=clazz.getSimpleName();
beanName=genBeanName(beanName);
iocBeans.put(beanName,instance);
if(clazz.isAnnotationPresent(CSService.class)){
for(Class c: clazz.getInterfaces()){
if(iocBeans.containsKey(c.getName())) continue;
iocBeans.put(c.getName(),instance);
}
}
}
}catch (Exception e){
e.printStackTrace();
}
}
private void doAutoWried(){
for(Object o:iocBeans.values()){
if(o==null) continue;
Class clazz=o.getClass();
if(clazz.isAnnotationPresent(CSService.class) || clazz.isAnnotationPresent(CSController.class)){
Field[] fields=clazz.getDeclaredFields();
for(Field f:fields){
if(!f.isAnnotationPresent(CSAutowired.class)) continue;
CSAutowired autowired=f.getAnnotation(CSAutowired.class);
String beanName=autowired.value();
if("".equals(beanName)) beanName=f.getType().getName();
f.setAccessible(true);
try{
Object o1=iocBeans.get(beanName);
f.set(o,iocBeans.get(beanName));
}catch (IllegalAccessException e){
e.printStackTrace();
}
}
}
}
}
private void doHandlerMap(ServletConfig config){
for(Object o:iocBeans.values()){
if(!o.getClass().isAnnotationPresent(CSController.class)) continue;
String baseUrl="";
if(o.getClass().isAnnotationPresent(CSRequestMapping.class)){
CSRequestMapping requestMapping=o.getClass().getAnnotation(CSRequestMapping.class);
baseUrl=requestMapping.value();
}
for(Method method: o.getClass().getMethods()){
if(method.isAnnotationPresent(CSRequestMapping.class)) {
CSRequestMapping requestMapping=method.getAnnotation(CSRequestMapping.class);
String url=baseUrl+requestMapping.value().replaceAll("/+","/");
String contextPath=config.getServletContext().getContextPath();
this.handlerMap.put(url,method);
}
}
}
}
@Override
public void init(ServletConfig config) throws ServletException {
InputStream is=null;
try{
System.out.println("custom servlet init........");
/*
Properties configContext=new Properties();
is=this.getClass().getClassLoader().getResourceAsStream(config.getInitParameter("contextConfigLocation"));
configContext.load(is);
String scanPackage=configContext.getProperty("scanPackage");
*/
Enumeration<String> enumerations= config.getInitParameterNames();
while (enumerations.hasMoreElements()){
System.out.println(enumerations.nextElement());
}
doScanner("com.mesui.spring.custom");
doInstance();
doAutoWried();
doHandlerMap( config);
}catch (Exception exception){
exception.printStackTrace();
}finally {
}
}
private void doScanner(String scanPackage){
URL url= this.getClass().getClassLoader().getResource("") ;
String filePath="";
try {
filePath= URLDecoder.decode( url.getPath(),"UTF-8")+"/"+scanPackage.replaceAll("\\.","/");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
File classDir=new File(filePath);
for(File file:classDir.listFiles()){
if(file.isDirectory()){
doScanner(scanPackage+"."+file.getName());
}else if(!file.getName().endsWith(".class")) {
continue;
}
if(!file.isDirectory()) {
String clzzName = (scanPackage + "." + file.getName().replace(".class", ""));
//map.put(clzzName,null);
classs.add(clzzName);
}
}
}
}在利用spring的configuration類初始化servlet
這邊為了方便進(jìn)行偷懶,這樣/custom/下的服務(wù)按照自已邏輯對(duì)對(duì)外服務(wù),不按照spring mvc的進(jìn)行,另外自已可以tomcat的web.xml中標(biāo)記servlet完全脫離spring
@Configuration
public class MybatisPlusConfig {
@Bean
public ServletRegistrationBean CustomServlet(){
return new ServletRegistrationBean(new CSDispatchServlet(),CSDispatchServlet.urlPattern+"/*");
}
}測(cè)試

結(jié)論
從上面的例子中我們可以看到自已寫(xiě)一個(gè)mvc也很方便,不是什么難事,但是這個(gè)只是用于學(xué)習(xí),畢竟spring是一個(gè)體系,我們自已不可能將所有內(nèi)容重新寫(xiě)一遍,但是自已寫(xiě)著玩有助于對(duì)spring mvc和IOC的理解。
到此這篇關(guān)于Spring手寫(xiě)簡(jiǎn)化版mvc流程詳解的文章就介紹到這了,更多相關(guān)Spring mvc內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java基于jdbc連接mysql數(shù)據(jù)庫(kù)功能實(shí)例詳解
這篇文章主要介紹了java基于jdbc連接mysql數(shù)據(jù)庫(kù)功能,結(jié)合實(shí)例形式詳細(xì)分析了jdbc連接mysql數(shù)據(jù)庫(kù)的原理、步驟、實(shí)現(xiàn)方法及相關(guān)操作技巧,需要的朋友可以參考下2017-10-10
SpringBoot服務(wù)端數(shù)據(jù)校驗(yàn)過(guò)程詳解
這篇文章主要介紹了SpringBoot服務(wù)端數(shù)據(jù)校驗(yàn)過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02
解決@PathVariable參數(shù)接收不完整的問(wèn)題
這篇文章主要介紹了解決@PathVariable參數(shù)接收不完整的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
Java使用雙異步實(shí)現(xiàn)將Excel的數(shù)據(jù)導(dǎo)入數(shù)據(jù)庫(kù)
在開(kāi)發(fā)中,我們經(jīng)常會(huì)遇到這樣的需求,將Excel的數(shù)據(jù)導(dǎo)入數(shù)據(jù)庫(kù)中,這篇文章主要來(lái)和大家講講Java如何使用雙異步實(shí)現(xiàn)將Excel的數(shù)據(jù)導(dǎo)入數(shù)據(jù)庫(kù),感興趣的可以了解下2024-01-01
springboot項(xiàng)目啟動(dòng)自動(dòng)跳轉(zhuǎn)到瀏覽器的操作代碼
這篇文章主要介紹了springboot項(xiàng)目啟動(dòng)自動(dòng)跳轉(zhuǎn)到瀏覽器的操作代碼,本文圖文實(shí)例代碼相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下2024-03-03
java開(kāi)發(fā)分布式服務(wù)框架Dubbo調(diào)用過(guò)程
這篇文章主要為大家介紹了java開(kāi)發(fā)分布式服務(wù)框架Dubbo調(diào)用過(guò)程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2021-11-11

