相关文章:
Spring5 - 30个类手写实战 - 打卡第一天(V1版本)
Spring5 - 30个类手写实战 - 打卡第二天(IOC与DI)
Spring5 - 30个类手写实战 - 打卡第三天(MVC)
Spring5 - 30个类手写实战 - 打卡第五天(手绘IOC时序图)
1.AOP
底层技术用动态代理实现。
增强、解耦。
由Spring生成的Proxy类来完成织入一些新的代码,生成一个新的类(JDK,CGLib),
要么就跟目标实现一个相同的接口
要么就是直接继承目标类,覆盖目标类的方法
真正去调用代码逻辑的时候,直接用新生成的Proxy类的代码
例:日志监听的切面
-
befor:记录方法开始调用时间,输出日志
-
after : 记录方法结束调用的时间,输出日志
-
afterThrows:如统一到某一个页面
只要有共同特征的类或者方法,都可以称之为一个切面(Aspect)。
如下例,可以将公共的部分拆分出来放到,before/after/afterThrows,在实际编写代码中,只需要关注业务逻辑代码部分即可。
public void handler(){
try {
//--------------before-------------//
long start = System.currentTimeMillis();
//--------------------------------//
... // 业务逻辑代码
//--------------after-------------//
long end = System.currentTimeMillis();
log.debug((end - start) + "ms"+""...);
//--------------------------------//
}catch (Exception e){
//--------------afterThrows-------------//
resp.getWriter().println("500");
//-------------------------------------//
}
}
通常,放到Advice(通知)中,
一个方法,对应多个Advice。可以大致理解为Map<Method,List<Advice>>
这样的结构。
AOP顶层设计
IOC==>AOP==>DI==>MVC
从调用getBean方法开始,进入到ApplicationContext,ApplicationContext内部读取配置BeanDifinition,封装成BeanWrapper。通过AopConfig判断BeanWrapper里面的类是不是要生成代理类。
可以采用注解或XML的方式配置,这里还是采用properties的方式进行配置
XML方式:
<bean id="xmlAspect" class="com.liulin.demo.aspect.LogAspect"></bean>
<aop:config>
<aop:aspect ref="xmlAspect">
<aop:before point-ref="simplePointcut" method="before"/>
<aop:after point-ref="simplePointcut" method="after"/>
<aop:after-returning point-ref="simplePointcut" method="afterReturn"/>
<aop:after-throwing point-ref="simplePointcut" method="afterThrow" throwing="ex"/>
</aop:aspect>
</aop:config>
本项目方式:
项目结构:
2.代码
LogAspect
package com.liulin.demo.aspect;
/**
* Create by DbL on 2020/5/3 0003
*/
public class LogAspect {
//在调用一个方法之前,执行before方法
public void before(){
//这个方法中的逻辑,是由我们自己写的
System.out.println("Invoker Before Method!!!");
}
//在调用一个方法之后,执行after方法
public void after(){
System.out.println("Invoker After Method!!!");
}
public void afterThrowing(){
System.out.println("出现异常");
}
}
LAdvice
package com.liulin.spring.framework.aop.aspect;
import java.lang.reflect.Method;
/**
* Create by DbL on 2020/5/3 0003
*/
public class LAdvice {
private Object aspect;
private Method adviceMethod;
private String throwName;
public LAdvice(Object aspect, Method adviceMethod) {
this.aspect = aspect;
this.adviceMethod = adviceMethod;
}
public Object getAspect() {
return aspect;
}
public void setAspect(Object aspect) {
this.aspect = aspect;
}
public Method getAdviceMethod() {
return adviceMethod;
}
public void setAdviceMethod(Method adviceMethod) {
this.adviceMethod = adviceMethod;
}
public String getThrowName() {
return throwName;
}
public void setThrowName(String throwName) {
this.throwName = throwName;
}
}
LAopConfig
package com.liulin.spring.framework.aop.config;
import lombok.Data;
/**
* Create by DbL on 2020/5/3 0003
*/
public class LAopConfig {
private String pointCut;
private String aspectClass;
private String aspectBefore;
private String aspectAfter;
private String aspectAfterThrow;
private String aspectAfterThrowingName;
public String getPointCut() {
return pointCut;
}
public void setPointCut(String pointCut) {
this.pointCut = pointCut;
}
public String getAspectClass() {
return aspectClass;
}
public void setAspectClass(String aspectClass) {
this.aspectClass = aspectClass;
}
public String getAspectBefore() {
return aspectBefore;
}
public void setAspectBefore(String aspectBefore) {
this.aspectBefore = aspectBefore;
}
public String getAspectAfter() {
return aspectAfter;
}
public void setAspectAfter(String aspectAfter) {
this.aspectAfter = aspectAfter;
}
public String getAspectAfterThrow() {
return aspectAfterThrow;
}
public void setAspectAfterThrow(String aspectAfterThrow) {
this.aspectAfterThrow = aspectAfterThrow;
}
public String getAspectAfterThrowingName() {
return aspectAfterThrowingName;
}
public void setAspectAfterThrowingName(String aspectAfterThrowingName) {
this.aspectAfterThrowingName = aspectAfterThrowingName;
}
}
LJdkDynamicAopProxy
package com.liulin.spring.framework.aop.support;
import com.liulin.spring.framework.aop.LAdvisedSupport;
import com.liulin.spring.framework.aop.aspect.LAdvice;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
/**
* Create by DbL on 2020/5/3 0003
*/
public class LJdkDynamicAopProxy implements InvocationHandler {
private LAdvisedSupport config;
public LJdkDynamicAopProxy(LAdvisedSupport config) {
this.config = config;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// key存储 befor、after、throw ...
Map<String, LAdvice> advices = config.getAdvices(method, null);
Object returnValue;
try {
invokeAdvice(advices.get("before"));
returnValue = method.invoke(this.config.getTarget(), args);
invokeAdvice(advices.get("after"));
} catch (Exception e) {
invokeAdvice(advices.get("afterThrow"));
throw e;
}
return returnValue;
}
private void invokeAdvice(LAdvice advice) {
try {
advice.getAdviceMethod().invoke(advice.getAspect());
} catch (Exception e) {
e.printStackTrace();
}
}
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), this.config.getTargetClass().getInterfaces(), this);
}
}
LAdvisedSupport
package com.liulin.spring.framework.aop;
import com.liulin.spring.framework.aop.aspect.LAdvice;
import com.liulin.spring.framework.aop.config.LAopConfig;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Create by DbL on 2020/5/3 0003
*/
public class LAdvisedSupport {
private LAopConfig config;
private Class<?> targetClass;
private Object target;
private Map<Method, Map<String, LAdvice>> methodCache;
private Pattern pointCutClassPattern;
public LAdvisedSupport(LAopConfig config) {
this.config = config;
}
// 解析配置文件的方法
private void parse() {
//pointCut=public .* com.liulin.demo.service..*Service..*(.*) 改为正则表达式
String pointCut = config.getPointCut()
.replaceAll("\\.", "\\\\.")
.replaceAll("\\\\.\\*", ".*")
.replaceAll("\\(", "\\\\(")
.replaceAll("\\)", "\\\\)");
// 1.方法的修饰符和返回值
// 2.类名
// 3.方法的名称和形参列表
// 生成匹配class的正则
String pointCutForClassRegex = pointCut.substring(0, pointCut.lastIndexOf("\\(") - 4);
pointCutClassPattern = Pattern.compile("class " + pointCutForClassRegex.substring(pointCutForClassRegex.lastIndexOf(" ") + 1));
methodCache = new HashMap<Method, Map<String, LAdvice>>();
// 专门匹配方法的正则
Pattern pointCutPattern = Pattern.compile(pointCut);
try {
Class<?> aspectClass = Class.forName(this.config.getAspectClass());
Map<String, Method> aspectMethods = new HashMap<String, Method>();
for (Method method : aspectClass.getMethods()) {
aspectMethods.put(method.getName(), method);
}
// 以上都是初始化工作,准备阶段
// 从这里开始封装LAopAdvice
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<String, LAdvice> advices = new HashMap<String, LAdvice>();
if (!(null == config.getAspectBefore() || "".equals(config.getAspectBefore()))) {
advices.put("before", new LAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAspectBefore())));
}
if (!(null == config.getAspectAfter() || "".equals(config.getAspectAfter()))) {
advices.put("after", new LAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAspectAfter())));
}
if (!(null == config.getAspectAfterThrow() || "".equals(config.getAspectAfterThrow()))) {
LAdvice advice = new LAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAspectAfterThrow()));
advice.setThrowName(config.getAspectAfterThrowingName());
advices.put("afterThrow", advice);
}
// 跟目标代理类的业务方法和Advices建立一对多的关联关系,以便在Proxy中获得
methodCache.put(method,advices);
}
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
// 根据一个目标代理类方法获得其对应的通知
public Map<String, LAdvice> getAdvices(Method method, Object o) throws Exception {
Map<String, LAdvice> 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 pointCutMatch() {
return pointCutClassPattern.matcher(this.targetClass.toString()).matches();
}
public void setTargetClass(Class<?> targetClass) {
this.targetClass = targetClass;
parse();
}
public Class getTargetClass() {
return this.targetClass ;
}
public void setTarget(Object target) {
this.target = target;
}
public Object getTarget() {
return this.target;
}
}
LApplicationContext
仅修改以下方法:
private Object instantiateBean(String beanName, LBeanDefinition beanDefinition) {
String className = beanDefinition.getBeanClassName();
Object instance = null;
try {
if(this.factoryBeanObjectCache.containsKey(beanName)){
instance = factoryBeanObjectCache.get(beanName);
}else {
Class<?> aClass = Class.forName(className);
instance = aClass.newInstance();
//====================AOP START==================//
// 如果满足条件,就指直接返回Proxy对象
// 1.加载AOP配置文件
LAdvisedSupport config = instantionAopConfig(beanDefinition);
config.setTargetClass(aClass);
config.setTarget(instance);
// 判断规则,要不要生成代理类,如果要就覆盖原生对象,如果不要就不做任何处理,返回原生对象
if(config.pointCutMatch()){
instance = new LJdkDynamicAopProxy(config).getProxy();
}
//====================AOP END==================//
this.factoryBeanObjectCache.put(beanName, instance);
}
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}
3.测试
例:
http://localhost:8080/web/query.json?name=DBL
http://localhost:8080/web/add.json?name=DBL&addr=chongqing