专栏首页Java架构师学习SpringAOP那些无处不在的动态代理

SpringAOP那些无处不在的动态代理

环境配置

代码结构

pom.xml文件
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
com.gupaoedu
gupaoedu-springaop
0.0.1-SNAPSHOT
gupaoedu-springaop
1.8
org.projectlombok
lombok
1.16.10
org.slf4j
slf4j-api
1.7.25
ch.qos.logback
logback-classic
1.2.3
application.properties文件
#多切面配置可以在key前面加前缀
#例如 aspect.logAspect.
#切面表达式,expression#
pointCut=public.* com.gupaoedu.springaop.demo.service..*Service..*(.*)
#切面类#
aspectClass=com.gupaoedu.springaop.demo.aspect.LogAspect
#切面前置通知#
aspectBefore=before
#切面后置通知#
aspectAfter=after
#切面异常通知#
aspectAfterThrow=afterThrowing
#切面异常类型#
aspectAfterThrowingName=java.lang.Exception
业务代码
Member实体类
packagecom.gupaoedu.springaop.demo.model;
publicclassMember{
}
IMermverServer接口
packagecom.gupaoedu.springaop.demo.service;
importcom.gupaoedu.springaop.demo.model.Member;
/**
* 注解版业务操作类
*@authorTom
*/
publicinterfaceIMemberService{
publicMemberget(String id);
publicMemberget();
publicvoidsave(Member member);
publicBooleandelete(String id)throwsException;
}
MemberService业务实现类
packagecom.gupaoedu.springaop.demo.service.impl;
importcom.gupaoedu.springaop.demo.model.Member;
importcom.gupaoedu.springaop.demo.service.IMemberService;
importlombok.extern.slf4j.Slf4j;
/**
* 注解版业务操作类
*@authorTom
*/
@Slf4j
publicclassMemberServiceimplementsIMemberService{
publicMemberget(String id){
log.info("getMemberById method . . .");
returnnewMember();
}
publicMemberget(){
log.info("getMember method . . .");
returnnewMember();
}
publicvoidsave(Member member){
log.info("save member method . . .");
}
publicBooleandelete(String id)throwsException{
log.info("delete method . . .");
thrownewException("spring aop ThrowAdvice演示");
}
}
LogAspect切面增强类
packagecom.gupaoedu.springaop.demo.aspect;
importlombok.extern.slf4j.Slf4j;
/**
* Created by Tom.
*/
@Slf4j
publicclassLogAspect{
//在调用一个方法之前,执行before方法
publicvoidbefore(){
//这个方法中的逻辑,是由我们自己写的
log.info("Invoker Before Method!!!");
}
//在调用一个方法之后,执行after方法
publicvoidafter(){
log.info("Invoker After Method!!!");
}
publicvoidafterThrowing(){
log.info("出现异常");
}
}
仿写JDK动态代理
GPInvocationHandler JDK动态代理接口
packagecom.gupaoedu.springaop.framework.proxy;
importjava.lang.reflect.Method;
/**
* Created by Tom.
*/
publicinterfaceGPInvocationHandler{
Objectinvoke(Object proxy, Method method, Object[] args)throwsThrowable;
}
GPClassLoader自定义类加载器
packagecom.gupaoedu.springaop.framework.proxy;
importjava.io.ByteArrayOutputStream;
importjava.io.File;
importjava.io.FileInputStream;
/**
* Created by Tom.
*/
publicclassGPClassLoaderextendsClassLoader{
privateFile classPathFile;
publicGPClassLoader(){
String classPath = GPClassLoader.class.getResource("").getPath();
this.classPathFile =newFile(classPath);
}
@Override
protectedClass findClass(String name)throwsClassNotFoundException {
String className = GPClassLoader.class.getPackage().getName() +"."+ name;
if(classPathFile  !=null){
File classFile =newFile(classPathFile,name.replaceAll("\\.","/") +".class");
if(classFile.exists()){
FileInputStream in =null;
ByteArrayOutputStream out =null;
try{
in =newFileInputStream(classFile);
out =newByteArrayOutputStream();
byte[] buff =newbyte[1024];
intlen;
while((len = in.read(buff)) != -1){
out.write(buff,0,len);
}
returndefineClass(className,out.toByteArray(),0,out.size());
}catch(Exception e){
e.printStackTrace();
}
}
}
returnnull;
}
}
GPProxy 动态生成代理类
packagecom.gupaoedu.springaop.framework.proxy;
importjavax.tools.JavaCompiler;
importjavax.tools.StandardJavaFileManager;
importjavax.tools.ToolProvider;
importjava.io.File;
importjava.io.FileWriter;
importjava.lang.reflect.Constructor;
importjava.lang.reflect.Method;
importjava.util.HashMap;
importjava.util.Map;
/**
* 用来生成源代码的工具类
* Created by Tom.
*/
publicclassGPProxy{
publicstaticfinalString ln ="\r\n";
publicstaticObjectnewProxyInstance(GPClassLoader classLoader, Class<?> [] interfaces, GPInvocationHandler h){
try{
//1、动态生成源代码.java文件
String src = generateSrc(interfaces);
//2、Java文件输出磁盘
String filePath = GPProxy.class.getResource("").getPath();
//           System.out.println(filePath);
File f =newFile(filePath +"$Proxy0.java");
FileWriter fw =newFileWriter(f);
fw.write(src);
fw.flush();
fw.close();
//3、把生成的.java文件编译成.class文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manage = compiler.getStandardFileManager(null,null,null);
Iterable iterable = manage.getJavaFileObjects(f);
JavaCompiler.CompilationTask task = compiler.getTask(null,manage,null,null,null,iterable);
task.call();
manage.close();
//4、编译生成的.class文件加载到JVM中来
Class proxyClass =  classLoader.findClass("$Proxy0");
Constructor c = proxyClass.getConstructor(GPInvocationHandler.class);
f.delete();
//5、返回字节码重组以后的新的代理对象
returnc.newInstance(h);
}catch(Exception e){
e.printStackTrace();
}
returnnull;
}
privatestaticStringgenerateSrc(Class<?>[] interfaces){
StringBuffer sb =newStringBuffer();
sb.append("package com.gupaoedu.springaop.framework.proxy;"+ ln);
sb.append("import java.lang.reflect.*;"+ ln);
sb.append("public class $Proxy0 implements "+ interfaces[0].getName() +"{"+ ln);
sb.append("GPInvocationHandler h;"+ ln);
sb.append("public $Proxy0(GPInvocationHandler h) { "+ ln);
sb.append("this.h = h;");
sb.append("}"+ ln);
for(Method m : interfaces[0].getMethods()){
Class[] params = m.getParameterTypes();
StringBuffer paramNames =newStringBuffer();
StringBuffer paramValues =newStringBuffer();
StringBuffer paramTypes =newStringBuffer();
for(inti =0; i < params.length; i++) {
Class clazz = params[i];
String type = clazz.getName();
String paramName = toLowerFirstCase(clazz.getSimpleName());
paramNames.append(type +" "+  paramName);
paramValues.append(paramName);
paramTypes.append(clazz.getName() +".class");
if(i >0&& i < params.length-1){
paramNames.append(",");
paramTypes.append(",");
paramValues.append(",");
}
}
sb.append("public "+ m.getReturnType().getName() +" "+ m.getName() +"("+ paramNames.toString() +") {"+ ln);
sb.append("try{"+ ln);
sb.append("Method m = "+ interfaces[0].getName() +".class.getMethod(\""+ m.getName() +"\",new Class[]{"+ paramTypes.toString() +"});"+ ln);
sb.append((hasReturnValue(m.getReturnType()) ?"return ("+ m.getReturnType().getName() +")":"") + getCaseCode("this.h.invoke(this,m,new Object[]{"+ paramValues +"})",m.getReturnType()) +";"+ ln);
sb.append("}catch(Error _ex) { }");
sb.append("catch(Throwable e){"+ ln);
sb.append("throw new UndeclaredThrowableException(e);"+ ln);
sb.append("}");
sb.append(getReturnEmptyCode(m.getReturnType()));
sb.append("}");
}
sb.append("}"+ ln);
returnsb.toString();
}
privatestaticMap mappings =newHashMap();
static{
mappings.put(int.class,Integer.class);
}
privatestaticStringgetReturnEmptyCode(Class<?> returnClass){
if(mappings.containsKey(returnClass)){
return"return 0;";
}elseif(returnClass ==void.class){
return"";
}else{
return"return null;";
}
}
privatestaticStringgetCaseCode(String code,Class<?> returnClass){
if(mappings.containsKey(returnClass)){
return"(("+ mappings.get(returnClass).getName() +")"+ code +")."+ returnClass.getSimpleName() +"Value()";
}
returncode;
}
privatestaticbooleanhasReturnValue(Class<?> clazz){
returnclazz !=void.class;
}
privatestaticStringtoLowerFirstCase(String src){
char[] chars = src.toCharArray();
chars[0] +=32;
returnString.valueOf(chars);
}
}
手写Spring AOP
GPAopConfig 保存配置信息
packagecom.gupaoedu.springaop.framework.config;
importlombok.Data;
/**
* Created by Tom.
*/
@Data
publicclassGPAopConfig{
privateString pointCut;
privateString aspectBefore;
privateString aspectAfter;
privateString aspectClass;
privateString aspectAfterThrow;
privateString aspectAfterThrowingName;
}
GPAdvice通知接口
packagecom.gupaoedu.springaop.framework.aspect;
importjava.lang.reflect.Method;
/**
* 用于通知回调
*/
publicclassGPAdvice{
privateObject aspect;
privateMethod adviceMethod;
privateString throwName;
publicGPAdvice(Object aspect, Method adviceMethod){
this.aspect = aspect;
this.adviceMethod = adviceMethod;
}
publicObjectgetAspect(){
returnaspect;
}
publicMethodgetAdviceMethod(){
returnadviceMethod;
}
publicvoidsetThrowName(String throwName){
this.throwName = throwName;
}
publicStringgetThrowName(){
returnthrowName;
}
}
GPJoinPoint切点
packagecom.gupaoedu.springaop.framework.aspect;
importjava.lang.reflect.Method;
publicinterfaceGPJoinPoint{
ObjectgetThis();
Object[] getArguments();
MethodgetMethod();
voidsetUserAttribute(String key, Object value);
ObjectgetUserAttribute(String key);
}
GPAdvisedSupport 解析切面配置
packagecom.gupaoedu.springaop.framework.aop;
importcom.gupaoedu.springaop.framework.aspect.GPAdvice;
importcom.gupaoedu.springaop.framework.config.GPAopConfig;
importcom.gupaoedu.springaop.framework.proxy.GPClassLoader;
importjava.lang.reflect.Method;
importjava.util.HashMap;
importjava.util.Map;
importjava.util.regex.Matcher;
importjava.util.regex.Pattern;
/**
* Created by Tom.
*/
publicclassGPAdvisedSupport{
privateGPClassLoader classLoader;
privateClass targetClass;
privateObject target;
privateGPAopConfig config;
privatePattern pointCutClassPattern;
privatetransientMap> methodCache;
publicGPAdvisedSupport(GPAopConfig config){
this.config = config;
this.classLoader =newGPClassLoader();
}
publicClass getTargetClass(){
returnthis.targetClass;
}
publicObjectgetTarget(){
returnthis.target;
}
publicMapgetInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass)throwsException{
Map cached = methodCache.get(method);
if(cached ==null){
Method m = targetClass.getMethod(method.getName(),method.getParameterTypes());
cached = methodCache.get(m);
//底层逻辑,对代理方法进行一个兼容处理
this.methodCache.put(m,cached);
}
returncached;
}
publicvoidsetTargetClass(Class<?> targetClass){
this.targetClass = targetClass;
parse();
}
privatevoidparse(){
String pointCut = config.getPointCut()
.replaceAll("\\.","\\\\.")
.replaceAll("\\\\.\\*",".*")
.replaceAll("\\(","\\\\(")
.replaceAll("\\)","\\\\)");
//pointCut=public .* com.gupaoedu.vip.spring.demo.service..*Service..*(.*)
//玩正则
String pointCutForClassRegex = pointCut.substring(0,pointCut.lastIndexOf("\\(") -4);
pointCutClassPattern = Pattern.compile("class "+ pointCutForClassRegex.substring(
pointCutForClassRegex.lastIndexOf(" ") +1));
try{
methodCache =newHashMap>();
Pattern pattern = Pattern.compile(pointCut);
Class aspectClass = Class.forName(this.config.getAspectClass());
Map aspectMethods =newHashMap();
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()){
//执行器链
Map advices =newHashMap();
//把每一个方法包装成 MethodIterceptor
//before
if(!(null== config.getAspectBefore() ||"".equals(config.getAspectBefore()))) {
//创建一个Advivce
advices.put("before",newGPAdvice(aspectClass.newInstance(),aspectMethods.get(config.getAspectBefore())));
}
//after
if(!(null== config.getAspectAfter() ||"".equals(config.getAspectAfter()))) {
//创建一个Advivce
advices.put("after",newGPAdvice(aspectClass.newInstance(),aspectMethods.get(config.getAspectAfter())));
}
//afterThrowing
if(!(null== config.getAspectAfterThrow() ||"".equals(config.getAspectAfterThrow()))) {
//创建一个Advivce
GPAdvice throwingAdvice =newGPAdvice(aspectClass.newInstance(),aspectMethods.get(config.getAspectAfterThrow()));
throwingAdvice.setThrowName(config.getAspectAfterThrowingName());
advices.put("afterThrow",throwingAdvice);
}
methodCache.put(m,advices);
}
}
}catch(Exception e){
e.printStackTrace();
}
}
publicvoidsetTarget(Object target){
this.target = target;
}
publicbooleanpointCutMatch(){
returnpointCutClassPattern.matcher(this.targetClass.toString()).matches();
}
publicGPClassLoadergetClassLoader(){returnclassLoader; }
}
GPJdkDynamicAopProxy实现动态代理
packagecom.gupaoedu.springaop.framework.aop;
importcom.gupaoedu.springaop.framework.aspect.GPAdvice;
importcom.gupaoedu.springaop.framework.proxy.GPClassLoader;
importcom.gupaoedu.springaop.framework.proxy.GPInvocationHandler;
importcom.gupaoedu.springaop.framework.proxy.GPProxy;
importjava.lang.reflect.InvocationTargetException;
importjava.lang.reflect.Method;
importjava.util.Map;
/**
* Created by Tom.
*/
publicclassGPJdkDynamicAopProxyimplementsGPInvocationHandler{
privateGPAdvisedSupport advised;
publicGPJdkDynamicAopProxy(GPAdvisedSupport config){
this.advised = config;
}
publicObjectgetProxy(){
returngetProxy(this.advised.getClassLoader());
}
publicObjectgetProxy(GPClassLoader classLoader){
returnGPProxy.newProxyInstance(classLoader,this.advised.getTargetClass().getInterfaces(),this);
}
publicObjectinvoke(Object proxy, Method method, Object[] args)throwsThrowable{
Map advices =this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,this.advised.getTargetClass());
invokeAdvice(advices.get("before"));
Object returnValue =null;
try{
returnValue = method.invoke(this.advised.getTarget(), args);
}catch(Exception e){
invokeAdvice(advices.get("afterThrow"));
}
invokeAdvice(advices.get("after"));
returnreturnValue;
}
privatevoidinvokeAdvice(GPAdvice advice){
try{
advice.getAdviceMethod().invoke(advice.getAspect());
}catch(IllegalAccessException e) {
e.printStackTrace();
}catch(InvocationTargetException e) {
e.printStackTrace();
}
}
}
测试代码
MemberServiceTest测试用例
packagecom.gupaoedu.springaop.service;
importcom.gupaoedu.springaop.demo.service.IMemberService;
importcom.gupaoedu.springaop.framework.GPApplicationContext;
publicclassMemberServiceTest{
publicstaticvoidmain(String[] args){
GPApplicationContext applicationContext =newGPApplicationContext();
IMemberService memberService = (IMemberService)applicationContext.getBean("memberService");
try{
memberService.delete("1");
}catch(Exception e) {
e.printStackTrace();
}
}
}

