IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 手写4不润源码v2 -> 正文阅读

[Java知识库]手写4不润源码v2

类图(手写版类图是原版迷你版)
在这里插入图片描述
手写版Spring启动流程图
在这里插入图片描述

手写版调用流程图
在这里插入图片描述
项目结构图
在这里插入图片描述

代码:

	<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.6.7</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.giant-gator.spring</groupId>
	<artifactId>gator-spring</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>gator.spring</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
        </dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.25</version>
		</dependency>

		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>1.2.3</version>
		</dependency>
    </dependencies>

	<build>
		<finalName>${artifactId}</finalName>
		<resources>
			<resource>
				<directory>${basedir}/src/main/resources</directory>
				<includes>
					<include>**/*</include>
				</includes>
			</resource>
			<resource>
				<directory>${basedir}/src/main/java</directory>
				<excludes>
					<exclude>**/*.java</exclude>
					<exclude>**/*.class</exclude>
				</excludes>
			</resource>
		</resources>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.3.2</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
					<encoding>UTF-8</encoding>
					<compilerArguments>
						<verbose />
						<bootclasspath>${java.home}/lib/rt.jar</bootclasspath>
					</compilerArguments>
				</configuration>
			</plugin>

			<plugin>
				<artifactId>maven-resources-plugin</artifactId>
				<version>2.5</version>
				<executions>
					<execution>
						<id>copy-resources</id>
						<!-- here the phase you need -->
						<phase>validate</phase>
						<goals>
							<goal>copy-resources</goal>
						</goals>
						<configuration>
							<encoding>UTF-8</encoding>
							<outputDirectory>${basedir}/target/classes</outputDirectory>
							<resources>
								<resource>
									<directory>src/main/resources</directory>
									<includes>
										<include>**/*.*</include>
									</includes>
									<filtering>true</filtering>
								</resource>
							</resources>
						</configuration>
					</execution>
				</executions>
			</plugin>
			<plugin>
				<groupId>org.mortbay.jetty</groupId>
				<artifactId>maven-jetty-plugin</artifactId>
				<version>6.1.26</version>
				<configuration>
					<webDefaultXml>src/main/resources/webdefault.xml</webDefaultXml>
					<contextPath>/</contextPath>
					<connectors>
						<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
							<port>80</port>
						</connector>
					</connectors>
					<scanIntervalSeconds>0</scanIntervalSeconds>
					<scanTargetPatterns>
						<scanTargetPattern>
							<directory>src/main/webapp</directory>
							<includes>
								<include>**/*.xml</include>
								<include>**/*.properties</include>
							</includes>
						</scanTargetPattern>
					</scanTargetPatterns>
					<systemProperties>
						<systemProperty>
							<name>
								javax.xml.parsers.DocumentBuilderFactory
							</name>
							<value>
								com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
							</value>
						</systemProperty>
						<systemProperty>
							<name>
								javax.xml.parsers.SAXParserFactory
							</name>
							<value>
								com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl
							</value>
						</systemProperty>
						<systemProperty>
							<name>
								javax.xml.transform.TransformerFactory
							</name>
							<value>
								com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl
							</value>
						</systemProperty>
						<systemProperty>
							<name>org.eclipse.jetty.util.URI.charset</name>
							<value>UTF-8</value>
						</systemProperty>
					</systemProperties>
				</configuration>
			</plugin>

			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<version>2.2</version>
				<configuration>
					<archive>
						<addMavenDescriptor>false</addMavenDescriptor>
					</archive>
					<webResources>
						<resource>
							<!-- this is relative to the pom.xml directory -->
							<directory>src/main/resources/</directory>
							<targetPath>WEB-INF/classes</targetPath>
							<includes>
								<include>**/*.*</include>
							</includes>
							<filtering>true</filtering>
						</resource>
						<resource>
							<!-- this is relative to the pom.xml directory -->
							<directory>src/main/resources</directory>
							<targetPath>WEB-INF/classes</targetPath>
							<filtering>true</filtering>
						</resource>
					</webResources>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.zeroturnaround</groupId>
				<artifactId>javarebel-maven-plugin</artifactId>
				<executions>
					<execution>
						<id>generate-rebel-xml</id>
						<phase>process-resources</phase>
						<goals>
							<goal>generate</goal>
						</goals>
					</execution>
				</executions>
				<version>1.0.5</version>
			</plugin>
		</plugins>
		<pluginManagement>
			<plugins>
				<!--This plugin's configuration is used to store Eclipse m2e settings
                    only. It has no influence on the Maven build itself. -->
				<plugin>
					<groupId>org.eclipse.m2e</groupId>
					<artifactId>lifecycle-mapping</artifactId>
					<version>1.0.0</version>
					<configuration>
						<lifecycleMappingMetadata>
							<pluginExecutions>
								<pluginExecution>
									<pluginExecutionFilter>
										<groupId>
											org.zeroturnaround
										</groupId>
										<artifactId>
											javarebel-maven-plugin
										</artifactId>
										<versionRange>
											[1.0.5,)
										</versionRange>
										<goals>
											<goal>generate</goal>
										</goals>
									</pluginExecutionFilter>
									<action>
										<ignore></ignore>
									</action>
								</pluginExecution>
							</pluginExecutions>
						</lifecycleMappingMetadata>
					</configuration>
				</plugin>
			</plugins>
		</pluginManagement>
	</build>


</project>

切面类

package com.gator.spring.executor.aspect;

import com.gator.spring.framework.aop.aspect.PABLO_JoinPoint;
import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;

//切面类  被com.gator.spring.framework.aop.aspect.PABLO_AbstractAspectAdvice.invokeAdviceMethod调用
@Slf4j
public class LogAspect {

    //在调用一个方法之前,执行before方法
    public void before(PABLO_JoinPoint joinPoint){
        joinPoint.setUserAttribute("startTime_" + joinPoint.getMethod().getName(),System.currentTimeMillis());
        //这个方法中的逻辑,是由我们自己写的
        log.info("Invoker Before Method!!!" +
                "\nTargetObject:" +  joinPoint.getThis() +
                "\nArgs:" + Arrays.toString(joinPoint.getArguments()));
    }

    //在调用一个方法之后,执行after方法
    public void after(PABLO_JoinPoint joinPoint){
        log.info("Invoker After Method!!!" +
                "\nTargetObject:" +  joinPoint.getThis() +
                "\nArgs:" + Arrays.toString(joinPoint.getArguments()));
        long startTime = (Long) joinPoint.getUserAttribute("startTime_" + joinPoint.getMethod().getName());
        long endTime = System.currentTimeMillis();
        System.out.println("use time :" + (endTime - startTime));
    }

    public void afterThrowing(PABLO_JoinPoint joinPoint, Throwable ex){
        log.info("出现异常" +
                "\nTargetObject:" +  joinPoint.getThis() +
                "\nArgs:" + Arrays.toString(joinPoint.getArguments()) +
                "\nThrows:" + ex.getMessage());
    }

}

controller

package com.gator.spring.executor.controller;

import com.gator.spring.executor.service.IModifyService;
import com.gator.spring.executor.service.IQueryService;
import com.gator.spring.framework.annotation.PABLO_Autowired;
import com.gator.spring.framework.annotation.PABLO_Controller;
import com.gator.spring.framework.annotation.PABLO_RequestMapping;
import com.gator.spring.framework.annotation.PABLO_RequestParam;
import com.gator.spring.framework.webmvc.servlet.PABLO_ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author PABLO
 * @Date 2022/5/7 13:39
 * @Desc
 */
@PABLO_Controller
@PABLO_RequestMapping("/web")
public class MyTestController {


    @PABLO_Autowired
    IQueryService queryService;
    @PABLO_Autowired
    IModifyService modifyService;

    @PABLO_RequestMapping("/query.json")
    public PABLO_ModelAndView query(HttpServletRequest request, HttpServletResponse response,
                                    @PABLO_RequestParam("name") String name) {
        String result = queryService.query(name);
        return out(response, result);
    }

    @PABLO_RequestMapping("/add*.json")
    public PABLO_ModelAndView add(HttpServletRequest request, HttpServletResponse response,
                                  @PABLO_RequestParam("name") String name, @PABLO_RequestParam("addr") String addr) {
        String result = modifyService.add(name, addr);
        return out(response, result);
    }

