[TOC]
在完全掌握 Spring 系统结构,实现原理,在理解设计模式的基础上,自己动手写一个高仿真版本的 Spring 框架,以达到透彻理解 Spring 的目的,感受作者创作意图
1. 基础配置
在 appliction.properties 中增力加如下自定义配置∶
#类扫描包路径
scanPackage=com.gupaoedu.vip.demo
templateRoot=layouts
#切面表达式 expression
pointCut=public .* com.gupaoedu.vip.demo.service..*Service..*(.*)
#切面类
aspectClass=com.gupaoedu.vip.demo.aspect.LogAspect
#切面前置通知
aspectBefore=before
#切面后置通知
aspectAfter=after
#切面异常通知
aspectAfterThrow=afterThrowing
#切面异常类型
aspectAfterThrowingName=java.lang.Exception
下面是 Spring AOP 的原生配置,为了方便操作,用 properties 文件来代替 xml,以简化操作∶
2. 完成 AOP 顶层设计
2.1 GPJdkDynamicAopProxy 基于 JDK 动态代理实现
public class GPJdkDynamicAopProxy implements InvocationHandler {
private GPAdvisedSupport config;
public GPJdkDynamicAopProxy(GPAdvisedSupport config) {
this.config = config;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Map advices = config.getAdvices(method,null);
Object returnValue;
try {
invokeAdivce(advices.get("before"));
returnValue = method.invoke(this.config.getTarget(),args);
invokeAdivce(advices.get("after"));
}catch (Exception e){
invokeAdivce(advices.get("afterThrow"));
throw e;
}
return returnValue;
}
private void invokeAdivce(GPAdvice advice) {
try {
advice.getAdviceMethod().invoke(advice.getAspect());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(),this.config.getTargetClass().getInterfaces(),this);
}
}
2.2 GPAdvisedSupport 解析配置
public class GPAdvisedSupport {
private GPAopConfig config;
private Object target;
private Class targetClass;
private Pattern pointCutClassPattern;
private Map> methodCache;
public GPAdvisedSupport(GPAopConfig config) {
this.config = config;
}
//解析配置文件的方法
private void parse() {
//把 Spring 的 Excpress 变成 Java 能够识别的正则表达式
String pointCut = config.getPointCut().replaceAll("\\.", "\\\\.").replaceAll("\\\\.\\*", ".*")
.replaceAll("\\(", "\\\\(").replaceAll("\\)", "\\\\)");
//保存专门匹配 Class 的正则
String pointCutForClassRegex = pointCut.substring(0, pointCut.lastIndexOf("\\(") - 4);
pointCutClassPattern = Pattern.compile("class " + pointCutForClassRegex.substring(pointCutForClassRegex.lastIndexOf(" ") + 1));
//享元的共享池
methodCache = new HashMap>();
//保存专门匹配方法的正则
Pattern pointCutPattern = Pattern.compile(pointCut);
try {
Class aspectClass = Class.forName(this.config.getAspectClass());
Map aspectMethods = new HashMap();
for (Method method : aspectClass.getMethods()) {
aspectMethods.put(method.getName(), method);
}
for (Method method : this.targetClass.getMethods()) {
String methodString = method.toString();
if (methodString.contains("throws")) {
methodString = methodString.substring(0, methodString.lastIndexOf("throws")).trim();
}
Matcher matcher = pointCutPattern.matcher(methodString);
if (matcher.matches()) {
Map advices = new HashMap();
if (!(null == config.getAspectBefore() || "".equals(config.getAspectBefore()))) {
advices.put("before", new GPAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAspectBefore())));
}
if (!(null == config.getAspectAfter() || "".equals(config.getAspectAfter()))) {
advices.put("after", new GPAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAspectAfter())));
}
if (!(null == config.getAspectAfterThrow() || "".equals(config.getAspectAfterThrow()))) {
GPAdvice advice = new GPAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAspectAfterThrow()));
advice.setThrowName(config.getAspectAfterThrowingName());
advices.put("afterThrow", advice);
}
//跟目标代理类的业务方法和 Advices 建立一对多个关联关系,以便在 Porxy 类中获得
methodCache.put(method, advices);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
//根据一个目标代理类的方法,获得其对应的通知
public Map getAdvices(Method method, Object o) throws Exception {
//享元设计模式的应用
Map cache = methodCache.get(method);
if (null == cache) {
Method m = targetClass.getMethod(method.getName(), method.getParameterTypes());
cache = methodCache.get(m);
this.methodCache.put(m, cache);
}
return cache;
}
//给 ApplicationContext 首先 IoC 中的对象初始化时调用,决定要不要生成代理类的逻辑
public boolean pointCutMath() {
return pointCutClassPattern.matcher(this.targetClass.toString()).matches();
}
public void setTargetClass(Class targetClass) {
this.targetClass = targetClass;
parse();
}
public void setTarget(Object target) {
this.target = target;
}
public Class getTargetClass() {
return targetClass;
}
public Object getTarget() {
return target;
}
}
2.3 GPAdvice 通知接口定义
@Data
@NoArgsConstructor
@AllArgsConstructor
public class GPAdvice {
private Object aspect;
private Method adviceMethod;
private String throwName;
public GPAdvice(Object aspect, Method adviceMethod) {
this.aspect = aspect;
this.adviceMethod = adviceMethod;
}
}
2.4 GPAopConfig 封装配置
@Data
@NoArgsConstructor
@AllArgsConstructor
public class GPAopConfig {
private String pointCut;
private String aspectClass;
private String aspectBefore;
private String aspectAfter;
private String aspectAfterThrow;
private String aspectAfterThrowingName;
}
3. 设计 AOP 基础实现
3.1 接入 getBean() 方法与 IOC 容器衔接
找到 GPApplicationContext 的 getBean() 方法,我们知道 getBean() 中负责 Bean 初始化的方法其实就是 instantiateBean(),我们在初始化时就可以确定是否返回原生 Bean 还是 Proxy Bean.代码实现如下
public class GPApplicationContext {
private GPBeanDefinitionReader reader;
private Map beanDefinitionMap = new HashMap();
private Map factoryBeanInstanceCache = new HashMap();
private Map factoryBeanObjectCache = new HashMap();
public GPApplicationContext(String... configLocations) {
//1、加载配置文件
reader = new GPBeanDefinitionReader(configLocations);
try {
//2、解析配置文件,封装成 BeanDefinition
List beanDefinitions = reader.loadBeanDefinitions();
//3、把 BeanDefintion 缓存起来
doRegistBeanDefinition(beanDefinitions);
doAutowrited();
} catch (Exception e) {
e.printStackTrace();
}
}
private void doAutowrited() {
//调用 getBean()
//这一步,所有的 Bean 并没有真正的实例化,还只是配置阶段
for (Map.Entry beanDefinitionEntry : this.beanDefinitionMap.entrySet()) {
String beanName = beanDefinitionEntry.getKey();
getBean(beanName);
}
}
private void doRegistBeanDefinition(List beanDefinitions) throws Exception {
for (GPBeanDefinition beanDefinition : beanDefinitions) {
if (this.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())) {
throw new Exception("The " + beanDefinition.getFactoryBeanName() + "is exists");
}
beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
beanDefinitionMap.put(beanDefinition.getBeanClassName(), beanDefinition);
}
}
//Bean 的实例化,DI 是从而这个方法开始的
public Object getBean(String beanName) {
//1、先拿到 BeanDefinition 配置信息
GPBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
//2、反射实例化 newInstance();
Object instance = instantiateBean(beanName, beanDefinition);
//3、封装成一个叫做 BeanWrapper
GPBeanWrapper beanWrapper = new GPBeanWrapper(instance);
//4、保存到 IoC 容器
factoryBeanInstanceCache.put(beanName, beanWrapper);
//5、执行依赖注入
populateBean(beanName, beanDefinition, beanWrapper);
return beanWrapper.getWrapperInstance();
}
private void populateBean(String beanName, GPBeanDefinition beanDefinition, GPBeanWrapper beanWrapper) {
//可能涉及到循环依赖?
//A{ B b}
//B{ A b}
//用两个缓存,循环两次
//1、把第一次读取结果为空的 BeanDefinition 存到第一个缓存
//2、等第一次循环之后,第二次循环再检查第一次的缓存,再进行赋值
Object instance = beanWrapper.getWrapperInstance();
Class clazz = beanWrapper.getWrappedClass();
//在 Spring 中@Component
if (!(clazz.isAnnotationPresent(GPController.class) || clazz.isAnnotationPresent(GPService.class))) {
return;
}
//把所有的包括 private/protected/default/public 修饰字段都取出来
for (Field field : clazz.getDeclaredFields()) {
if (!field.isAnnotationPresent(GPAutowired.class)) {
continue;
}
GPAutowired autowired = field.getAnnotation(GPAutowired.class);
//如果用户没有自定义的 beanName,就默认根据类型注入
String autowiredBeanName = autowired.value().trim();
if ("".equals(autowiredBeanName)) {
//field.getType().getName() 获取字段的类型
autowiredBeanName = field.getType().getName();
}
//暴力访问
field.setAccessible(true);
try {
if (this.factoryBeanInstanceCache.get(autowiredBeanName) == null) {
continue;
}
//ioc.get(beanName) 相当于通过接口的全名拿到接口的实现的实例
field.set(instance, this.factoryBeanInstanceCache.get(autowiredBeanName).getWrapperInstance());
} catch (IllegalAccessException e) {
e.printStackTrace();
continue;
}
}
}
//创建真正的实例对象
private Object instantiateBean(String beanName, GPBeanDefinition beanDefinition) {
String className = beanDefinition.getBeanClassName();
Object instance = null;
try {
if (this.factoryBeanObjectCache.containsKey(beanName)) {
instance = this.factoryBeanObjectCache.get(beanName);
} else {
Class clazz = Class.forName(className);
//2、默认的类名首字母小写
instance = clazz.newInstance();
//==================AOP 开始=========================
//如果满足条件,就直接返回 Proxy 对象
//1、加载 AOP 的配置文件
GPAdvisedSupport config = instantionAopConfig(beanDefinition);
config.setTargetClass(clazz);
config.setTarget(instance);
//判断规则,要不要生成代理类,如果要就覆盖原生对象
//如果不要就不做任何处理,返回原生对象
if (config.pointCutMath()) {
instance = new GPJdkDynamicAopProxy(config).getProxy();
}
//===================AOP 结束========================
this.factoryBeanObjectCache.put(beanName, instance);
}
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}
private GPAdvisedSupport instantionAopConfig(GPBeanDefinition beanDefinition) {
GPAopConfig config = new GPAopConfig();
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 GPAdvisedSupport(config);
}
public Object getBean(Class beanClass) {
return getBean(beanClass.getName());
}
public int getBeanDefinitionCount() {
return this.beanDefinitionMap.size();
}
public String[] getBeanDefinitionNames() {
return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
}
public Properties getConfig() {
return this.reader.getConfig();
}
}
4. 织入业务代码
4.1 LogAspect 自定义切面配置
@Slf4j
public class LogAspect {
//在调用一个方法之前,执行 before 方法
public void before() {
//这个方法中的逻辑,是由我们自己写的
log.info("Invoker Before Method!!!");
}
//在调用一个方法之后,执行 after 方法
public void after() {
log.info("Invoker After Method!!!");
}
public void afterThrowing() {
log.info("出现异常");
}
}