运行结果

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

推荐阅读

  • 远程办公经验为0,如何将日常工作平滑过度到线上?

    我是一名创业者,我的公司(深圳市友浩达科技有限公司)在2018年8月8日开始运营,现在还属于微型公司。这个春节假期,我一直十分关注疫情动向,也非常关心其对公司带来的影响。

    TVP官方团队
    TAPD 敏捷项目管理腾讯乐享企业邮箱企业编程算法
  • 数据中台,概念炒作还是另有奇效? | TVP思享

    作者简介:史凯,花名凯哥,腾讯云最具价值专家TVP,ThoughtWorks数据智能业务总经理。投身于企业数字化转型工作近20年。2000年初,在IBM 研发企业级中间件,接着加入埃森哲,为大型企业提供信息化架构规划,设计,ERP,云平台,数据仓库构建等技术咨询实施服务,随后在EMC负责企业应用转型业务,为企业提供云迁移,应用现代化服务。现在专注于企业智能化转型领域,是数据驱动的数字化转型的行业布道者,数据中台的推广者,精益数据创新体系的创始人,2019年荣获全球Data IQ 100人的数据赋能者称号,创业邦卓越生态聚合赋能官TOP 5。2019年度数字化转型专家奖。打造了行业第一个数据创新的数字化转型卡牌和工作坊。创建了精益数据创新方法论体系构建数据驱动的智能企业,并在多个企业验证成功,正在向国内外推广。

    TVP官方团队
    大数据数据分析企业
  • 扩展 Kubernetes 之 CRI

    使用 cri-containerd 的调用流程更为简洁, 省去了上面的调用流程的 1,2 两步

    王磊-AI基础
    Kubernetes
  • 扩展 Kubernetes 之 Kubectl Plugin

    kubectl 功能非常强大, 常见的命令使用方式可以参考 kubectl --help,或者这篇文章

    王磊-AI基础
    Kubernetes
  • 多种登录方式定量性能测试方案

    最近接到到一个测试任务,某服务提供了两种登录方式:1、账号密码登录;2、手机号+验证码登录。要对这两种登录按照一定的比例进行压测。

    八音弦
    测试服务 WeTest
  • 线程安全类在性能测试中应用

    首先验证接口参数签名是否正确,然后加锁去判断订单信息和状态,处理用户增添VIP时间事务,成功之后释放锁。锁是针对用户和订单的分布式锁,使用方案是用的redis。

    八音弦
    安全编程算法
  • 使用CDN(jsdelivr) 优化博客访问速度

    PS: 此篇文章适用于 使用 Github pages 或者 coding pages 的朋友,其他博客也类似.

    IFONLY@CUIT
    CDNGitGitHub开源
  • 扩展 Kubernetes 之 CNI

    Network Configuration 是 CNI 输入参数中最重要当部分, 可以存储在磁盘上

    王磊-AI基础
    Kubernetes
  • 聚焦【技术应变力】云加社区沙龙online重磅上线!

    云加社区结合特殊时期热点,挑选备受关注的音视频流量暴增、线下业务快速转线上、紧急上线防疫IoT应用等话题,邀请众多业界专家,为大家提供连续十一天的干货分享。从视野、预判、应对等多角度,帮助大家全面提升「技术应变力」!

    腾小云
  • 京东购物小程序购物车性能优化实践

    它是小程序开发工具内置的一个可视化监控工具,能够在 OS 级别上实时记录系统资源的使用情况。

    WecTeam
    渲染JavaScripthttps网络安全缓存

扫码关注云+社区

领取腾讯云代金券