    @PABLO_RequestMapping("/remove.json")
    public PABLO_ModelAndView remove(HttpServletRequest request, HttpServletResponse response,
                                     @PABLO_RequestParam("id") Integer id) {
        String result = modifyService.remove(id);
        return out(response, result);
    }

    @PABLO_RequestMapping("/edit.json")
    public PABLO_ModelAndView edit(HttpServletRequest request, HttpServletResponse response,
                                   @PABLO_RequestParam("id") Integer id,
                                   @PABLO_RequestParam("name") String name) {
        String result = modifyService.edit(id, name);
        return out(response, result);
    }


    private PABLO_ModelAndView out(HttpServletResponse resp, String str) {
        try {
            //resp.getWriter().write(str);
            Map<String,String> model = new HashMap<>();
            model.put("teacher","pablo");
            model.put("token","djhsjksmcvnff65f46ds5654xcss");
            model.put("data","何章怀晓");
            PABLO_ModelAndView pablo_modelAndView = new PABLO_ModelAndView("first",model);
            return pablo_modelAndView;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}

接口

package com.gator.spring.executor.service;


public interface IModifyService {

	/**
	 * 增加
	 */
	public String add(String name, String addr);
	
	/**
	 * 修改
	 */
	public String edit(Integer id, String name);
	
	/**
	 * 删除
	 */
	public String remove(Integer id);
	
}
public interface IQueryService {
	
	/**
	 * 查询
	 */
	public String query(String name);
}

实现类

@PABLO_Service
public class ModifyServiceImpl implements IModifyService {

	/**
	 * 增加
	 */
	public String add(String name,String addr) {
		return "modifyService add,name=" + name + ",addr=" + addr;
	}

	/**
	 * 修改
	 */
	public String edit(Integer id,String name) {
		return "modifyService edit,id=" + id + ",name=" + name;
	}

	/**
	 * 删除
	 */
	public String remove(Integer id) {
		return "modifyService id=" + id;
	}


	@Override
	public String toString() {
		return "ModifyServiceImpl{}";
	}
}
@PABLO_Service
@Slf4j
public class QueryServiceImpl implements IQueryService {

	/**
	 * 查询
	 */
	public String query(String name) {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		String time = sdf.format(new Date());
		String json = "{name:\"" + name + "\",time:\"" + time + "\"}";
		log.info("这是在业务方法中打印的:" + json);
		return json;
	}

	@Override
	public String toString() {
		return "QueryServiceImpl{}";
	}
}

注解

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PABLO_Autowired {
	String value() default "";
}

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PABLO_Controller {
	String value() default "";
}
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PABLO_RequestMapping {
	String value() default "";
}

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PABLO_RequestParam {
	
	String value() default "";
	
	boolean required() default true;

}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PABLO_Service {
	String value() default "";
}

aspect

package com.gator.spring.framework.aop.aspect;

import java.lang.reflect.Method;

/**
 * @Authror PABLO
 * @Date 2022/5/8 17:15
 * @Desc 抽象通知接口实现
 */
public abstract class PABLO_AbstractAspectAdvice {
    //切面方法  如before after...
    private Method aspectMethod;
    //切面所在的类 如自定义的LogAspect
    private Object aspectTarget;

    public PABLO_AbstractAspectAdvice(Method aspectMethod, Object aspectTarget) {
        this.aspectMethod = aspectMethod;
        this.aspectTarget = aspectTarget;
    }

    /**
     * @Description:
     * @Author: PABLO
     * @Date: 2022/5/9 23:42
     * @Params: [joinPoint连接点, returnValue返回值, tx异常信息]
     * @Return: java.lang.Object
     **/
    //被每个通知(拦截器链节点)调用
    public Object invokeAdviceMethod(PABLO_JoinPoint joinPoint, Object returnValue, Throwable tx) throws Throwable {
        //获得切面方法的参数列表
        Class<?>[] paramTypes = this.aspectMethod.getParameterTypes();
        //如参数直接反射调用
        if (null == paramTypes || paramTypes.length == 0) {
            return this.aspectMethod.invoke(aspectTarget);
        } else {
            //有参数,匹配这三个入参
            Object[] args = new Object[paramTypes.length];
            for (int i = 0; i < paramTypes.length; i++) {
                if (paramTypes[i] == PABLO_JoinPoint.class) {
                    args[i] = joinPoint;
                } else if (paramTypes[i] == Throwable.class) {
                    args[i] = tx;
                } else if (paramTypes[i] == Object.class) {
                    args[i] = returnValue;
                }
            }//根据aspectMethod确定是哪一个通知,调用该通知的具体方法,如before,这里会调用before的
            //com.gator.spring.executor.aspect.LogAspect.before
            return this.aspectMethod.invoke(aspectTarget, args);
        }
    }
}
/**
 * @Author PABLO
 * @Date 2022/5/8 17:26
 * @Desc 方法返回通知拦截器   在执行拦截器链的时候,会依次回调这里的invoke方法
 */
public class PABLO_AfterReturningAdviceInterceptor
        extends PABLO_AbstractAspectAdvice
        implements PABLO_Advice, PABLO_MethodInterceptor {
    //切点连接点,即每个method和它对应的拦截器链,这里保存是为了继续执行责任链模式,每个通知(拦截器)都是一个责任链节点
    private PABLO_JoinPoint joinPoint;

    public PABLO_AfterReturningAdviceInterceptor(Method aspectMethod, Object aspectTarget) {
        super(aspectMethod, aspectTarget);
    }

    @Override
    public Object invoke(PABLO_MethodInvocation invocation) throws Throwable {
        //如果非before,直接回调proceed,继续执行下一个拦截链,直到执行目标方法
        //等待方法栈回退,会后到这里后,retVal已经是目标方法的返回值了,再从这里依次往后直行
        Object retVal = invocation.proceed();
        this.joinPoint = invocation;
        this.afterReturning(retVal,invocation.getMethod(),invocation.getArguments(),invocation.getThis());
        return retVal;
    }

    private void afterReturning(Object retVal, Method method, Object[] arguments, Object aThis) throws Throwable {
        super.invokeAdviceMethod(this.joinPoint,retVal,null);//这里将joinPoint传入是为了从拦截器链下标的位置继续匹配对应通知(拦截器),继续执行
    }
}

/**
 * @Author PABLO
 * @Date 2022/5/8 17:28
 * @Desc 异常通知   在执行拦截器链的时候,会依次回调这里的invoke方法
 */
public class PABLO_AfterThrowingAdviceInterceptor
        extends PABLO_AbstractAspectAdvice
        implements PABLO_Advice, PABLO_MethodInterceptor {
    //异常名称
    private String throwingName;
    public PABLO_AfterThrowingAdviceInterceptor(Method aspectMethod, Object aspectTarget) {
        super(aspectMethod, aspectTarget);
    }

    @Override
    public Object invoke(PABLO_MethodInvocation invocation) throws Throwable {
        try {
            return invocation.proceed();
        }catch (Throwable e){
            invokeAdviceMethod(invocation,null,e.getCause());
            throw e;
        }
    }

    public void setThrowName(String throwName){
        this.throwingName = throwName;
    }
}

/**
 * @Author PABLO
 * @Date 2022/5/8 17:20
 * @Desc 代表一个运行时 连接点,即被代理的方法
 *         注:静态连接点是被代理的method本身,动态连接点是method的增强方法,在Spring中可getStaticPart调用
 */
public interface PABLO_JoinPoint {
    //被代理实例对象
    Object getThis();
    //参数列表
    Object[] getArguments();
    //被代理的切点方法
    Method getMethod();

    void setUserAttribute(String key, Object value);

    Object getUserAttribute(String key);

}

/**
 * @Author PABLO
 * @Date 2022/5/8 17:17
 * @Desc 前置通知拦截器  在执行拦截器链的时候,会依次回调这里的invoke方法
 */
public class PABLO_MethodBeforeAdviceInterceptor
        extends PABLO_AbstractAspectAdvice
        implements PABLO_Advice, PABLO_MethodInterceptor {

    //切点  切点连接点,即每个method和它对应的拦截器链
    private PABLO_JoinPoint joinPoint;

    public PABLO_MethodBeforeAdviceInterceptor(Method aspectMethod, Object aspectTarget) {
        super(aspectMethod, aspectTarget);
    }

    private void before(Method method,Object[] args,Object target) throws Throwable{
        //传送了给织入参数
        //method.invoke(target);
        super.invokeAdviceMethod(this.joinPoint,null,null);

    }


    //被com.gator.spring.framework.aop.intercept.PABLO_MethodInvocation.proceed回调
    //执行完毕后
    //在回调到com.gator.spring.framework.aop.intercept.PABLO_MethodInvocation.proceed继续执行
    @Override
    public Object invoke(PABLO_MethodInvocation invocation) throws Throwable {
        //从被织入的代码中才能拿到,JoinPoint
        this.joinPoint = invocation;
        //先执行本节点自己添加的逻辑,即method的某一个拦截器节点
        before(invocation.getMethod(), invocation.getArguments(), invocation.getThis());
        //执行完毕后在回调到com.gator.spring.framework.aop.intercept.PABLO_MethodInvocation.proceed
        return invocation.proceed();
    }
}

aopconfig

/**
 * @Author PABLO
 * @Date 2022/5/8 17:23
 * @Desc 配置文件的类实现 application.properties
 */
@Data
public class PABLO_AopConfig {

    private String pointCut;
    private String aspectBefore;
    private String aspectAfter;
    private String aspectClass;
    private String aspectAfterThrow;
    private String aspectAfterThrowingName;

}

intercepter

/**
 * @Authror PABLO
 * @Date 2022/5/8 17:20
 * @Desc 切点拦截器(通知)顶层接口,每个通知都必须实现,在Spring中,以集合的形式保存每个method对应的执行器链
 * Map<Method,List<PABLO_MethodInterceptor> chain
 */
public interface PABLO_MethodInterceptor {

    /**
     * @Description: 通过method对应的 连接链集合  依次调用
     * @Author: PABLO
     * @Date: 2022/5/9 13:40
     * @Params: [invocation 每一个动态连接链对象,即每个method对应的拦截器list]
     * @Return: java.lang.Object
     **/
    Object invoke(PABLO_MethodInvocation invocation) throws Throwable;

}

package com.gator.spring.framework.aop.intercept;

import com.gator.spring.framework.aop.aspect.PABLO_JoinPoint;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @Author PABLO
 * @Date 2022/5/8 17:20
 * @Desc 切点method的拦截器链(动态连接点),提供执行链条上节点的 process 方法
 */
public class PABLO_MethodInvocation implements PABLO_JoinPoint {
    //代理对象
    private Object proxy;
    //切点方法
    private Method method;
    //被代理的实例对象
    private Object target;
    //切点方法的参数列表
    private Object[] arguments;
    //目标方法拦截器上的链集合,即每个method对应的通知集合
    private List<Object> interceptorsAndDynamicMethodMatchers;
    //目标类的class
    private Class<?> targetClass;

    private Map<String, Object> userAttributes;

    //定义一个索引,从-1开始来记录当前拦截器执行的位置
    private int currentInterceptorIndex = -1;

    public PABLO_MethodInvocation(
            Object proxy, Object target, Method method, Object[] arguments,
            Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {

        this.proxy = proxy;
        this.target = target;
        this.targetClass = targetClass;
        this.method = method;
        this.arguments = arguments;
        this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
    }

    /**
     * @Description: 真正执行拦截器链的方法
     * 调用完毕会转到链的下一个拦截器上,执行完毕后最后执行目标方法
     * @Author: PABLO
     * @Date: 2022/5/9 12:41
     * @Params: []
     * @Return: java.lang.Object
     **/
    public Object proceed() throws Throwable {
        //如果Interceptor执行完了,则执行joinPoint 即目标方法
        if (Objects.isNull(this.interceptorsAndDynamicMethodMatchers)
                || this.interceptorsAndDynamicMethodMatchers.isEmpty()
                || this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            return this.method.invoke(this.target, this.arguments);
        }
        //获取下一个拦截器节点
        Object interceptorOrInterceptionAdvice =
                this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
        //拦截器链上的每个节点必须是PABLO_MethodInterceptor规范
        if (interceptorOrInterceptionAdvice instanceof PABLO_MethodInterceptor) {
            //获得切点的某一个具体的拦截器,如前置 后置  异常通知拦截器等
            PABLO_MethodInterceptor mi = (PABLO_MethodInterceptor) interceptorOrInterceptionAdvice;
            //如调用com.gator.spring.framework.aop.aspect.PABLO_MethodBeforeAdviceInterceptor.invoke
            return mi.invoke(this);
        } else {
            //动态匹配失败时,略过当前PABLO_MethodInterceptor,调用下一个Interceptor
            return proceed();
        }
    }

    @Override
    public Object getThis() {
        return this.target;
    }

    @Override
    public Object[] getArguments() {
        return this.arguments;
    }

    @Override
    public Method getMethod() {
        return this.method;
    }

    @Override
    public void setUserAttribute(String key, Object value) {
        if (value != null) {
            if (this.userAttributes == null) {
                this.userAttributes = new HashMap<String, Object>();
            }
            this.userAttributes.put(key, value);
        } else {
            if (this.userAttributes != null) {
                this.userAttributes.remove(key);
            }
        }
    }

    @Override
    public Object getUserAttribute(String key) {
        return (this.userAttributes != null ? this.userAttributes.get(key) : null);
    }
}

support

 package com.gator.spring.framework.aop.support;

import com.gator.spring.framework.aop.aspect.PABLO_AfterReturningAdviceInterceptor;
import com.gator.spring.framework.aop.aspect.PABLO_AfterThrowingAdviceInterceptor;
import com.gator.spring.framework.aop.aspect.PABLO_MethodBeforeAdviceInterceptor;
import com.gator.spring.framework.aop.config.PABLO_AopConfig;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Author PABLO
 * @Date 2022/5/8 13:34
 * @Desc 回调代理通知的扩展类,切点被执行时会先调用代理 AOP核心思想
 */
public class PABLO_AdvisedSupport {
    //被代理的对象的class  即目标类(切点)的class
    private Class<?> targetClass;
    //被代理的实例对象
    private Object target;
    //配置文件映射对象
    private PABLO_AopConfig config;
    //正则编译  用来匹配正在实例化的对象是否符合代理要求
    private Pattern pointCutClassPattern;
    //切点方法缓存,value保存该方法所有拦截器
    //transient修饰的变量不能序列化和反序列化
    private transient Map<Method, List<Object>> methodCache;

    public PABLO_AdvisedSupport(PABLO_AopConfig config) {
        this.config = config;
    }

    public Class<?> getTargetClass() {
        return this.targetClass;
    }

    public Object getTarget() {
        return this.target;
    }

    /**
     * @Description: 根据method获取切点方法的拦截器链
     * @Author: PABLO
     * @Date: 2022/5/9 12:46
     * @Params: [method, targetClass]
     * @Return: java.util.List<java.lang.Object>
     **/
    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) throws Exception {
        //每个切点方法在parse时都会被加载到缓存中,这里根据method获取该method的拦截器链
        //Object必须是一个intercept
        // --> 即PABLO_MethodInterceptor的实现类,如前置、后置、异常通知,最终调用每个通知的invoke
        List<Object> cached = methodCache.get(method);
        //缓存没有
        if (cached == null) {
            //根据参数类型列表和方法名称得到对应method
            Method m = targetClass.getMethod(method.getName(), method.getParameterTypes());
            cached = methodCache.get(m);
            //底层逻辑,对代理方法进行一个兼容处理
            this.methodCache.put(m, cached);
        }
        return cached;
    }

    public void setTargetClass(Class<?> targetClass) {
        this.targetClass = targetClass;
        parse();
    }


    /**
     * @Description: 解析配置文件中的AOP设置项 生成拦截器链
     * @Author: PABLO
     * @Date: 2022/5/9 14:41
     * @Params: []
     * @Return: void
     **/
    private void parse() {
        String pointCut = config.getPointCut()
                .replaceAll("\\.", ".")
                .replaceAll("\\\\.\\*", ".*")
                .replaceAll("\\(", "\\\\(")
                .replaceAll("\\)", "\\\\)");
        //pointCut=public .* com.gator.spring.executor.service..*Service..*(.*)

        //玩正则
        String pointCutForClassRegex = pointCut.substring(0, pointCut.lastIndexOf("\\(") - 2);
        //class com.gator.spring.executor.service.impl.*
        pointCutClassPattern = Pattern.compile("class " + pointCutForClassRegex.substring(
                pointCutForClassRegex.lastIndexOf(" ") + 1));
        try {

            methodCache = new HashMap<Method, List<Object>>();
            Pattern pattern = Pattern.compile(pointCutForClassRegex);
            //com.gator.spring.executor.aspect.LogAspect
            Class aspectClass = Class.forName(this.config.getAspectClass());
            Map<String, Method> aspectMethods = new HashMap<String, Method>();
            //将切面类中所有通知方法存入集合中,在下面匹配切点,匹配上的存入 对应 method的 拦截器链
            for (Method m : aspectClass.getDeclaredMethods()) {
                //切面方法,即每个通知
                aspectMethods.put(m.getName(), m);
            }
            //获取目标类方法,但不包括继承的方法
            for (Method m : this.targetClass.getDeclaredMethods()) {
                String methodString = m.toString();
                //处理方法上的异常
                if (methodString.contains("throws")) {
                    //截取目标的方法名称
                    methodString = methodString.substring(0, methodString.lastIndexOf("throws")).trim();
                }
                //【核心】正则匹配方法名,如匹配,构建执行器链,即将目标方法包装为拦截器!!!!!!!!!
                Matcher matcher = pattern.matcher(methodString);
                if (matcher.matches()) {
                    //执行器链
                    List<Object> advices = new LinkedList<Object>();
                    //把每一个方法包装成 MethodIterceptor拦截器链的节点
                    //before
                    if (!(null == config.getAspectBefore() || "".equals(config.getAspectBefore()))) {
                        //创建前置通知对象
                        advices.add(new PABLO_MethodBeforeAdviceInterceptor(aspectMethods.get(config.getAspectBefore()), aspectClass.newInstance()));
                    }
                    //after
                    if (!(null == config.getAspectAfter() || "".equals(config.getAspectAfter()))) {
                        //创建后置通知对象
                        advices.add(new PABLO_AfterReturningAdviceInterceptor(aspectMethods.get(config.getAspectAfter()), aspectClass.newInstance()));
                    }
                    //afterThrowing
                    if (!(null == config.getAspectAfterThrow() || "".equals(config.getAspectAfterThrow()))) {
                        //创建一个Advivce
                        PABLO_AfterThrowingAdviceInterceptor throwingAdvice =
                                new PABLO_AfterThrowingAdviceInterceptor(
                                        aspectMethods.get(config.getAspectAfterThrow()),
                                        aspectClass.newInstance());
                        throwingAdvice.setThrowName(config.getAspectAfterThrowingName());
                        advices.add(throwingAdvice);
                    }
                    methodCache.put(m, advices);
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    public void setTarget(Object target) {
        this.target = target;
    }

    public boolean pointCutMatch() {
        return pointCutClassPattern.matcher(this.targetClass.toString()).matches();
    }
}

/**
 * @Author PABLO
 * @Date 2022/5/8 13:34
 * @Desc 代理顶层接口,在运行时结合在一起,非运行时是独立的
 *       两种实现 JDK和CGLIB
 *       可获得代理对象
 */
public interface PABLO_AopProxy {


    Object getProxy();


    Object getProxy(ClassLoader classLoader);
}

import com.gator.spring.framework.aop.support.PABLO_AdvisedSupport;

/**
 * @Author PABLO
 * @Date 2022/5/8 13:34
 * @Desc GLIB动态代理实现
 */
public class PABLO_CglibAopProxy implements PABLO_AopProxy {
    public PABLO_CglibAopProxy(PABLO_AdvisedSupport config) {
    }

    @Override
    public Object getProxy() {
        return null;
    }

    @Override
    public Object getProxy(ClassLoader classLoader) {
        return null;
    }
}


package com.gator.spring.framework.aop;

import com.gator.spring.framework.aop.intercept.PABLO_MethodInvocation;
import com.gator.spring.framework.aop.support.PABLO_AdvisedSupport;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;

/**
 * @Author PABLO
 * @Date 2022/5/8 13:34
 * @Desc JDK动态代理
 */
public class PABLO_JdkDynamicAopProxy implements PABLO_AopProxy, InvocationHandler {

    //回调通知的扩展   切点被执行时会回调代理,即调用这里的invoke
    private PABLO_AdvisedSupport advised;

    public PABLO_JdkDynamicAopProxy(PABLO_AdvisedSupport config) {
        this.advised = config;
    }

    @Override
    public Object getProxy() {
        return getProxy(this.advised.getTargetClass().getClassLoader());
    }

    /**
     * @Description: 创建代理对象
     * @Author: PABLO
     * @Date: 2022/5/9 12:36
     * @Params: [classLoader]
     * @Return: java.lang.Object
     **/
    @Override
    public Object getProxy(ClassLoader classLoader) {
            return Proxy.newProxyInstance(classLoader, this.advised.getTargetClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //通过method(key)获得该切点的拦截器链
        //每个方法和对应的执行器链保存在map中,Map<Method,List<PABLO_MethodInterceptor> chain
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, this.advised.getTargetClass());
        //构建执行拦截器链对象,会依次执行链条上的方法
        PABLO_MethodInvocation invocation = new PABLO_MethodInvocation(proxy, this.advised.getTarget(), method, args, this.advised.getTargetClass(), chain);
        //执行拦截器链,proceed执行完链条上所有节点后会执行真正的目标方法
        return invocation.proceed();
    }
}



beans

/**
 * @Author PABLO
 * @Date 2022/5/7 13:13
 * @Desc bean初始化增强器
 */
public class PABLO_BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception {
        System.out.println( "-----"+beanName+"初始化对象之前");
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
        System.out.println( "-----"+beanName+"初始化对象之后");
        return bean;
    }
}



/**
 * @Author PABLO
 * @Date 2022/5/7 11:45
 * @Desc bean描述,存储配置信息
 */
@Data
public class PABLO_BeanDefinition {

    private String beanClassName; //类的完全限定名
    private String factoryBeanName; //对象正在工厂内的名称,默认首字母小写
    private boolean lazyInit = false;
    private boolean isSingleton = true;
}

package com.gator.spring.framework.beans.support;

import com.gator.spring.framework.beans.factory.config.PABLO_BeanDefinition;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * @Author PABLO
 * @Date 2022/5/7 12:19
 * @Desc definition读取器
 */
public class PABLO_BeanDefinitionReader {

    //保存所有需要注册的class
    private List<String> registryBeanClasses = new ArrayList<String>();

    private Properties config = new Properties();

    //固定配置文件中的key,相对于xml的规范
    private final String SCAN_PACKAGE = "scanPackage";

    /**
     * @Description: 将入参配置文件加载到内存
     * @Author: PABLO
     * @Date: 2022/5/7 12:21
     * @Params: [locations]
     * @Return:
     **/
    public PABLO_BeanDefinitionReader(String... locations) {

        //通过URL定位找到其所对应的文件,然后转换为文件流
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(locations[0].replace("classpath:", ""));
        try {
            //加载配置
            config.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        //多路径扫描
        String property = config.getProperty(SCAN_PACKAGE);
        for (String scanPackage : property.split(",")) {
            doScanner(scanPackage);
        }

    }

    private void doScanner(String scanPackage) {
        //转换为文件路径,实际上就是把.替换为/就OK了
        //处理每个包,将包路径转为文件路径  .-->/
        URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
        //System.out.println("url" + url);
        File dir = new File(url.getFile());
        for (File file : dir.listFiles()) {
            //如果是文件夹,继续递归
            if (file.isDirectory()) {
                doScanner(scanPackage + "." + file.getName());
            } else {
                //E:\IDEALocation\giant-gator\target\classes\com\bj\summary\spring_mvc\controller\TestController.class
                registryBeanClasses.add(scanPackage + "." + file.getName().replace(".class", "").trim());
            }
        }
    }

    public Properties getConfig() {
        return this.config;
    }


    /**
     * @Description: 配置信息封装为definition规范
     *              注意:我这里定义的规则是:(可自定义)
     *                  如果是实现类,可使用实现类完全限定名/实现类首字母小写/接口完全限定名/
     *                  如果是普通类,可使用类的完全限定名/类名小写getBean获取对象
     * @Author: PABLO
     * @Date: 2022/5/7 12:29
     * @Params: []
     * @Return: java.util.List<GPBeanDefinition>
     **/
    public List<PABLO_BeanDefinition> loadBeanDefinitions() {

        List<PABLO_BeanDefinition> result = new ArrayList<PABLO_BeanDefinition>();
        try {
            for (String className : registryBeanClasses) {
                Class<?> beanClass = Class.forName(className);
                //如遇接口使用其实现类初始化
                if (beanClass.isInterface()) continue;
                //beanName有三种情况:
                //1、默认是类名首字母小写
                //2、自定义名字
                //3、接口注入
                //判断首字母大小写,如小写直接添加,如大写需转换
                //beanClass.getSimpleName()是类名
                //beanClass.getName()是类的完全限定名
                //保存小写名称的映射关系
                char c = beanClass.getSimpleName().toCharArray()[0];
                result.add(doCreateBeanDefinition(Character.isLowerCase(c)
                                ? beanClass.getSimpleName()
                                : toLowerFirstCase(beanClass.getSimpleName()),
                        beanClass.getName()));
                //保存完全限定名的映射关系
                result.add(doCreateBeanDefinition(beanClass.getName(), beanClass.getName()));
                //获取该实现类的接口列表
                Class<?>[] interfaces = beanClass.getInterfaces();
                for (Class<?> i : interfaces) {
                    //如果是多个实现类,只能覆盖
                    //为什么?因为Spring没那么智能,就是这么傻
                    //这个时候,可以自定义名字
                    //保存接口的完全限定名的映射关系
                    result.add(doCreateBeanDefinition(i.getName(), beanClass.getName()));
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * @Description: 将配置信息解析为definition规范
     * @Author: PABLO
     * @Date: 2022/5/7 12:31
     * @Params: [factoryBeanName, beanClassName 是完全限定名]
     * @Return: GPBeanDefinition
     **/
    private PABLO_BeanDefinition doCreateBeanDefinition(String factoryBeanName, String beanClassName) {

        PABLO_BeanDefinition beanDefinition = new PABLO_BeanDefinition();
        beanDefinition.setBeanClassName(beanClassName);
        beanDefinition.setFactoryBeanName(factoryBeanName);
        //System.out.println(beanDefinition);
        return beanDefinition;
    }

    /**
     * @Description:大写转小写
     * @Author: PABLO
     * @Date: 2022/5/7 12:34
     * @Params: [simpleName]
     * @Return: java.lang.String
     **/
    private String toLowerFirstCase(String simpleName) {
        char[] chars = simpleName.toCharArray();
        //大小写字母的ASCII码相差32,
        //而且大写字母的ASCII码要小于小写字母的ASCII码
        //在Java中,对char做算学运算,实际上就是对ASCII码做算学运算
        chars[0] += 32;
        return String.valueOf(chars);
    }

}


package com.gator.spring.framework.beans.support;

import com.gator.spring.framework.beans.factory.config.PABLO_BeanDefinition;
import com.gator.spring.framework.context.support.PABLO_AbstractApplicationContext;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author PABLO
 * @Date 2022/5/7 11:36
 * @Desc IOC容器的默认实现  兜底
 */
public class PABLO_DefaultListableBeanFactory
        extends PABLO_AbstractApplicationContext {


    //存储对象描述信息map集合
    //key为对象在BeanFactory中的名称,默认小写字母开头
    public final Map<String, PABLO_BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, PABLO_BeanDefinition>(256);

    @Override
    public Object getBean(String beanName) throws Exception {
        return null;
    }
}

package com.gator.spring.framework.beans;

/**
 * @Author PABLO
 * @Date 2022/5/7 12:47
 * @Desc 对象包装类  创建对象成功后返回的装饰器
 */
public class PABLO_BeanWrapper {
    //被包装的bean实例
    private Object wrappedInstance;
    //被包装的实例类型
    private Class<?> wrappedClass;

    public PABLO_BeanWrapper(Object wrappedInstance){
        this.wrappedInstance = wrappedInstance;
        this.wrappedClass = wrappedInstance.getClass();
    }

    public Object getWrappedInstance(){
        return this.wrappedInstance;
    }

    // 返回代理以后的Class
    // 可能会是这个 $Proxy0
    public Class<?> getWrappedClass(){
        return this.wrappedClass;
    }


    @Override
    public String toString() {
        return "PABLO_BeanWrapper{" +
                "wrappedInstance=" + wrappedInstance +
                ", wrappedClass=" + wrappedClass +
                '}';
    }
}


context

package com.gator.spring.framework.context.support;

import com.gator.spring.framework.core.PABLO_BeanFactory;

/**
 * @Author PABLO
 * @Date 2022/5/7 11:30
 * @Desc IOC容器的顶层设计
 */
public abstract class PABLO_AbstractApplicationContext implements PABLO_BeanFactory {
    
    /**
     * @Description: 模板方法,供子类重写生效 可一键重启
     * @Author: PABLO
     * @Date: 2022/5/7 11:32
     * @Params: 
     * @Return: 
     **/
    protected void refresh() throws Exception {}
}


package com.gator.spring.framework.context;

import com.gator.spring.framework.annotation.PABLO_Autowired;
import com.gator.spring.framework.annotation.PABLO_Controller;
import com.gator.spring.framework.annotation.PABLO_Service;
import com.gator.spring.framework.aop.PABLO_AopProxy;
import com.gator.spring.framework.aop.PABLO_CglibAopProxy;
import com.gator.spring.framework.aop.PABLO_JdkDynamicAopProxy;
import com.gator.spring.framework.aop.config.PABLO_AopConfig;
import com.gator.spring.framework.aop.support.PABLO_AdvisedSupport;
import com.gator.spring.framework.beans.PABLO_BeanWrapper;
import com.gator.spring.framework.beans.config.PABLO_BeanPostProcessor;
import com.gator.spring.framework.beans.factory.config.PABLO_BeanDefinition;
import com.gator.spring.framework.beans.support.PABLO_BeanDefinitionReader;
import com.gator.spring.framework.beans.support.PABLO_DefaultListableBeanFactory;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Authror PABLO
 * @Date 2022/5/7 11:25
 * @Desc 继承默认的IOC容器
 */
public class PABLO_ApplicationContext
        extends PABLO_DefaultListableBeanFactory {


    private String[] configLocations;
    //封装definition规范方法
    private PABLO_BeanDefinitionReader reader;

    //单例的IOC容器缓存
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>();
    //通用的IOC容器  缓存wrapper
    private Map<String, PABLO_BeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<String, PABLO_BeanWrapper>();


    public PABLO_ApplicationContext(String... configLocations) {
        this.configLocations = configLocations;
        try {
            refresh();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void refresh() throws Exception {
        //定位配置文件
        reader = new PABLO_BeanDefinitionReader(this.configLocations);
        //扫描加载代内存的类,封装为definition规范
        List<PABLO_BeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
        //注册 将配置信息注册到beanDefinitionMap中
        doRegisterBeanDefinition(beanDefinitions);
        //将非延迟加载的类,提前初始化
        doAutowired();


        //System.out.println(singletonObjects);

    }

    private void doAutowired() {
        for (Map.Entry<String, PABLO_BeanDefinition> beanDefinitionEntry : super.beanDefinitionMap.entrySet()) {
            //beanFactory中的name,默认首字母小写
            String beanName = beanDefinitionEntry.getKey();
            //非懒加载直接getBean
            if (!beanDefinitionEntry.getValue().isLazyInit()) {
                try {
                    getBean(beanName);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void doRegisterBeanDefinition(List<PABLO_BeanDefinition> beanDefinitions) throws Exception {
        for (PABLO_BeanDefinition beanDefinition : beanDefinitions) {
            if (super.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())) {
                throw new Exception("The “" + beanDefinition.getFactoryBeanName() + "” is exists!!");
            }
            super.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
        }
    }

    /**
     * @Description: 通过class获取对象
     * @Author: PABLO
     * @Date: 2022/5/7 20:38
     * @Params: [clazz]
     * @Return: java.lang.Object
     **/
    public Object getBean(Class clazz) throws Exception {
        return getBean(clazz.getName());
    }

    /**
     * @Description: 根据beanName实例化并初始化对象
     * @Author: PABLO
     * @Date: 2022/5/7 17:27
     * @Params: [beanName 支持类的完全限定名 和 首字母小写类名]
     * @Return: java.lang.Object
     **/
    @Override
    public Object getBean(String beanName) throws Exception {

        //先考虑只有单例
        //存在直接拿
        if (this.singletonObjects.containsKey(beanName)) {
            return this.singletonObjects.get(beanName);
        }
        //1.实例化
        //获取对象的描述信息
        PABLO_BeanDefinition pablo_beanDefinition = beanDefinitionMap.get(beanName);

        //根据名称和对应的描述信息实例化bean
        Object instance = instantiateBean(beanName, pablo_beanDefinition);

        //将对象封装到beanWrapper中
        PABLO_BeanWrapper beanWrapper = new PABLO_BeanWrapper(instance);

        //存储IOC容器
        factoryBeanInstanceCache.put(beanName, beanWrapper);
        //--------------------------------------------------------------------------
        //增强器
        PABLO_BeanPostProcessor postProcessor = new PABLO_BeanPostProcessor();
        //前置增强器
        postProcessor.postProcessBeforeInitialization(instance, beanName);
        //DI注入  初始化
        populateBean(beanName, new PABLO_BeanDefinition(), beanWrapper);
        //后置增强器
        postProcessor.postProcessAfterInitialization(instance, beanName);

        return this.factoryBeanInstanceCache.get(beanName).getWrappedInstance();
    }

    private void populateBean(String beanName, PABLO_BeanDefinition pablo_beanDefinition, PABLO_BeanWrapper beanWrapper) {
        Object instance = beanWrapper.getWrappedInstance();
        //获得实例对象的类 class com.gator.spring.executor.service.impl.xxxClass
        Class<?> clazz = beanWrapper.getWrappedClass();

        //判断只有加了注解的类,才执行依赖注入
        if (!(clazz.isAnnotationPresent(PABLO_Controller.class) || clazz.isAnnotationPresent(PABLO_Service.class)))
            return;
        //获得所有的fields
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            //没有注入注解跳过
            if (!field.isAnnotationPresent(PABLO_Autowired.class)) continue;
            PABLO_Autowired autowired = field.getAnnotation(PABLO_Autowired.class);
            //获取注解上的value值
            String autowiredBeanName = autowired.value().trim();
            //“”默认类型名字
            if ("".equals(autowiredBeanName)) autowiredBeanName = field.getType().getName();
            //强制访问
            field.setAccessible(true);
            try {
                if (this.factoryBeanInstanceCache.get(autowiredBeanName) == null) {
                    continue;
                }
                //属性注入
                field.set(instance, this.factoryBeanInstanceCache.get(autowiredBeanName).getWrappedInstance());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

        }
    }


    //beanName可能是小写名,也可能是全路径
    //我这里的逻辑默认创建的对象都是单例的,不涉及原型
    private Object instantiateBean(String beanName, PABLO_BeanDefinition pablo_beanDefinition) {
        Object instance = null;
        try {
            //获取该类完全限定名
            String className = pablo_beanDefinition.getBeanClassName();
            //反射创建
            Class<?> clazz = Class.forName(className);
            instance = clazz.newInstance();
            //加载代理配置
            PABLO_AdvisedSupport advisedSupport = loadAopConfig();
            advisedSupport.setTargetClass(clazz);
            advisedSupport.setTarget(instance);
            //实例化的类是否符合PointCut的规则的话,TRUE 创建代理对象
            //现在的匹配逻辑是com.gator.spring.executor.service.impl.*.*(..)中所有方法
            if (advisedSupport.pointCutMatch()) {
                instance = createProxy(advisedSupport).getProxy();
            }

            //this.singletonObjects.put(className, instance);
            //实现通过完全限定名和类名小写都能获取到对象
            this.singletonObjects.put(beanName, instance);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }

    public String[] getBeanDefinitionNames() {
        return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
    }

    public Properties getConfig() {
        return this.reader.getConfig();
    }

    /**
     * @Description: 根据对象信息创建代理对象
     * @Author: PABLO
     * @Date: 2022/5/9 14:38
     * @Params: [config]
     * @Return: com.gator.spring.framework.aop.PABLO_AopProxy
     **/
    private PABLO_AopProxy createProxy(PABLO_AdvisedSupport config) {
        Class targetClass = config.getTargetClass();
        if (targetClass.getInterfaces().length > 0) {
            return new PABLO_JdkDynamicAopProxy(config);
        }
        return new PABLO_CglibAopProxy(config);
    }

    private PABLO_AdvisedSupport loadAopConfig() {
        PABLO_AopConfig config = new PABLO_AopConfig();
        //加载配置文件
        config.setPointCut(this.reader.getConfig().getProperty("pointCut"));
        config.setAspectClass(this.reader.getConfig().getProperty("aspectClass"));
        config.setAspectBefore(this.reader.getConfig().getProperty("aspectBefore"));
        config.setAspectAfter(this.reader.getConfig().getProperty("aspectAfter"));
        config.setAspectAfterThrow(this.reader.getConfig().getProperty("aspectAfterThrow"));
        config.setAspectAfterThrowingName(this.reader.getConfig().getProperty("aspectAfterThrowingName"));
        return new PABLO_AdvisedSupport(config);
    }
}



package com.gator.spring.framework.context;

import org.springframework.beans.BeansException;


/**
 * @Authror PABLO
 * @Date 2022/5/7 11:58
 * @Desc 解耦获得IOC容器的顶层设计
 *       通过监听器(Observer)扫描所有类,只要实现此接口,将调用setApplicationContext(),将IOC容器注入到目标类中
 */
public interface PABLO_ApplicationContextAware {

    void setApplicationContext(PABLO_ApplicationContext applicationContext) throws BeansException;
}


core

package com.gator.spring.framework.core;

/**
 * @Authror PABLO
 * @Date 2022/5/7 11:21
 * @Desc 单例工厂的顶层设计
 */
public interface PABLO_BeanFactory {

    /**
     * @Description: 根据beanName获取对象
     * @Author: PABLO
     * @Date: 2022/5/7 11:23
     * @Params: [beanName]
     * @Return: java.lang.Object
     **/
    Object getBean(String beanName) throws Exception;

}

package com.gator.spring.framework.core;

/**
 * @Authror PABLO
 * @Date 2022/5/7 11:21
 * @Desc
 */
public interface PABLO_FactoryBean {
}

webmvc

package com.gator.spring.framework.webmvc.servlet;

import com.gator.spring.framework.annotation.PABLO_RequestParam;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author PABLO
 * @Date 2022/5/8 13:34
 * @Desc handler 适配器
 */
public class PABLO_HandlerAdapter {

    public boolean supports(Object handler){ return (handler instanceof PABLO_HandlerMapping);}

    //调用方法
    PABLO_ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{
        PABLO_HandlerMapping handlerMapping = (PABLO_HandlerMapping)handler;

        //把方法的形参列表和request的参数列表所在顺序进行一一对应
        Map<String,Integer> paramIndexMapping = new HashMap<String, Integer>();
        //handlerMapping.getMethod()是获取方法的参数类型列表
        Annotation[] [] pa = handlerMapping.getMethod().getParameterAnnotations();
        for (int i = 0; i < pa.length ; i ++) {
            for(Annotation a : pa[i]){
                if(a instanceof PABLO_RequestParam){
                    String paramName = ((PABLO_RequestParam) a).value();
                    //只记录value不为空的
                    if(!"".equals(paramName.trim())){
                        //记录下标
                        paramIndexMapping.put(paramName, i);
                    }
                }
            }
        }
        //记录req和response下标位置
        Class<?> [] paramsTypes = handlerMapping.getMethod().getParameterTypes();
        for (int i = 0; i < paramsTypes.length ; i ++) {
            Class<?> type = paramsTypes[i];
            if(type == HttpServletRequest.class ||
                    type == HttpServletResponse.class){
                paramIndexMapping.put(type.getName(),i);
            }
        }

        //获得方法的形参列表
        Map<String,String[]> params = request.getParameterMap();

        //实参列表,这个参数列表是需要invoke的实际参数
        Object [] paramValues = new Object[paramsTypes.length];

        for (Map.Entry<String, String[]> parm : params.entrySet()) {
            String value = Arrays.toString(parm.getValue()).replaceAll("\\[|\\]","")
                    .replaceAll("\\s",",");

            if(!paramIndexMapping.containsKey(parm.getKey())){continue;}

            int index = paramIndexMapping.get(parm.getKey());
            //指定下标位置的参数类型转换
            paramValues[index] = caseStringValue(value,paramsTypes[index]);
        }

        //处理req和response
        if(paramIndexMapping.containsKey(HttpServletRequest.class.getName())) {
            int reqIndex = paramIndexMapping.get(HttpServletRequest.class.getName());
            paramValues[reqIndex] = request;
        }

        if(paramIndexMapping.containsKey(HttpServletResponse.class.getName())) {
            int respIndex = paramIndexMapping.get(HttpServletResponse.class.getName());
            paramValues[respIndex] = response;
        }

        //调用
        Object result = handlerMapping.getMethod().invoke(handlerMapping.getController(),paramValues);
        if(result == null || result instanceof Void){ return null; }

        boolean isModelAndView = handlerMapping.getMethod().getReturnType() == PABLO_ModelAndView.class;
        if(isModelAndView){
            return (PABLO_ModelAndView) result;
        }
        return null;
    }


    private Object caseStringValue(String value, Class<?> paramsType) {
        if(String.class == paramsType){
            return value;
        }
        //如果是int
        if(Integer.class == paramsType){
            return Integer.valueOf(value);
        }
        else if(Double.class == paramsType){
            return Double.valueOf(value);
        }else {
            if(value != null){
                return value;
            }
            return null;
        }
        //可用策略模式

    }
}

package com.gator.spring.framework.webmvc.servlet;

import java.lang.reflect.Method;
import java.util.regex.Pattern;

/**
 * @Author PABLO
 * @Date 2022/5/8 13:34
 * @Desc handler映射器,建立request的请求路径URL和 某controller中某方法路径的映射
 */
public class PABLO_HandlerMapping {

    //方法对应实例对象  如/web
    private Object controller;
    //具体执行方法  如/add
    private Method method;
    //URL的正则匹配  /web/add
    private Pattern pattern;

    public PABLO_HandlerMapping(Pattern pattern, Object controller, Method method) {
        this.controller = controller;
        this.method = method;
        this.pattern = pattern;
    }

    @Override
    public String toString() {
        return "PABLO_HandlerMapping{" +
                "controller=" + controller +
                ", method=" + method +
                ", pattern=" + pattern +
                '}';
    }

    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;
    }
}

package com.gator.spring.framework.webmvc.servlet;

import java.util.Map;

/**
 * @Author PABLO
 * @Date 2022/5/8 13:39
 * @Desc
 */
public class PABLO_ModelAndView {

    //视图名称  如404...
    private String viewName;
    //该视图中需要被替换内容位置的集合
    private Map<String,?> model;

    public PABLO_ModelAndView(String viewName) { this.viewName = viewName; }

    public PABLO_ModelAndView(String viewName, Map<String, ?> model) {
        this.viewName = viewName;
        this.model = model;
    }
    public String getViewName() {
        return viewName;
    }


    public Map<String, ?> getModel() {
        return model;
    }

}

package com.gator.spring.framework.webmvc.servlet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.RandomAccessFile;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Author PABLO
 * @Date 2022/5/8 13:48
 * @Desc 视图
 */
public class PABLO_View {
    //Context-Type
    public final String DEFAULT_CONTENT_TYPE = "text/html;charset=utf-8";
    public final String CHARACTER_ENCODING = "UTF-8";

    private File viewFile;

    public PABLO_View(File viewFile) {
        this.viewFile = viewFile;
    }

    /**
     * @Description: 渲染,直接输出了
     * @Author: PABLO
     * @Date: 2022/5/8 13:51
     * @Params: [model, request, response]
     * @Return: void
     **/
    public void render(Map<String, ?> model,
                       HttpServletRequest request, HttpServletResponse response) throws Exception{
        StringBuffer sb = new StringBuffer();

        //设置访问返回的页面文件
        RandomAccessFile ra = new RandomAccessFile(this.viewFile,"r");

        String line  = null;
        //读取每一行
        while (null != (line = ra.readLine())){
            line = new String(line.getBytes("ISO-8859-1"),"utf-8");
            //正则,只要该行包含指定表达式,就会填充
            Pattern pattern = Pattern.compile("¥\\{[^\\}]+\\}",Pattern.CASE_INSENSITIVE);
            Matcher matcher = pattern.matcher(line);
            while (matcher.find()){
                String paramName = matcher.group();//返回匹配的位置
                paramName = paramName.replaceAll("¥\\{|\\}","");//取出文件中对应的占位符名称  如¥{teacher},取出teacher
                Object paramValue = model.get(paramName);
                if(null == paramValue){ continue;}
                line = matcher.replaceFirst(makeStringForRegExp(paramValue.toString()));
                matcher = pattern.matcher(line);
            }
            sb.append(line);//填充后保存源文件的内容
        }

        response.setCharacterEncoding(CHARACTER_ENCODING);
        response.getWriter().write(sb.toString());
    }

    //处理特殊字符
    public static String makeStringForRegExp(String str) {
        return str.replace("\\", "\\\\").replace("*", "\\*")
                .replace("+", "\\+").replace("|", "\\|")
                .replace("{", "\\{").replace("}", "\\}")
                .replace("(", "\\(").replace(")", "\\)")
                .replace("^", "\\^").replace("$", "\\$")
                .replace("[", "\\[").replace("]", "\\]")
                .replace("?", "\\?").replace(",", "\\,")
                .replace(".", "\\.").replace("&", "\\&");
    }
}

package com.gator.spring.framework.webmvc.servlet;

import java.io.File;
import java.util.Locale;

/**
 * @Author PABLO
 * @Date 2022/5/8 13:34
 * @Desc 视图解析器
 */
public class PABLO_ViewResolver {

    private final String DEFAULT_TEMPLATE_SUFFIX = ".html";
    //页面文件,这是一个文件夹,放置多个文件,如需要多种模板,改为File[]即可
    private File templateRootDir;


    public PABLO_ViewResolver(String templateRoot) {
        String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();
        //将该目录中所有页面保存
        templateRootDir = new File(templateRootPath);
    }

    public PABLO_View resolveViewName(String viewName, Locale locale) throws Exception{
        if(null == viewName || "".equals(viewName.trim())){return null;}
        //如以这个后缀,直接使用,如没有这个后缀,程序加上后缀
        viewName = viewName.endsWith(DEFAULT_TEMPLATE_SUFFIX) ? viewName : (viewName + DEFAULT_TEMPLATE_SUFFIX);
        File templateFile = new File((templateRootDir.getPath() + "/" + viewName).replaceAll("/+","/"));
        return new PABLO_View(templateFile);
    }
}
package com.gator.spring.framework.webmvc.servlet;

import com.gator.spring.framework.annotation.PABLO_Controller;
import com.gator.spring.framework.annotation.PABLO_RequestMapping;
import com.gator.spring.framework.context.PABLO_ApplicationContext;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Author PABLO
 * @Date 2022/5/8 13:29
 * @Desc
 */
@Slf4j
public class PABO_DispatcherServlet extends HttpServlet {

    private final String CONTEXT_CONFIG_LOCATION = "contextConfigLocation";

    //聚合容器
    private PABLO_ApplicationContext context;


    //缓存定位每个Method请求路径
    private List<PABLO_HandlerMapping> handlerMappings = new ArrayList<>();

    //缓存每个method对应的adapter
    private Map<PABLO_HandlerMapping, PABLO_HandlerAdapter> handlerAdapters = new HashMap<PABLO_HandlerMapping, PABLO_HandlerAdapter>();
    //缓存视图解析器,缓存页面
    private List<PABLO_ViewResolver> viewResolvers = new ArrayList<PABLO_ViewResolver>();

    @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 e) {
            try {
                processDispatchResult(req, resp, new PABLO_ModelAndView("500"));
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            //resp.getWriter().write("500 Exception,Details:\r\n" + Arrays.toString(e.getStackTrace()).replaceAll("\\[|\\]", "").replaceAll(",\\s", "\r\n"));
            e.printStackTrace();
        }
    }

    //最先执行
    @Override
    public void init(ServletConfig config) throws ServletException {
        //初始化ApplicationContext容器
        //com.gator.spring.framework.context.PABLO_ApplicationContext.refresh
        context = new PABLO_ApplicationContext(config.getInitParameter(CONTEXT_CONFIG_LOCATION));
        //初始化Spring-MVC  九大组件
        initStrategies(context);
    }

    private void initStrategies(PABLO_ApplicationContext context) {
        //多文件上传的组件
        initMultipartResolver(context);
        //初始化本地语言环境
        initLocaleResolver(context);
        //初始化模板处理器
        initThemeResolver(context);
        //初始化handlerMapping  √
        initHandlerMappings(context);
        //初始化参数适配器  √
        initHandlerAdapters(context);
        //初始化异常拦截器
        initHandlerExceptionResolvers(context);
        //初始化视图预处理器
        initRequestToViewNameTranslator(context);
        //初始化视图解析器  √
        initViewResolvers(context);
        //参数缓存器
        initFlashMapManager(context);
    }

    /**
     * @Description: 每个handler都有一个adapter路由,通过req获取对应handlerMapping即Method的URL
     * 通过adapter将req正确的路由到某对应method上
     * @Author: PABLO
     * @Date: 2022/5/8 14:28
     * @Params: [context]
     * @Return: void
     **/
    private void initHandlerAdapters(PABLO_ApplicationContext context) {
        //将request请求转为一个具体handler的对应,并匹配参数
        //需先通过request携带的URiUI 定位到具体handler(Method)
        //每个请求都对应一个handler,每个请求都对应一个handlerAdapter,负责handler路由
        for (PABLO_HandlerMapping handlerMapping : this.handlerMappings) {
            this.handlerAdapters.put(handlerMapping, new PABLO_HandlerAdapter());
        }
    }

    //每个view元素对应一个视图如HTML
    private void initViewResolvers(PABLO_ApplicationContext context) {

        //拿到模板的存放目录,存放404...等页面
        //可设置多个目录,这里只设置了一个
        String templateRoot = context.getConfig().getProperty("templateRoot");
        String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();

        //根据目录路径创建文件,集合的一个元素(view)对应一个模板(一个view类型)即可(即每个文件目录),例如html、freemarker...
        File templateRootDir = new File(templateRootPath);
        String[] templates = templateRootDir.list();
        for (int i = 0; i < templates.length; i++) {
            //这里主要是为了兼容多模板,所有模仿Spring用List保存
            this.viewResolvers.add(new PABLO_ViewResolver(templateRoot));
        }

    }

    private void initRequestToViewNameTranslator(PABLO_ApplicationContext context) {
    }

    private void initHandlerExceptionResolvers(PABLO_ApplicationContext context) {
    }

    private void initFlashMapManager(PABLO_ApplicationContext context) {
    }

    /**
     * @Description: 初始化扫描所有Controller和其中method,将每个方法的请求路径保存起来
     * 供后续请求url匹配
     * @Author: PABLO
     * @Date: 2022/5/8 14:22
     * @Params: [context]
     * @Return: void
     **/
    private void initHandlerMappings(PABLO_ApplicationContext context) {
        String[] beanNames = context.getBeanDefinitionNames();
        try {
            for (String beanName : beanNames) {
                Object controller = context.getBean(beanName);
                Class<?> clazz = controller.getClass();
                if (!clazz.isAnnotationPresent(PABLO_Controller.class)) continue;
                String baseUrl = "";
                //获取Controller路径  如/web
                if (clazz.isAnnotationPresent(PABLO_RequestMapping.class)) {
                    PABLO_RequestMapping requestMapping = clazz.getAnnotation(PABLO_RequestMapping.class);
                    baseUrl = requestMapping.value();
                }
                //获取Method的url配置
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    //没有加RequestMapping注解的直接忽略
                    if (!method.isAnnotationPresent(PABLO_RequestMapping.class)) continue;
                    //映射URL
                    PABLO_RequestMapping requestMapping = method.getAnnotation(PABLO_RequestMapping.class);
                    String regex = ("/" + baseUrl + "/" + requestMapping.value().replaceAll("\\*", ".*")).replaceAll("/+", "/");
                    Pattern pattern = Pattern.compile(regex);
                    //添加映射关系到handlerMapping
                    //注意:这里会添加两次,因为ApplicationContext中的definition中通过消协首字母类名和类.class都可以获取对象
                    //故一个对象其实有两个definition,如设置只能通过class获取对象或加判断逻辑等,这里将不会有重复pattern【自己把握】
                    this.handlerMappings.add(new PABLO_HandlerMapping(pattern, controller, method));
                }
            }

            //去重复
            handlerMappings = removeDuplicate(handlerMappings);
            System.out.println("------------Mapper info------------");
            handlerMappings.forEach(PABLO_HandlerMapping->
                    System.out.print("Controller->"+PABLO_HandlerMapping.getController().getClass().getSimpleName()+"    url->"+PABLO_HandlerMapping.getPattern().pattern()+ " \n")
            );


        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    private List<PABLO_HandlerMapping> removeDuplicate(List<PABLO_HandlerMapping> handlerMappings) {
        Set<PABLO_HandlerMapping> distance = new TreeSet<>(new Comparator<PABLO_HandlerMapping>() {
            @Override
            public int compare(PABLO_HandlerMapping o1, PABLO_HandlerMapping o2) {
                return o1.getPattern().pattern().compareTo(o2.getPattern().pattern());
            }
        });

        distance.addAll(handlerMappings);

        return new ArrayList<>(distance);
    }

    private void initThemeResolver(PABLO_ApplicationContext context) {
    }

    private void initLocaleResolver(PABLO_ApplicationContext context) {
    }

    private void initMultipartResolver(PABLO_ApplicationContext context) {
    }

    /**
     * @Description: 核心方法
     * @Author: PABLO
     * @Date: 2022/5/8 19:25
     * @Params: [req, resp]
     * @Return: void
     **/
    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {

        //根据请求获得URL 返回对应handler
        PABLO_HandlerMapping handler = getHandler(req);
        //将mv转为response可输出的http支持的结果
        if (Objects.isNull(handler)) {
            processDispatchResult(req, resp, new PABLO_ModelAndView("404"));
            return;
        }
        //根据请求对应adapter,adapter和method是一一对应的,缓存在map中
        //为什么需要adapter,因为形参和实参需要转换,request请求可以通过handlerMapping定位method,但是
        //method的参数列表也需要和request请求的列表相对应,才能构建出invoke需要的参数列表
        PABLO_HandlerAdapter ha = getHandlerAdapter(handler);
        //返回mv对象
        PABLO_ModelAndView mv = ha.handle(req, resp, handler);
        //将mv转为response可输出的http支持的结果
        processDispatchResult(req, resp, mv);
    }

    private PABLO_HandlerAdapter getHandlerAdapter(PABLO_HandlerMapping handler) {
        if (this.handlerAdapters.isEmpty()) {
            return null;
        }
        PABLO_HandlerAdapter ha = this.handlerAdapters.get(handler);
        if (ha.supports(handler)) return ha;
        return null;
    }

    /**
     * @Description: 将mv转为response可输出的http支持的结果
     * @Author: PABLO
     * @Date: 2022/5/8 13:45
     * @Params: [req, resp, pablo_modelAndView]
     * @Return: void
     **/
    private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, PABLO_ModelAndView mv) throws Exception {
        if (Objects.isNull(mv)) return;
        //判断视图解析器,视图解析器中的所有页面文件在init时候就被加载进来了
        if (this.viewResolvers.isEmpty()) return;
        for (PABLO_ViewResolver viewResolver : this.viewResolvers) {
            PABLO_View view = viewResolver.resolveViewName(mv.getViewName(), null);
            //渲染
            view.render(mv.getModel(), req, resp);
            return;
        }
    }

    private PABLO_HandlerMapping getHandler(HttpServletRequest req) {

        if (this.handlerMappings.isEmpty()) return null;
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath, "").replaceAll("/+", "/");

        for (PABLO_HandlerMapping handler : this.handlerMappings) {
            try {
                Matcher matcher = handler.getPattern().matcher(url);
                //如果没有匹配上继续下一个匹配
                if (!matcher.matches()) {
                    continue;
                }
                return handler;
            } catch (Exception e) {
                throw e;
            }
        }
        return null;
    }


}


  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-05-11 16:17:11  更:2022-05-11 16:18:47 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 23:21:33-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码