写在前面:
宝子们请注意:手写版并不是和spring源码的所有步骤都完全相同的,也不是源码解读。是我学习源码后对流程的梳理和思想总结。
类图(手写版类图是原版迷你版)
手写版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);
//DI注入 填充
populateBean(beanName, new PABLO_BeanDefinition(), beanWrapper);
//--------------------------------------------------------------------------
//增强器
PABLO_BeanPostProcessor postProcessor = new PABLO_BeanPostProcessor();
//前置增强器
postProcessor.postProcessBeforeInitialization(instance, beanName);
//可设置init-Method
//(1). 初始化前 postProcessBeforeInitialization()
//(2). 执行构造方法之后 执行 @PostConstruct 的方法
//(3). 所有属性赋初始化值之后 afterPropertiesSet() 该对象实现InitializingBean接口
//(4). 初始化时 配置文件中指定的 init-method 方法
//(5). 初始化后 postProcessAfterInitialization()
//后置增强器
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;
}
}