相关内容:
架构师系列内容:架构师学习笔记(持续更新)
一步一步手绘Spring IOC运行时序图一(Spring 核心容器 IOC初始化过程)
一步一步手绘Spring IOC运行时序图二(基于XML的IOC容器初始化)
一步一步手绘Spring IOC运行时序图三(基于Annotation的IOC容器初始化)
一步一步手绘Spring DI运行时序图(Spring 自动装配之依赖注入)
一步一步手绘Spring AOP运行时序图(Spring AOP 源码分析)
一步一步手绘Spring MVC运行时序图(Spring MVC原理)
Spring MVC 使用优化建议
模仿SpringMVC的DispatcherServlet 手撸300行代码提炼精华设计思想并保证功能可用(1.0版本)
必读,代码只是模仿spring的一个大致设计思路,很多细节问题并没有考虑到,代码本身还存在很多的问题,只能供参考一下。
从 Servlet 到 ApplicationContext
我们已经了解 SpringMVC 的入口是 DispatcherSerlvet,我们实现了 DispatcherServlet 的 init()方法。在 init()方法中完成了 IOC 容器的初始化。而在我们使用Spring 的经验中,我们见得最多的是 ApplicationContext,似乎 Spring 托管的所有实例 Bean 都可以通过调用 getBean()方法来获得。那么 ApplicationContext 又是从何而来的呢?从 Spring 源码中我们可以看到,DispatcherServlet 的类图如下:
DispatcherServlet 继 承 了 FrameworkServlet , FrameworkServlet 继 承 了 HttpServletBean ,HttpServletBean 继承了 HttpServlet。在 HttpServletBean 的 init()方法中调用了 FrameworkServlet的 initServletBean()方法,在 initServletBean()方法中初始化 WebApplicationContext 实例。在initServletBean()方法中调用了 DispatcherServlet 重写的 onRefresh()方法。在 DispatcherServlet的 onRefresh()方法中又调用了 initStrategies()方法,初始化 SpringMVC 的九大组件。其实,上面复杂的调用关系,我们可以简单的得出一个结论:就是在 Servlet 的 init 方法中初始化了 IOC容器和 SpringMVC 所依赖的九大组件。
高仿真的类关系图
项目环境搭建
application.properties 配置
还是先从 application.properties 文件开始 , 用 application.properties 来代替application.xml,具体配置如下
#IOC部分 扫描包路径
scanPackage=com.jarvisy.mvc2.demo
#MVC部分
#页面模板路径
templateRoot=layouts
#AOP部分
#切面表达式#
pointCut=public .* com.jarvisy.mvc2.demo.service..*Service..*(.*)
#切面类#
aspectClass=com.jarvisy.mvc2.demo.aspect.LogAspect
#切面前置通知#
aspectBefore=before
#切面后置通知#
aspectAfter=after
#切面异常通知#
aspectAfterThrow=afterThrowing
#切面异常类型#
aspectAfterThrowingName=java.lang.Exception
pom.xml 配置
引入所需的依赖就可以了 此代码依赖了lombok 插件,请注意。
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.0-b01</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.8.0-alpha0</version>
<scope>test</scope>
</dependency>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>com.jarvisy.mvc2.framework.webmvc.servlet.MyDispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
IOC 和DI部分
annotation(自定义配置)模块
注解完整代码
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAutowired {
String value() default "";
}
//----------------------------分割线,不同类---------------------------------------
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyController {
String value() default "";
}
//----------------------------分割线,不同类---------------------------------------
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestMapping {
String value() default "";
}
//----------------------------分割线,不同类---------------------------------------
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestParam {
String value() default "";
}
//----------------------------分割线,不同类---------------------------------------
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyService {
String value() default "";
}
core(顶层接口)模块
MyBeanFactory,MyFactoryBean完整代码
/**
* @author :Jarvisy
* @date :Created in 2020/10/20 22:13
* @description :单例工厂得顶层设计
*/
public interface MyBeanFactory {
/**
* 根据beanName从IOC容器中获得一个实例Bean
*
* @param beanName
* @return
*/
Object getBean(String beanName) throws Exception;
Object getBean(Class<?> beanClass) throws Exception;
}
/**
* @author :Jarvisy
* @date :Created in 2020/10/20 22:13
* @description : 这里没有用到
*/
public interface MyFactoryBean {
}
beans(配置封装)模块
MyBeanDefinition完整代码
//用来存储配置文件中的信息
//相当于保存在内存中的配置
public class MyBeanDefinition {
private String beanClassName;
private boolean lazyInit = false;
private String factoryBeanName;
public String getBeanClassName() {
return beanClassName;
}
public void setBeanClassName(String beanClassName) {
this.beanClassName = beanClassName;
}
public boolean isLazyInit() {
return lazyInit;
}
public void setLazyInit(boolean lazyInit) {
this.lazyInit = lazyInit;
}
public String getFactoryBeanName() {
return factoryBeanName;
}
public void setFactoryBeanName(String factoryBeanName) {
this.factoryBeanName = factoryBeanName;
}
}
MyBeanWrapper完整代码
/**
* @author :Jarvisy
* @date :Created in 2020/10/21 0:08
* @description :
*/
public class MyBeanWrapper {
private Object wrappedInstance;
private Class<?> wrappedClass;
public MyBeanWrapper(Object wrappedInstance) {
this.wrappedInstance = wrappedInstance;
}
public Object getWrappedInstance() {
return this.wrappedInstance;
}
// 返回代理以后的Class
// 可能会是这个 $Proxy0
public Class<?> getWrappedClass() {
return this.wrappedInstance.getClass();
}
}
context(IOC 容器)模块
MyAbstractApplicationContext完整代码
/**
* @author :Jarvisy
* @date :Created in 2020/10/20 22:19
* @description :IOC容器实现得顶层设计
*/
public abstract class MyAbstractApplicationContext {
/**
* 受保护得,只提供给子类进行重写
*/
public void refresh() throws Exception{
}
}
MyDefaultListableBeanFactory完整代码
/**
* @author :Jarvisy
* @date :Created in 2020/10/20 22:27
* @description :
*/
public class MyDefaultListableBeanFactory extends MyAbstractApplicationContext {
//存储注册信息的BeanDefinition
protected final Map<String, MyBeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, MyBeanDefinition>();
}
MyApplicationContext完整代码
/**
* Created on Jarvisy by 2020/10/24
*/
public class MyApplicationContext extends MyDefaultListableBeanFactory implements MyBeanFactory {
private String[] configLocations;
private MyBeanDefinitionReader reader;
//单例的IOC容器缓存
private Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>();
//通用的IOC容器
private Map<String, MyBeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<>();
public MyApplicationContext(String... configLocations) {
this.configLocations = configLocations;
try {
refresh();
} catch (Exception e) {
e.printStackTrace();
}
}
public Object getBean(Class<?> beanClass) throws Exception {
return getBean(beanClass.getName());
}
//依赖注入,从这里开始,通过读取BeanDefinition中的信息
//然后,通过反射机制创建一个实例并返回
//Spring做法是,不会把最原始的对象放出去,会用一个BeanWrapper来进行一次包装
//装饰器模式:
//1、保留原来的OOP关系
//2、我需要对它进行扩展,增强(为了以后AOP打基础)
@Override
public Object getBean(String beanName) throws Exception {
//1、初始化 初始化和注入不一起是为了解决循环依赖的问题
MyBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
Object instance = null;
//bean初始化前的通知和初始化后的通知
MyBeanPostProcessor postProcessor = new MyBeanPostProcessor();
postProcessor.postProcessBeforeInitialization(instance, beanName);
instance = instantiateBean(beanName, beanDefinition);
// 3、把这个对象封装到BeanWrapper
MyBeanWrapper beanWrapper = new MyBeanWrapper(instance);
// 4、拿到BeanWrapper之后,把BeanWrapper保存到IOC容器中去
this.factoryBeanInstanceCache.put(beanName, beanWrapper);
postProcessor.postProcessAfterInitialization(instance, beanName);
// 5、注入
populateBean(beanName, new MyBeanDefinition(), beanWrapper);
return this.factoryBeanInstanceCache.get(beanName).getWrappedInstance();
}
private void populateBean(String beanName, MyBeanDefinition beanDefinition, MyBeanWrapper beanWrapper) {
Object instance = beanWrapper.getWrappedInstance();
Class<?> clazz = beanWrapper.getWrappedClass();
//判断只有加了注解的类,才执行依赖注入
if (!(clazz.isAnnotationPresent(MyController.class) || clazz.isAnnotationPresent(MyService.class))) return;
//获得所有的fields
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (!field.isAnnotationPresent(MyAutowired.class)) continue;
MyAutowired autowired = field.getAnnotation(MyAutowired.class);
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();
}
}
}
private Object instantiateBean(String beanName, MyBeanDefinition beanDefinition) {
// 1、拿到要实例化的对象的类名
String className = beanDefinition.getBeanClassName();
// 2、反射实例化,得到一个对象
Object instance = null;
try {
//假设默认就是单例,细节暂且不考虑,先把主线拉通
if (this.factoryBeanObjectCache.containsKey(className))
instance = this.factoryBeanObjectCache.get(className);
else {
Class<?> clazz = Class.forName(className);
instance = clazz.newInstance();
// AOP部分 start
MyAdvisedSupport config = instantiateAopConfig(beanDefinition);
config.setTargetClass(clazz);
config.setTarget(instance);
//符合PointCut的规则的话,闯将代理对象
if (config.pointCutMatch()) instance = createProxy(config).getProxy();
// AOP部分 end
this.factoryBeanObjectCache.put(className, instance);
this.factoryBeanObjectCache.put(beanDefinition.getFactoryBeanName(), instance);
}
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}
@Override
public void refresh() throws Exception {
//1、定位配置文件
reader = new MyBeanDefinitionReader(this.configLocations);
//2、加载配置文件,扫描相关得类,把它们封装成BeanDefinition
List<MyBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
//3、注册,把配置信息放到容器里面(伪IOC容器)
doRegisterBeanDefinition(beanDefinitions);
//4、把不是延时加载得类,提前初始化
doAutowired();
}
//只处理非延时加载的情况
private void doAutowired() {
for (Map.Entry<String, MyBeanDefinition> beanDefinitionEntry : super.beanDefinitionMap.entrySet()) {
String beanName = beanDefinitionEntry.getKey();
if (!beanDefinitionEntry.getValue().isLazyInit()) {
try {
getBean(beanName);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private void doRegisterBeanDefinition(List<MyBeanDefinition> beanDefinitions) throws Exception {
for (MyBeanDefinition beanDefinition : beanDefinitions) {
if (super.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())) {
throw new Exception("The “" + beanDefinition.getFactoryBeanName() + "” is exists!!");
}
super.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
}
//到这里为止,容器初始化完毕
}
public String[] getBeanDefinitionNames() {
return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
}
public int getBeanDefinitionCount() {
return this.beanDefinitionMap.size();
}
public Properties getConfig() {
return this.reader.getConfig();
}
private MyAdvisedSupport instantiateAopConfig(MyBeanDefinition gpBeanDefinition) {
MyAopConfig config = new MyAopConfig();
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 MyAdvisedSupport(config);
}
private MyAopProxy createProxy(MyAdvisedSupport config) {
Class targetClass = config.getTargetClass();
if (targetClass.getInterfaces().length > 0) return new MyJdkDynamicAopProxy(config);
return new MyCglibAopProxy(config);
}
}
MyBeanDefinitionReader完整代码
/**
* @author :Jarvisy
* @date :Created in 2020/10/20 23:22
* @description :
*/
public class MyBeanDefinitionReader {
private List<String> registerBeanClasses = new ArrayList<String>();
private Properties config = new Properties();
//固定配置文件中的key,相对于xml的规范
private final String SCAN_PACKAGE = "scanPackage";
public MyBeanDefinitionReader(String... locations) {
//通过URL定位找到其所对应的文件,然后转换为文件流
try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(locations[0].replace("classpath:", ""))) {
config.load(is);
} catch (IOException e) {
e.printStackTrace();
}
doScanner(config.getProperty(SCAN_PACKAGE));
}
private void doScanner(String scanPackage) {
//转换为文件路径,实际上就是把.替换为/就OK了
URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
File classPath = new File(url.getFile());
for (File file : classPath.listFiles()) {
if (file.isDirectory()) doScanner(scanPackage + "." + file.getName());
else {
if (!file.getName().endsWith(".class")) continue;
String className = (scanPackage + "." + file.getName().replace(".class", ""));
registerBeanClasses.add(className);
}
}
}
public Properties getConfig() {
return this.config;
}
public List<MyBeanDefinition> loadBeanDefinitions() {
List<MyBeanDefinition> result = new ArrayList<MyBeanDefinition>();
try {
for (String className : registerBeanClasses) {
Class<?> beanClass = Class.forName(className);
//如果是一个接口,是不能实例化的
//用它实现类来实例化
if (beanClass.isInterface()) continue;
//beanName有三种情况:
//1、默认是类名首字母小写
//2、自定义名字
//3、接口注入
result.add(doCreateBeanDefinition(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;
}
//把配置文件中扫描到得所有配置信息解析成一个BeanDefinition对象,以便于之后IOC操作
private MyBeanDefinition doCreateBeanDefinition(String factoryBeanName, String beanClassName) {
MyBeanDefinition beanDefinition = new MyBeanDefinition();
beanDefinition.setBeanClassName(beanClassName);
beanDefinition.setFactoryBeanName(factoryBeanName);
return beanDefinition;
}
//转换首字母小写
private String toLowerFirstCase(String beanName) {
return beanName.replaceFirst("^.", String.valueOf(beanName.charAt(0)).toLowerCase());
}
}
MyApplicationContextAware完整代码
/**
* @author :Jarvisy
* @date :Created in 2020/10/20 22:48
* @description :
* 通过解耦方式获得IOC容器的顶层设计
* 后面将通过一个监听器去扫描所有的类,只要实现了此接口,
* 将自动调用setApplicationContext()方法,从而将IOC容器注入到目标类中
*/
public interface MyApplicationContextAware {
void setApplicationContext(MyApplicationContext applicationContext);
}
完成 DI 依赖注入功能
依赖注入的入口是从 getBean()方法开始的,前面的 IOC 手写部分基本流程已通。先在 GPApplicationContext 中定义好 IOC 容器,一个是 GPBeanWrapper,一个是单例对象缓存
完整代码从上面找
public class MyApplicationContext extends MyDefaultListableBeanFactory implements MyBeanFactory {
private String[] configLocations;
private MyBeanDefinitionReader reader;
//单例的IOC容器缓存
private Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>();
//通用的IOC容器
private Map<String, MyBeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<>();
}
从getBean()开始
完善 getBean()方法 完整代码从上面找
//依赖注入,从这里开始,通过读取BeanDefinition中的信息
//然后,通过反射机制创建一个实例并返回
//Spring做法是,不会把最原始的对象放出去,会用一个BeanWrapper来进行一次包装
//装饰器模式:
//1、保留原来的OOP关系
//2、我需要对它进行扩展,增强(为了以后AOP打基础)
@Override
public Object getBean(String beanName) throws Exception {
//1、初始化 初始化和注入不一起是为了解决循环依赖的问题
MyBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
Object instance = null;
//bean初始化前的通知和初始化后的通知
MyBeanPostProcessor postProcessor = new MyBeanPostProcessor();
postProcessor.postProcessBeforeInitialization(instance, beanName);
instance = instantiateBean(beanName, beanDefinition);
// 3、把这个对象封装到BeanWrapper
MyBeanWrapper beanWrapper = new MyBeanWrapper(instance);
// 4、拿到BeanWrapper之后,把BeanWrapper保存到IOC容器中去
this.factoryBeanInstanceCache.put(beanName, beanWrapper);
postProcessor.postProcessAfterInitialization(instance, beanName);
// 5、注入
populateBean(beanName, new MyBeanDefinition(), beanWrapper);
return this.factoryBeanInstanceCache.get(beanName).getWrappedInstance();
}
MyBeanPostProcessor完整代码
/**
* Created on Jarvisy by 2020/10/26
*/
public class MyBeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception {
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
return bean;
}
}
DI 部分完成
MVC 部分
MyDispatcherServlet完整代码
package com.jarvisy.mvc2.framework.webmvc.servlet;
import com.jarvisy.mvc2.framework.annotation.MyController;
import com.jarvisy.mvc2.framework.annotation.MyRequestMapping;
import com.jarvisy.mvc2.framework.context.MyApplicationContext;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerExceptionResolver;
/**
* Created on Jarvisy by 2020/10/28
*/
@Slf4j
public class MyDispatcherServlet extends HttpServlet {
private final String CONTEXT_CONFIG_LOCATION = "contextConfigLocation";
private MyApplicationContext context;
private List<MyHandlerMapping> handlerMappings = new ArrayList<>();
//
private Map<MyHandlerMapping, MyHandlerAdapter> handlerAdapters = new HashMap<>();
//
private List<MyViewResolver> viewResolvers = new ArrayList<MyViewResolver>();
@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.doDispatcher(req, resp);
} catch (Exception e) {
resp.getWriter().write("500 Exception,Details:\r\n" + Arrays.toString(e.getStackTrace()).replaceAll("\\[|\\]", "").replaceAll(",\\s", "\r\n"));
e.printStackTrace();
}
}
private void doDispatcher(HttpServletRequest request, HttpServletResponse response) throws Exception {
//1、通过从request中拿到URL,去匹配一个HandlerMapping
MyHandlerMapping handler = getHandler(request);
if (null == handler) {
processDispatchResult(request, response, new MyModelAndView("404"));
return;
}
//2、准备调用前的参数
MyHandlerAdapter ha = getHandleAdapter(handler);
//3、真正的调用方法,返回ModelAndView存储了要穿页面上值,和页面模板的名称
MyModelAndView mv = ha.handle(request, response, handler);
//这一步才是真正的输出
processDispatchResult(request, response, mv);
}
private MyHandlerAdapter getHandleAdapter(MyHandlerMapping handler) {
if (this.handlerAdapters.isEmpty()) return null;
MyHandlerAdapter ha = this.handlerAdapters.get(handler);
if (ha.supports(handler)) return ha;
return null;
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, MyModelAndView mv) throws Exception {
//把给我的ModelAndView变成一个HTML、OutputStream、json、freemarker、velocity
//ContextType
if (null == mv || this.viewResolvers.isEmpty()) return;
//
// //如果ModelAndView不为null,怎么办?
for (MyViewResolver viewResolver : this.viewResolvers) {
MyView view = viewResolver.resolveViewName(mv.getViewName(), null);
if (view != null) {
view.render(mv.getModel(), request, response);
return;
}
}
// do something
}
private MyHandlerMapping getHandler(HttpServletRequest req) throws Exception {
if (this.handlerMappings.isEmpty()) return null;
String url = req.getRequestURI();
String contextPath = req.getContextPath();
url = url.replace(contextPath, "").replaceAll("/+", "/");
for (MyHandlerMapping handler : this.handlerMappings) {
Matcher matcher = handler.getPattern().matcher(url);
//如果没有匹配上继续下一个匹配
if (!matcher.matches()) continue;
return handler;
}
return null;
}
@Override
public void init(ServletConfig config) throws ServletException {
//1、初始化ApplicationContext
context = new MyApplicationContext(config.getInitParameter(CONTEXT_CONFIG_LOCATION));
//2、初始化Spring MVC 九大组件
initStrategies(context);
}
//初始化策略
protected void initStrategies(MyApplicationContext context) {
//有九种策略
// 针对于每个用户请求, 都会经过一些处理的策略之后, 最终才能有结果输出
// 每种策略可以自定义干预, 但是最终的结果都是一致
// ============= 这里说的就是传说中的九大组件 ================
initMultipartResolver(context);//文件上传解析, 如果请求类型是 multipart 将通过MultipartResolver 进行文件上传解析
initLocaleResolver(context);//本地化解析
initThemeResolver(context);//主题解析
/** 我们自己会实现 */
//GPHandlerMapping 用来保存 Controller 中配置的 RequestMapping 和 Method 的一个对应关系
initHandlerMappings(context);//通过 HandlerMapping, 将请求映射到处理器
/** 我们自己会实现 */
//HandlerAdapters 用来动态匹配 Method 参数, 包括类转换, 动态赋值
initHandlerAdapters(context);//通过 HandlerAdapter 进行多类型的参数动态匹配
initHandlerExceptionResolvers(context);//如果执行过程中遇到异常, 将交给HandlerExceptionResolver 来解析
initRequestToViewNameTranslator(context);//直接解析请求到视图名
/** 我们自己会实现 */
//通过 ViewResolvers 实现动态模板的解析
//自己解析一套模板语言
initViewResolvers(context);//通过 viewResolver 解析逻辑视图到具体视图实现
initFlashMapManager(context);//flash 映射管理器
}
private void initFlashMapManager(MyApplicationContext context) { }
private void initViewResolvers(MyApplicationContext context) {
//解决页面名字和模板文件关联的问题
String templateRoot = context.getConfig().getProperty("templateRoot");
String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();
File templateRootDir = new File(templateRootPath);
String[] templates = templateRootDir.list();
for (int i = 0; i < templates.length; i++) {
//这里主要是为了兼容多模板,所有模仿Spring用List保存
//在代码中简化了,其实只有需要一个模板就可以搞定,这里只是为了仿真,所有还是搞了个List
this.viewResolvers.add(new MyViewResolver(templateRoot));
}
}
private void initRequestToViewNameTranslator(MyApplicationContext context) {
}
private void initHandlerExceptionResolvers(MyApplicationContext context) {
}
private void initHandlerAdapters(MyApplicationContext context) {
//把一个request请求变成一个handler,参数都是字符串的,自动配到handler中的形参
//可想而知,他要拿到HandlerMapping才能干活
//就意味着,有几个HandlerMapping就有几个HandlerAdapter
//在初始化阶段, 我们能做的就是, 将这些参数的名字或者类型按一定的顺序保存下来
//因为后面用反射调用的时候, 传的形参是一个数组
//可以通过记录这些参数的位置 index,挨个从数组中填值, 这样的话, 就和参数的顺序无关了
for (MyHandlerMapping handlerMapping : this.handlerMappings) {
//每一个方法有一个参数列表, 那么这里保存的是形参列表
this.handlerAdapters.put(handlerMapping, new MyHandlerAdapter());
}
}
//将 Controller 中配置的 RequestMapping 和 Method 进行一一对应
private void initHandlerMappings(MyApplicationContext context) {
//按照我们通常的理解应该是一个 Map
//Map<String,Method> map;
//map.put(url,Method)
//首先从容器中取到所有的实例
String[] beanNames = context.getBeanDefinitionNames();
try {
for (String beanName : beanNames) {
//到了 MVC 层, 对外提供的方法只有一个 getBean 方法
Object controller = context.getBean(beanName);
Class<?> clazz = controller.getClass();
if (!clazz.isAnnotationPresent(MyController.class)) continue;
String baseUrl = "";
//获取Controller的url配置
if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
MyRequestMapping requestMapping = clazz.getAnnotation(MyRequestMapping.class);
baseUrl = requestMapping.value();
}
//扫描所有的 public 方法
//获取Method的url配置
Method[] methods = clazz.getMethods();
for (Method method : methods) {
//没有加RequestMapping注解的直接忽略
if (!method.isAnnotationPresent(MyRequestMapping.class)) continue;
//映射URL
MyRequestMapping requestMapping = method.getAnnotation(MyRequestMapping.class);
String regex = ("/" + baseUrl + "/" + requestMapping.value().replaceAll("\\*", ".*")).replaceAll("/+", "/");
Pattern pattern = Pattern.compile(regex);
this.handlerMappings.add(new MyHandlerMapping(pattern, controller, method));
log.info("Mapped " + regex + "," + method);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void initThemeResolver(MyApplicationContext context) {
}
private void initLocaleResolver(MyApplicationContext context) {
}
private void initMultipartResolver(MyApplicationContext context) {
}
}
MyHandlerMapping完整代码
/**
* Created on Jarvis.y by 2020/10/28
*/
public class MyHandlerMapping {
private Object controller; //保存方法对应的实例
private Method method; //保存映射的方法
private Pattern pattern; //URL的正则匹配
public MyHandlerMapping(Pattern pattern, Object controller, Method method) {
this.controller = controller;
this.method = method;
this.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;
}
}
MyHandlerAdapter完整代码
/**
* Created on Jarvisy by 2020/10/28
*/
public class MyHandlerAdapter {
public boolean supports(Object handler) {
return (handler instanceof MyHandlerMapping);
}
//进行数据类型转换
//Spring 做了顶层转换策略 public interface Converter<S, T> 实现了很多转换类型
private Object convert(Class<?> type, String[] value) {
//如果是int
if (Integer.class == type) return Integer.valueOf(value[0]);
else if (Integer[].class == type) return null; // do something
else if (String.class == type) return value[0];
else if (String[].class == type) return value;
//如果还有double或者其他类型,继续加if
//这时候,我们应该想到策略模式了,在这里暂时不实现
return value;
}
MyModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
MyHandlerMapping handlerMapping = (MyHandlerMapping) handler;
//把方法的形参列表和request的参数列表所在顺序进行一一对应
//每一个方法有一个参数列表, 那么这里保存的是形参列表
Map<String, Integer> paramIndexMapping = new HashMap<String, Integer>();
Method method = handlerMapping.getMethod();
//获取方法参数的真实名称
DefaultParameterNameDiscoverer discover = new DefaultParameterNameDiscoverer();
String[] parameterNames = discover.getParameterNames(method);
//提取方法中加了注解的参数
//根据用户请求的参数信息, 跟 method 中的参数信息进行动态匹配
//resp 传进来的目的只有一个: 只是为了将其赋值给方法参数, 仅此而已
//只有当用户传过来的 ModelAndView 为空的时候, 才会 new 一个默认的
//1、 要准备好这个方法的形参列表
//方法重载: 形参的决定因素: 参数的个数、 参数的类型、 参数顺序、 方法的名字
//只处理 Request 和 Response
Annotation[][] pa = method.getParameterAnnotations();
Class<?>[] paramTypes = method.getParameterTypes();
for (int i = 0; i < paramTypes.length; i++) {
Class<?> type = paramTypes[i];
if (type == HttpServletRequest.class || type == HttpServletResponse.class) {
paramIndexMapping.put(type.getName(), i);
continue;
}
String paramName = "";
for (Annotation a : pa[i]) {
if (a instanceof MyRequestParam) {
paramName = ((MyRequestParam) a).value();
break;
}
}
if ("".equals(paramName)) paramName = parameterNames[i];
paramIndexMapping.put(paramName, i);
}
//2、 拿到自定义命名参数所在的位置
//用户通过 URL 传过来的参数列表
Map<String, String[]> params = request.getParameterMap();
//3、 构造实参列表
Object[] paramValues = new Object[paramTypes.length];
for (Map.Entry<String, String[]> param : params.entrySet()) {
if (!paramIndexMapping.containsKey(param.getKey())) continue;
Integer index = paramIndexMapping.get(param.getKey());
//因为页面上传过来的值都是 String 类型的, 而在方法中定义的类型是千变万化的
//要针对我们传过来的参数进行类型转换
paramValues[index] = convert(paramTypes[index], param.getValue());
}
if (paramIndexMapping.containsKey(HttpServletRequest.class.getName()))
paramValues[paramIndexMapping.get(HttpServletRequest.class.getName())] = request;
if (paramIndexMapping.containsKey(HttpServletResponse.class.getName()))
paramValues[paramIndexMapping.get(HttpServletResponse.class.getName())] = response;
//4、 从 handler 中取出 controller、 method, 然后利用反射机制进行调用
Object result = method.invoke(handlerMapping.getController(), paramValues);
if (result == null || result instanceof Void) return null;
boolean isModelAndView = method.getReturnType() == MyModelAndView.class;
if (isModelAndView) return (MyModelAndView) result;
return null;
}
}
MyModelAndView完整代码
/**
* Created on Jarvis.y by 2020/10/28
*/
public class MyModelAndView {
private String viewName;
private Map<String, ?> model;
public MyModelAndView(String viewName) {
this.viewName = viewName;
}
public MyModelAndView(String viewName, Map<String, ?> model) {
this.viewName = viewName;
this.model = model;
}
public String getViewName() {
return viewName;
}
public Map<String, ?> getModel() {
return model;
}
}
MyViewResolver完整代码
/**
* Created on Jarvisy by 2020/10/28
* 设计这个类的主要目的是:
* 1、 讲一个静态文件变为一个动态文件
* 2、 根据用户传送参数不同, 产生不同的结果
* 最终输出字符串, 交给 Response 输出
*/
public class MyViewResolver {
private final String DEFAULT_TEMPLATE_SUFFIX = ".html";
private File templateRootDir;
public MyViewResolver(String templateRoot) {
String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();
templateRootDir = new File(templateRootPath);
}
public MyView 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 MyView(templateFile);
}
}
MyView完整代码
/**
* Created on Jarvisy by 2020/10/28
*/
public class MyView {
public final String DEFAULT_CONTENT_TYPE = "text/html;charset=utf-8";
private File viewFile;
public MyView(File viewFile) {
this.viewFile = viewFile;
}
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("¥\\{|\\}", "");
Object paramValue = model.get(paramName);
if (null == paramValue) continue;
//要把¥{}中间的这个字符串给取出来
line = matcher.replaceFirst(makeStringForRegExp(paramValue.toString()));
matcher = pattern.matcher(line);
}
sb.append(line);
}
response.setCharacterEncoding("utf-8");
// response.setContentType(DEFULAT_CONTENT_TYPE);
response.getWriter().write(sb.toString());
}
//处理特殊字符 可能存在有问题
// FIXME: 2020/10/31 这个正则可能存在问题
public static String makeStringForRegExp(String str) {
return str.replace("\\", "\\\\").replace("*", "\\*")
.replace("+", "\\+").replace("|", "\\|")
.replace("{", "\\{").replace("}", "\\}")
.replace("(", "\\(").replace(")", "\\)")
.replace("^", "\\^").replace("$", "\\$")
.replace("[", "\\[").replace("]", "\\]")
.replace("?", "\\?").replace(",", "\\,")
.replace(".", "\\.").replace("&", "\\&");
}
}
业务代码实现
MyAction完整代码
/**
* 公布接口url
*/
@MyController
@MyRequestMapping("/web")
public class MyAction {
@MyAutowired
IQueryService queryService;
@MyAutowired
IModifyService modifyService;
@MyRequestMapping("/query.json")
public MyModelAndView query(HttpServletRequest request, HttpServletResponse response,
@MyRequestParam("name") String name) {
String result = queryService.query(name);
return out(response, result);
}
@MyRequestMapping("/add*.json")
public MyModelAndView add(HttpServletRequest request, HttpServletResponse response,
@MyRequestParam("name") String name, @MyRequestParam("addr") String addr) {
String result = null;
try {
result = modifyService.add(name, addr);
return out(response, result);
} catch (Exception e) {
// e.printStackTrace();
Map<String, Object> model = new HashMap<String, Object>();
model.put("detail", e.getMessage());
// System.out.println(Arrays.toString(e.getStackTrace()).replaceAll("\\[|\\]",""));
model.put("stackTrace", Arrays.toString(e.getStackTrace()).replaceAll("\\[|\\]", ""));
return new MyModelAndView("500", model);
}
}
@MyRequestMapping("/remove.json")
public void remove(HttpServletRequest request, HttpServletResponse response,
@MyRequestParam("id") Integer id) {
String result = modifyService.remove(id);
out(response, result);
}
@MyRequestMapping("/edit.json")
public void edit(HttpServletRequest request, HttpServletResponse response,
@MyRequestParam("id") Integer id,
@MyRequestParam("name") String name) {
String result = modifyService.edit(id, name);
out(response, result);
}
private MyModelAndView out(HttpServletResponse resp, String str) {
try {
resp.getWriter().write(str);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
IModifyService,IQueryService完整代码
//增删改业务
public interface IModifyService {
//增加
public String add(String name, String addr) throws Exception;
//修改
public String edit(Integer id, String name);
//删除
public String remove(Integer id);
}
//查询业务
public interface IQueryService {
//查询
public String query(String name);
}
ModifyService,QueryService完整代码
@MyService
public class ModifyService implements IModifyService {
public String add(String name, String addr) throws Exception {
throw new Exception("这是故意抛的异常!!");
}
public String edit(Integer id, String name) {
return "modifyService edit,id=" + id + ",name=" + name;
}
public String remove(Integer id) {
return "modifyService id=" + id;
}
}
@MyService
@Slf4j
public class QueryService 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;
}
}
定制模板页面
在resources 目录下新建layouts文件夹加入以下文件
fisrst.html
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8">
<title>demo</title>
</head>
<center>
<h1>大家好,我是¥{teacher}<br/>欢迎大家一起来探索Spring的世界</h1>
<h3>Hello,My name is ¥{teacher}</h3>
<div>¥{data}</div>
Token值:¥{token}
</center>
</html>
500.html
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8">
<title>服务器好像累了</title>
</head>
<body>
<font size='25' color='blue'>500 服务器好像有点累了,需要休息一下</font><br/>
<b>Message:¥{detail}</b><br/>
<b>StackTrace:¥{stackTrace}</b><br/>
<br/><font color='green'><i>jarvisy@www.jarvisy.com</i></font>
</body>
</html>
400.html
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8">
<title>页面去火星了</title>
</head>
<body>
<font size='25' color='red'>404 Not Found</font><br/><font color='green'><i>jarvisy@www.jarvisy.com</i></font>
</body>
</html>
mvc部分完成 至此可以运行看效果了
AOP部分
在 application.properties 中增加如下自定义配置
#AOP部分
#切面表达式#
pointCut=public .* com.jarvisy.mvc2.demo.service..*Service..*(.*)
#切面类#
aspectClass=com.jarvisy.mvc2.demo.aspect.LogAspect
#切面前置通知#
aspectBefore=before
#切面后置通知#
aspectAfter=after
#切面异常通知#
aspectAfterThrow=afterThrowing
#切面异常类型#
aspectAfterThrowingName=java.lang.Exception
MyAopProxy完整代码
/**
* Created on Jarvisy by 2020/10/29
*/
public interface MyAopProxy {
Object getProxy();
Object getProxy(ClassLoader classLoader);
}
MyCglibAopProxy完整代码 未实现逻辑
public class MyCglibAopProxy implements MyAopProxy {
public MyCglibAopProxy(MyAdvisedSupport config) {
}
@Override
public Object getProxy() {
return null;
}
@Override
public Object getProxy(ClassLoader classLoader) {
return null;
}
}
MyJdkDynamicAopProxy完整代码
public class MyJdkDynamicAopProxy implements MyAopProxy, InvocationHandler {
private MyAdvisedSupport advised;
public MyJdkDynamicAopProxy(MyAdvisedSupport config) {
this.advised = config;
}
@Override
public Object getProxy() {
return getProxy(this.advised.getTargetClass().getClassLoader());
}
@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 {
List<Object> interceptorsAndDynamicMethodMatchers = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, this.advised.getTargetClass());
MyMethodInvocation invocation = new MyMethodInvocation(proxy, this.advised.getTarget(), method, args, this.advised.getTargetClass(), interceptorsAndDynamicMethodMatchers);
return invocation.proceed();
}
}
MyAdvisedSupport完整代码
/**
* Created on Jarvisy by 2020/10/29
*/
public class MyAdvisedSupport {
private Class<?> targetClass;
private Object target;
private MyAopConfig config;
private Pattern pointCutClassPattern;
private transient Map<Method, List<Object>> methodCache;
public MyAdvisedSupport(MyAopConfig config) {
this.config = config;
}
public Class<?> getTargetClass() {
return this.targetClass;
}
public void setTargetClass(Class<?> targetClass) {
this.targetClass = targetClass;
parse();
}
private void parse() {
String pointCut = config.getPointCut().replaceAll("\\.", "\\\\.")
.replaceAll("\\\\.\\*", ".*").replaceAll("\\(", "\\\\(").replaceAll("\\)", "\\\\)");
String pointCutForClassRegex = pointCut.substring(0, pointCut.lastIndexOf("\\(") - 4);
pointCutClassPattern = Pattern.compile("class " + pointCutForClassRegex.substring(
pointCutForClassRegex.lastIndexOf(" ") + 1));
try {
methodCache = new HashMap<Method, List<Object>>();
Pattern pattern = Pattern.compile(pointCut);
Class aspectClass = Class.forName(this.config.getAspectClass());
Map<String, Method> aspectMethods = new HashMap<String, Method>();
for (Method m : aspectClass.getMethods()) {
aspectMethods.put(m.getName(), m);
}
for (Method m : this.targetClass.getMethods()) {
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>();
//把每一个方法包装成 MethodInterceptor
//before
if (!(null == config.getAspectBefore() || "".equals(config.getAspectBefore()))) {
//创建一个Advice
advices.add(new MyMethodBeforeAdviceInterceptor(aspectMethods.get(config.getAspectBefore()), aspectClass.newInstance()));
}
//after
if (!(null == config.getAspectAfter() || "".equals(config.getAspectAfter()))) {
advices.add(new MyAfterReturningAdviceInterceptor(aspectMethods.get(config.getAspectAfter()), aspectClass.newInstance()));
}
//afterThrowing
if (!(null == config.getAspectAfterThrow() || "".equals(config.getAspectAfterThrow()))) {
MyAfterThrowingAdviceInterceptor throwingAdvice =
new MyAfterThrowingAdviceInterceptor(
aspectMethods.get(config.getAspectAfterThrow()),
aspectClass.newInstance());
throwingAdvice.setThrowName(config.getAspectAfterThrowingName());
advices.add(throwingAdvice);
}
methodCache.put(m, advices);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public Object getTarget() {
return this.target;
}
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) throws Exception {
List<Object> cached = methodCache.get(method);
if (cached == null) {
Method m = targetClass.getMethod(method.getName(), method.getParameterTypes());
cached = methodCache.get(m);
//底层逻辑,对代理方法进行一个兼容处理
this.methodCache.put(m, cached);
}
return cached;
}
public void setTarget(Object target) {
this.target = target;
}
public boolean pointCutMatch() {
return pointCutClassPattern.matcher(this.targetClass.toString()).matches();
}
}
MyMethodInvocation完整代码
/**
* Created on Jarvisy by 2020/10/29
*/
public class MyMethodInvocation implements MyJoinPoint {
private Object proxy;
private Method method;
private Object target;
private Object[] arguments;
private List<Object> interceptorsAndDynamicMethodMatchers;
private Class<?> targetClass;
private Map<String, Object> userAttributes;
//定义一个索引,从-1开始来记录当前拦截器执行的位置
private int currentInterceptorIndex = -1;
public MyMethodInvocation(Object proxy, Object target, Method method, Object[] arguments, Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {
this.proxy = proxy;
this.method = method;
this.target = target;
this.arguments = arguments;
this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
this.targetClass = targetClass;
}
public Object proceed() throws Throwable {
//如果Interceptor执行完了,则执行joinPoint
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return this.method.invoke(this.target, this.arguments);
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
//如果要动态匹配joinPoint
if (interceptorOrInterceptionAdvice instanceof MyMethodInterceptor) {
MyMethodInterceptor mi =
(MyMethodInterceptor) interceptorOrInterceptionAdvice;
return mi.invoke(this);
} else {
//动态匹配失败时,略过当前Interceptor,调用下一个Interceptor
return proceed();
}
}
@Override
public Object getThis() {
return this.target;
}
@Override
public Object[] getArguments() {
return this.arguments;
}
@Override
public Method getMethod() {
return this.method;
}
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);
}
}
}
public Object getUserAttribute(String key) {
return (this.userAttributes != null ? this.userAttributes.get(key) : null);
}
}
MyMethodInterceptor完整代码
public interface MyMethodInterceptor {
Object invoke(MyMethodInvocation invocation) throws Throwable;
}
MyAdvice完整代码
public interface MyAdvice {
}
MyJoinPoint完整代码
public interface MyJoinPoint {
Object getThis();
Object[] getArguments();
Method getMethod();
void setUserAttribute(String key, Object value);
Object getUserAttribute(String key);
}
MyAopConfig完整代码
@Data
public class MyAopConfig {
private String pointCut;
private String aspectBefore;
private String aspectAfter;
private String aspectClass;
private String aspectAfterThrow;
private String aspectAfterThrowingName;
}
MyAbstractAspectAdvice完整代码
/**
* Created on Jarvisy by 2020/10/30
*/
public abstract class MyAbstractAspectAdvice implements MyAdvice {
private Method aspectMethod;
private Object aspectTarget;
public MyAbstractAspectAdvice(Method aspectMethod, Object aspectTarget) {
this.aspectMethod = aspectMethod;
this.aspectTarget = aspectTarget;
}
public Object invokeAdviceMethod(MyJoinPoint 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] == MyJoinPoint.class) {
args[i] = joinPoint;
} else if (paramTypes[i] == Throwable.class) {
args[i] = tx;
} else if (paramTypes[i] == Object.class) {
args[i] = returnValue;
}
}
return this.aspectMethod.invoke(aspectTarget, args);
}
}
}
MyMethodBeforeAdviceInterceptor完整代码
//前置通知
public class MyMethodBeforeAdviceInterceptor extends MyAbstractAspectAdvice implements MyAdvice, MyMethodInterceptor {
private MyJoinPoint joinPoint;
public MyMethodBeforeAdviceInterceptor(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);
}
@Override
public Object invoke(MyMethodInvocation mi) throws Throwable {
//从被织入的代码中才能拿到,JoinPoint
this.joinPoint = mi;
before(mi.getMethod(), mi.getArguments(), mi.getThis());
return mi.proceed();
}
}
MyAfterReturningAdviceInterceptor完整代码
// 后置通知
public class MyAfterReturningAdviceInterceptor extends MyAbstractAspectAdvice implements MyAdvice, MyMethodInterceptor {
private MyJoinPoint joinPoint;
public MyAfterReturningAdviceInterceptor(Method aspectMethod, Object aspectTarget) {
super(aspectMethod, aspectTarget);
}
@Override
public Object invoke(MyMethodInvocation mi) throws Throwable {
Object retVal = mi.proceed();
this.joinPoint = mi;
this.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;
}
private void afterReturning(Object retVal, Method method, Object[] arguments, Object aThis) throws Throwable {
super.invokeAdviceMethod(this.joinPoint, retVal, null);
}
}
MyAfterThrowingAdviceInterceptor完整代码
//异常通知
public class MyAfterThrowingAdviceInterceptor extends MyAbstractAspectAdvice implements MyAdvice, MyMethodInterceptor {
private String throwingName;
public MyAfterThrowingAdviceInterceptor(Method aspectMethod, Object aspectTarget) {
super(aspectMethod, aspectTarget);
}
@Override
public Object invoke(MyMethodInvocation mi) throws Throwable {
try {
return mi.proceed();
} catch (Throwable e) {
invokeAdviceMethod(mi, null, e.getCause());
throw e;
}
}
public void setThrowName(String throwName) {
this.throwingName = throwName;
}
}
接入 getBean()方法, 找到 GPApplicationContext 的 getBean()方法,我们知道 getBean()中负责 Bean 初始化的方法其实就是 instantiateBean(),我们在初始化时就可以确定是否返回原生 Bean 还是 Proxy Bean。代码实现
private Object instantiateBean(String beanName, MyBeanDefinition beanDefinition) {
// 1、拿到要实例化的对象的类名
String className = beanDefinition.getBeanClassName();
// 2、反射实例化,得到一个对象
Object instance = null;
try {
//假设默认就是单例,细节暂且不考虑,先把主线拉通
if (this.factoryBeanObjectCache.containsKey(className))
instance = this.factoryBeanObjectCache.get(className);
else {
Class<?> clazz = Class.forName(className);
instance = clazz.newInstance();
// AOP部分 start
MyAdvisedSupport config = instantiateAopConfig(beanDefinition);
config.setTargetClass(clazz);
config.setTarget(instance);
//符合PointCut的规则的话,闯将代理对象
if (config.pointCutMatch()) instance = createProxy(config).getProxy();
// AOP部分 end
this.factoryBeanObjectCache.put(className, instance);
this.factoryBeanObjectCache.put(beanDefinition.getFactoryBeanName(), instance);
}
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}
private MyAdvisedSupport instantiateAopConfig(MyBeanDefinition gpBeanDefinition) {
MyAopConfig config = new MyAopConfig();
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 MyAdvisedSupport(config);
}
private MyAopProxy createProxy(MyAdvisedSupport config) {
Class targetClass = config.getTargetClass();
if (targetClass.getInterfaces().length > 0) return new MyJdkDynamicAopProxy(config);
return new MyCglibAopProxy(config);
}
业务代码
LogAspect完整代码
@Slf4j
public class LogAspect {
//在调用一个方法之前,执行before方法
public void before(MyJoinPoint 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(MyJoinPoint 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(MyJoinPoint joinPoint, Throwable ex) {
log.info("出现异常" +
"\nTargetObject:" + joinPoint.getThis() +
"\nArgs:" + Arrays.toString(joinPoint.getArguments()) +
"\nThrows:" + ex.getMessage());
}
}
AOP完成
最后附上项目包结构