<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.gupaoedu</groupId>
<artifactId>gupaoedu-springaop</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>gupaoedu-springaop</name>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</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>
</project>
#多切面配置可以在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
package com.gupaoedu.springaop.demo.model;
public class Member {
}
package com.gupaoedu.springaop.demo.service;
import com.gupaoedu.springaop.demo.model.Member;
/**
* 注解版业务操作类
* @author Tom
*/
public interface IMemberService {
public Member get(String id);
public Member get();
public void save(Member member);
public Boolean delete(String id) throws Exception;
}
package com.gupaoedu.springaop.demo.service.impl;
import com.gupaoedu.springaop.demo.model.Member;
import com.gupaoedu.springaop.demo.service.IMemberService;
import lombok.extern.slf4j.Slf4j;
/**
* 注解版业务操作类
* @author Tom
*/
@Slf4j
public class MemberService implements IMemberService{
public Member get(String id){
log.info("getMemberById method . . .");
return new Member();
}
public Member get(){
log.info("getMember method . . .");
return new Member();
}
public void save(Member member){
log.info("save member method . . .");
}
public Boolean delete(String id) throws Exception{
log.info("delete method . . .");
throw new Exception("spring aop ThrowAdvice演示");
}
}
package com.gupaoedu.springaop.demo.aspect;
import lombok.extern.slf4j.Slf4j;
/**
* Created by Tom.
*/
@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("出现异常");
}
}
package com.gupaoedu.springaop.framework.proxy;
import java.lang.reflect.Method;
/**
* Created by Tom.
*/
public interface GPInvocationHandler {
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
package com.gupaoedu.springaop.framework.proxy;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
/**
* Created by Tom.
*/
public class GPClassLoader extends ClassLoader {
private File classPathFile;
public GPClassLoader(){
String classPath = GPClassLoader.class.getResource("").getPath();
this.classPathFile = new File(classPath);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String className = GPClassLoader.class.getPackage().getName() + "." + name;
if(classPathFile != null){
File classFile = new File(classPathFile,name.replaceAll("//.","/") + ".class");
if(classFile.exists()){
FileInputStream in = null;
ByteArrayOutputStream out = null;
try{
in = new FileInputStream(classFile);
out = new ByteArrayOutputStream();
byte [] buff = new byte[1024];
int len;
while ((len = in.read(buff)) != -1){
out.write(buff,0,len);
}
return defineClass(className,out.toByteArray(),0,out.size());
}catch (Exception e){
e.printStackTrace();
}
}
}
return null;
}
}
package com.gupaoedu.springaop.framework.proxy;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* 用来生成源代码的工具类
* Created by Tom.
*/
public class GPProxy {
public static final String ln = "/r/n";
public static Object newProxyInstance(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 = new File(filePath + "$Proxy0.java");
FileWriter fw = new FileWriter(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、返回字节码重组以后的新的代理对象
return c.newInstance(h);
}catch (Exception e){
e.printStackTrace();
}
return null;
}
private static String generateSrc(Class<?>[] interfaces){
StringBuffer sb = new StringBuffer();
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 = new StringBuffer();
StringBuffer paramValues = new StringBuffer();
StringBuffer paramTypes = new StringBuffer();
for (int i = 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);
return sb.toString();
}
private static Map<Class,Class> mappings = new HashMap<Class, Class>();
static {
mappings.put(int.class,Integer.class);
}
private static String getReturnEmptyCode(Class<?> returnClass){
if(mappings.containsKey(returnClass)){
return "return 0;";
}else if(returnClass == void.class){
return "";
}else {
return "return null;";
}
}
private static String getCaseCode(String code,Class<?> returnClass){
if(mappings.containsKey(returnClass)){
return "((" + mappings.get(returnClass).getName() + ")" + code + ")." + returnClass.getSimpleName() + "Value()";
}
return code;
}
private static boolean hasReturnValue(Class<?> clazz){
return clazz != void.class;
}
private static String toLowerFirstCase(String src){
char [] chars = src.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}
}
package com.gupaoedu.springaop.framework.config;
import lombok.Data;
/**
* Created by Tom.
*/
@Data
public class GPAopConfig {
private String pointCut;
private String aspectBefore;
private String aspectAfter;
private String aspectClass;
private String aspectAfterThrow;
private String aspectAfterThrowingName;
}
package com.gupaoedu.springaop.framework.aspect;
import java.lang.reflect.Method;
/**
* 用于通知回调
*/
public class GPAdvice {
private Object aspect;
private Method adviceMethod;
private String throwName;
public GPAdvice(Object aspect, Method adviceMethod) {
this.aspect = aspect;
this.adviceMethod = adviceMethod;
}
public Object getAspect() {
return aspect;
}
public Method getAdviceMethod() {
return adviceMethod;
}
public void setThrowName(String throwName) {
this.throwName = throwName;
}
public String getThrowName() {
return throwName;
}
}
package com.gupaoedu.springaop.framework.aspect;
import java.lang.reflect.Method;
public interface GPJoinPoint {
Object getThis();
Object[] getArguments();
Method getMethod();
void setUserAttribute(String key, Object value);
Object getUserAttribute(String key);
}
package com.gupaoedu.springaop.framework.aop;
import com.gupaoedu.springaop.framework.aspect.GPAdvice;
import com.gupaoedu.springaop.framework.config.GPAopConfig;
import com.gupaoedu.springaop.framework.proxy.GPClassLoader;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by Tom.
*/
public class GPAdvisedSupport {
private GPClassLoader classLoader;
private Class<?> targetClass;
private Object target;
private GPAopConfig config;
private Pattern pointCutClassPattern;
private transient Map<Method, Map<String,GPAdvice>> methodCache;
public GPAdvisedSupport(GPAopConfig config) {
this.config = config;
this.classLoader = new GPClassLoader();
}
public Class<?> getTargetClass(){
return this.targetClass;
}
public Object getTarget(){
return this.target;
}
public Map<String,GPAdvice> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) throws Exception{
Map<String,GPAdvice> 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 setTargetClass(Class<?> targetClass) {
this.targetClass = targetClass;
parse();
}
private void parse() {
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 = new HashMap<Method, Map<String,GPAdvice>>();
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()){
//执行器链
Map<String,GPAdvice> advices = new HashMap<String, GPAdvice>();
//把每一个方法包装成 MethodIterceptor
//before
if(!(null == config.getAspectBefore() || "".equals(config.getAspectBefore()))) {
//创建一个Advivce
advices.put("before",new GPAdvice(aspectClass.newInstance(),aspectMethods.get(config.getAspectBefore())));
}
//after
if(!(null == config.getAspectAfter() || "".equals(config.getAspectAfter()))) {
//创建一个Advivce
advices.put("after",new GPAdvice(aspectClass.newInstance(),aspectMethods.get(config.getAspectAfter())));
}
//afterThrowing
if(!(null == config.getAspectAfterThrow() || "".equals(config.getAspectAfterThrow()))) {
//创建一个Advivce
GPAdvice throwingAdvice = new GPAdvice(aspectClass.newInstance(),aspectMethods.get(config.getAspectAfterThrow()));
throwingAdvice.setThrowName(config.getAspectAfterThrowingName());
advices.put("afterThrow",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();
}
public GPClassLoader getClassLoader() { return classLoader; }
}
package com.gupaoedu.springaop.framework.aop;
import com.gupaoedu.springaop.framework.aspect.GPAdvice;
import com.gupaoedu.springaop.framework.proxy.GPClassLoader;
import com.gupaoedu.springaop.framework.proxy.GPInvocationHandler;
import com.gupaoedu.springaop.framework.proxy.GPProxy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
/**
* Created by Tom.
*/
public class GPJdkDynamicAopProxy implements GPInvocationHandler {
private GPAdvisedSupport advised;
public GPJdkDynamicAopProxy(GPAdvisedSupport config){
this.advised = config;
}
public Object getProxy() {
return getProxy(this.advised.getClassLoader());
}
public Object getProxy(GPClassLoader classLoader) {
return GPProxy.newProxyInstance(classLoader,this.advised.getTargetClass().getInterfaces(),this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Map<String,GPAdvice> 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"));
return returnValue;
}
private void invokeAdvice(GPAdvice advice){
try {
advice.getAdviceMethod().invoke(advice.getAspect());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
package com.gupaoedu.springaop.service;
import com.gupaoedu.springaop.demo.service.IMemberService;
import com.gupaoedu.springaop.framework.GPApplicationContext;
public class MemberServiceTest {
public static void main(String[] args) {
GPApplicationContext applicationContext = new GPApplicationContext();
IMemberService memberService = (IMemberService)applicationContext.getBean("memberService");
try {
memberService.delete("1");
} catch (Exception e) {
e.printStackTrace();
}
}
}