Spring IOC、AOP可谓是spring中两大王牌
上次我们手写了IOC,那么今天我们来手写一下AOP
感谢这篇博主的手写AOP博文(https://blog.csdn.net/qq_35704236/article/details/79997182)
我是学习他的写法,然后将一些我不懂的代码进行了注释解释,应该可以更加适合小白学习吧。
首先我们都知道Spring AOP的核心技术是动态代理,但是在这个demo中用的不是Jdj1.8的动态代理方法。
用的是cgib的动态代理方式。
那么这二者有什么区别呢?
JDK动态代理: 只能代理实现了接口的类
没有实现接口的类不能实现JDK动态代理。
Cglib代理: 针对类来实现代理,对指定目标
产生一个子类 通过方法拦截技术拦截所有父类方法的调用。
1 扫描 aop 包, 获取 aspect 的类
2 根据 切点 获取该切点的 类 和 方法
3 根据配置的 类 和 方法 为该类生成一个代理对象
4 将改代理对象放入 bean Map 中
5 调用的时候 将代理对象 转换成需要的对象
注解:
package com.example.writeaopdemo.annotion;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** * @author Jet */
@Retention(RetentionPolicy.RUNTIME) //保留时间长短
@Target(value = {ElementType.TYPE})//使用范围、接口、类、枚举、注解
public @interface Aspect {
}
package com.example.writeaopdemo.annotion;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** * 方法切入点 */
@Retention(RetentionPolicy.RUNTIME)//保留时间长短
@Target(value = {ElementType.METHOD}) //使用范围 方法
public @interface PointCut {
/** * 全类名_方法名 * @return */
String value();
}
测试bean测试Aspect
package com.example.writeaopdemo.domain;
import com.example.writeaopdemo.annotion.Aspect;
import com.example.writeaopdemo.annotion.PointCut;
import com.example.writeaopdemo.proxy.AbsMethodAdvance;
@Aspect
public class TestAspect extends AbsMethodAdvance {
/** *全类名 方法名 */
@PointCut("com.example.writeaopdemo.domain.Test_doSomeThing")
public void testAspect(){
}
@Override
public void doBefore() {
System.out.println("do before");
}
@Override
public void doAfter() {
System.out.println("do after");
}
}
代理类(在创建代理对象的同时拦截方法的执行实现before和after逻辑)
package com.example.writeaopdemo.proxy;
import com.example.writeaopdemo.util.StringUtils;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/** * @author Jet */
public abstract class AbsMethodAdvance implements MethodInterceptor {
/** * 要被代理的对象 */
private Object targetObject;
/** * 被代理的方法名 */
private String proxyMethodName;
public Object createProxyObject(Object target){
this.targetObject = target;
//该类用于生成代理对象
Enhancer enhancer = new Enhancer();
//设置目标类为代理对象的父类
enhancer.setSuperclass(this.targetObject.getClass());
//设置回调用对象为本身
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable{
Object result;
String proxyMethod = getProxyMethodName();
if(StringUtils.isNotBlank(proxyMethod) && proxyMethod.equals(method.getName())){
doBefore();
}
//执行拦截的方法
result = methodProxy.invokeSuper(proxy,args);
if(StringUtils.isNotBlank(proxyMethod) && proxyMethod.equals(method.getName())){
doAfter();
}
return result;
}
public abstract void doBefore();
public abstract void doAfter();
public String getProxyMethodName(){
return proxyMethodName;
}
public void setProxyMethodName(String proxyMethodName){
this.proxyMethodName = proxyMethodName;
}
}
加载类
package com.example.writeaopdemo.util;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/** * @author Jet */
public class ClassUtil {
/** * 获取类加载器 * * @return */
public static ClassLoader getClassLoader() {
return Thread.currentThread().getContextClassLoader();
}
/** * 加载类 * 需要提供类名是否初始化标志 * 初始化是指知否执行静态代码块 * * @param className * @param isInitialized * @return */
public static Class> loadClass(String className, boolean isInitialized) {
Class> cls;
try {
cls = Class.forName(className, isInitialized, getClassLoader());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
return cls;
}
/** * 加载类(默认将初始化类) * * @param className * @return */
public static Class> loadClass(String className) {
return loadClass(className, true);
}
public static Set> getClassSet(String packageName) throws IOException {
Set> classSet = new HashSet<>();
try {
Enumeration urls = getClassLoader().getResources(packageName.replace(".", "/"));
//Enumeration相当于老板迭代器
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
if (url != null) {
String protocol = url.getProtocol();//获得URL的协议
if (protocol.equals("file")) {
//转码
String packagePath = URLDecoder.decode(url.getFile(), "UTF-8");//转码为utf-8的格式
addClass(classSet, packagePath, packageName);
} else if (protocol.equals("jar")) {
JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();//解析Jar文件
if (jarURLConnection != null) {
JarFile jarFile = jarURLConnection.getJarFile();
if (jarFile != null) {
Enumeration jarEntries = jarFile.entries();
while (jarEntries.hasMoreElements()) {
JarEntry jarEntry = jarEntries.nextElement();
String jarEntryName = jarEntry.getName();
if (jarEntryName.endsWith(".class")) {
String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", ".");
doAddClass(classSet, className);
}
}
}
}
}
}
}
} catch (IOException e) {
throw e;
}
return classSet;
}
private static void addClass(Set> classSet,String packagePath,String packageName){
// System.out.println("packageName: " + packageName);
File[] files = new File(packagePath).listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return (file.isFile() && file.getName().endsWith(".class") || file.isDirectory());
}
});
for(File file : files){
String fileName = file.getName();
if(file.isFile()){
String className = fileName.substring(0,fileName.lastIndexOf("."));
if(StringUtils.isNotBlank(packageName)){
className = packageName + "." + className;
}
//添加
doAddClass(classSet,className);
}else{
//子目录
String subPackagePath = fileName;
if(StringUtils.isNotBlank(packagePath)){
subPackagePath = subPackagePath + "/" + subPackagePath;
}
String subPackageName = fileName;
if(StringUtils.isNotBlank(packageName)){
subPackageName = packageName + "." + subPackageName;
}
addClass(classSet,subPackagePath,subPackageName);
}
}
}
private static void doAddClass(Set> classSet,String className){
Class> cls = loadClass(className,false);
classSet.add(cls);
}
}
通过反射机制创建实例调用方法和设置成员变量的值
package com.example.writeaopdemo.util;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionUtil {
/** * 创建实例 */
public static Object newInstance(Class> cls){
Object instance;
try{
instance = cls.newInstance();
}catch (Exception e){
throw new RuntimeException();
}
return instance;
}
/** * 创建实例 根据类名 */
public static Object newInstance(String className){
Class> cls = ClassUtil.loadClass(className);
return newInstance(cls);
}
/** * 调用方法 */
public static Object invokeMethod(Object obj, Method method,Object... args){
Object result;
try{
method.setAccessible(true);
result = method.invoke(obj,args);
}catch (Exception e){
throw new RuntimeException(e);
}
return result;
}
/** * 设置成员变量的值 */
public static void setField(Object obj, Field field,Object value){
try{
field.setAccessible(true);
field.set(obj,value);
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
字符串处理类
package com.example.writeaopdemo.util;
/** * @author Jet */
public class StringUtils {
public static boolean isNotBlank(String str){
if(str != null && str.trim().length() > 0){
return true;
}else{
return false;
}
}
public static boolean isBlank(String str){
return !isNotBlank(str);
}
}
找到切点切面设置代理初始化容器类
package com.example.writeaopdemo;
import com.example.writeaopdemo.annotion.Aspect;
import com.example.writeaopdemo.annotion.PointCut;
import com.example.writeaopdemo.proxy.AbsMethodAdvance;
import com.example.writeaopdemo.util.ClassUtil;
import com.example.writeaopdemo.util.ReflectionUtil;
import org.apache.tools.ant.taskdefs.EchoXML;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/** * @author Jet */
public class ApplicationContext {
/** * 存放代理类的集合 */
public static ConcurrentHashMap proxyBeanMap = new ConcurrentHashMap();
static {
initAopBeanMap("com.example.writeaopdemo.domain");
}
/** * 初始化容器 * @param basePath */
public static void initAopBeanMap(String basePath){
try{
Set> classSet = ClassUtil.getClassSet(basePath);
for(Class clazz : classSet){
if(clazz.isAnnotationPresent(Aspect.class)){
Method[] methods = clazz.getMethods();
for(Method method : methods){
if(method.isAnnotationPresent(PointCut.class)){
//找到切点
PointCut pointCut = (PointCut)method.getAnnotations()[0];
String pointCutStr = pointCut.value();
//System.out.println("pointCutStr:" + pointCutStr);
String[] pointCutArr = pointCutStr.split("_");
//被代理的类名
String className = pointCutArr[0];
//System.out.println("className:" + className);
//被代理的方法名
String methodName = pointCutArr[1];
// System.out.println("methodName:" + methodName);
//根据切点 创建被代理对象
Object targeObj = ReflectionUtil.newInstance(className);
//根据切面类创建代理者
AbsMethodAdvance proxyer = (AbsMethodAdvance)ReflectionUtil.newInstance(clazz);
//设置代理的方法
proxyer.setProxyMethodName(methodName);
Object object = proxyer.createProxyObject(targeObj);
if(object != null){
proxyBeanMap.put(targeObj.getClass().getSimpleName().toLowerCase(),object);
}
}
}
}
}
}catch (Exception e){
e.printStackTrace();
}
}
}
Main类运行
import com.example.writeaopdemo.ApplicationContext;
import com.example.writeaopdemo.domain.Test;
import java.util.concurrent.ConcurrentHashMap;
public class Main {
public static void main(String[] args){
//模拟容器初始化
ApplicationContext applicationContext = new ApplicationContext();
ConcurrentHashMap proxyBeanMap = ApplicationContext.proxyBeanMap;
//生成代理对象,默认为该类名的小写
Test test =(Test)proxyBeanMap.get("test");
test.doSomeThing();
System.out.println("------------");
test.doWithNotProxy();
}
}
全部代码地址:https://github.com/jet0605/writeAop