手写spring(二)AOP实现

前面已经实现了IOC逻辑

手写spring(一)IOC实现

下面实现AOP

1.回顾AOP

AOP(面向切面编程)是一种强大的编程范式,它允许你在不修改源代码的情况下向程序中添加横切关注点(如日志记录、安全检查、事务管理等)。 AOP的用法很简单,只要导入依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>${spring.version}</version>
</dependency>

然后添加配置类

package com.dreams.aspect;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

/**
 * @author PoemsAndDreams
 * @description //
 */
@Aspect
@Component
@EnableAspectJAutoProxy //开启注解版AOP
public class MyAspect {

    @Pointcut("execution(* com.dreams.service.impl.*..*(..))")
    public void pointCut(){}

    @Before("pointCut()")
    public void before(){
        System.out.println("方法执行前!");
    }

    @After("pointCut()")
    public void after(){
        System.out.println("方法执行后!");
    }

}

运行个方法,自动在所有方法前或后加上各类操作。

 

2.AOP注解

在AOP中,@Aspect 注解来标记它为一个切面。

在前面的IOC逻辑代码的基础上接着开发,首先新建上面需要的注解

参考源码新建

注解Aspect

package com.dreams.springframework.aspectj.annotation;

import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 *  @author PoemsAndDreams
 *  //Aspect declaration
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Aspect {

    String value() default "";
}

 

Before注解

package com.dreams.springframework.aspectj.annotation;

import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * @author PoemsAndDreams
 * Before advice
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Before {


    String value();

    String argNames() default "";

}

 

After注解

package com.dreams.springframework.aspectj.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author PoemsAndDreams
 * @description //
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface After {
    String value();
    String argNames() default "";
}

 

Pointcut注解

package com.dreams.springframework.aspectj.annotation;

import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * @author PoemsAndDreams
 * Pointcut declaration
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Pointcut {

    String value() default "";

    String argNames() default "";
}

 

 

3.加载@Aspect注解的类

在工厂类DefaultListableBeanFactory中添加属性来存储切面数据,key为@Pointcut 注解的值,value为切面类的class。

ConcurrentHashMap<String, Class<?>> advisorsCacheMap = new ConcurrentHashMap<>();

@Override
public ConcurrentHashMap<String, Class<?>> getAdvisorsCacheMap() {
    return advisorsCacheMap;
}

 

在XmlBeanDefinitionReader中的loadAllClass中加入以下代码,判断是否是加了Aspect注解,如果加了,他就是一个切面类,获取 @Pointcut 注解的值,并使用它作为键,将类存储在 advisorsCacheMap 中。

//判断是否是切面类
if (clazz.isAnnotationPresent(Aspect.class) && clazz.isAnnotationPresent(Component.class)){
    //获取 @Pointcut 注解的值,并使用它作为键,将类存储在 advisorsCacheMap 中。
    for (Method method : clazz.getDeclaredMethods()) {
        if (method.isAnnotationPresent(Pointcut.class)) {
            Pointcut pointcut = method.getAnnotation(Pointcut.class);
            String pointcutExpression = pointcut.value();

            if (!advisorsCacheMap.containsKey(pointcutExpression)) {
                advisorsCacheMap.put(pointcutExpression, clazz);
            }
        }
    }
    continue;
}

 

改造后的loadAllClass类,代码如下:

//加载
private void loadAllClass(ConfigurableListableBeanFactory beanFactory, File path) {
    ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = beanFactory.getBeanDefinitionMap();
    ConcurrentHashMap<String, Class<?>> advisorsCacheMap = beanFactory.getAdvisorsCacheMap();
    File[] files = path.listFiles();
    for (File file : files) {
        //如果是个目录
        if (!file.isDirectory()){
            //获取文件路劲
            String fileName = file.getAbsolutePath();
            if (fileName.endsWith(".class")){
                String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
                className = className.replace("\\",".");
                //获取类加载器
                ClassLoader classLoader = XmlBeanDefinitionReader.class.getClassLoader();
                try {
                    //加载类
                    Class<?> clazz = classLoader.loadClass(className);
                    
                    //判断是否是切面类
                    if (clazz.isAnnotationPresent(Aspect.class) && clazz.isAnnotationPresent(Component.class)){
                        //获取 @Pointcut 注解的值,并使用它作为键,将类存储在 advisorsCacheMap 中。
                        for (Method method : clazz.getDeclaredMethods()) {
                            if (method.isAnnotationPresent(Pointcut.class)) {
                                Pointcut pointcut = method.getAnnotation(Pointcut.class);
                                String pointcutExpression = pointcut.value();

                                if (!advisorsCacheMap.containsKey(pointcutExpression)) {
                                    advisorsCacheMap.put(pointcutExpression, clazz);
                                }
                            }
                        }
                        continue;
                    }

                    //类是否有Component,Service,Controller,Repository注解
                    if (clazz.isAnnotationPresent(Component.class) || clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(Service.class) || clazz.isAnnotationPresent(Repository.class)){

                        Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
                        Controller controllerAnnotation = clazz.getDeclaredAnnotation(Controller.class);
                        Service serviceAnnotation = clazz.getDeclaredAnnotation(Service.class);
                        Repository repositoryAnnotation = clazz.getDeclaredAnnotation(Repository.class);

                        String value = "";
                        //Bean定义类
                        BeanDefinition beanDefinition = new BeanDefinition();
                        //判断注解是否有value值
                        if (componentAnnotation != null || controllerAnnotation != null || serviceAnnotation != null || repositoryAnnotation != null){
                            if (componentAnnotation != null && !componentAnnotation.value() .equals("")){
                                value = componentAnnotation.value();
                            }else if (controllerAnnotation != null && !controllerAnnotation.value().equals("")){
                                value = controllerAnnotation.value();
                            }else if (serviceAnnotation != null && !serviceAnnotation.value().equals("")){
                                value = serviceAnnotation.value();
                            } else if (repositoryAnnotation != null && !repositoryAnnotation.value().equals("")) {
                                value = repositoryAnnotation.value();
                            } else {
                                String name = clazz.getSimpleName();
                                //默认以开头小写的类名作为实例名
                                value = name.valueOf(name.charAt(0)).toLowerCase() + name.substring(1);

                            }
                            //不能重名
                            if (beanDefinitionMap.get(value) != null) {
                                throw new RuntimeException("spring IOC Container is already exists " + beanDefinitionMap.get(value));
                            }
                            
                            beanDefinition.setClazz(clazz);
                            beanDefinition.setBeanName(value);
                            //保存到bean定义
                            beanDefinitionMap.put(value,beanDefinition);

                            //获取到该类实现的所有接口
                            Class[] interfaces = clazz.getInterfaces();
                            //在beanDefinitionMap中存储为一个接口对应一个实现类
                            for (Class anInterface : interfaces) {
                                String interfaceSimpleName = anInterface.getSimpleName();
                                interfaceSimpleName = interfaceSimpleName.valueOf(interfaceSimpleName.charAt(0)).toLowerCase() + interfaceSimpleName.substring(1);
                                beanDefinitionMap.put(interfaceSimpleName,beanDefinition);
                            }
                        }

                    }

                } catch (ClassNotFoundException e) {
                    throw new RuntimeException(e);
                }

            }
        }
        else {
            loadAllClass(beanFactory, file);
        }
    }
}

 

 

4.处理切面

最后就是处理切面,因为JDK代理要求目标类要实现接口,所以我使用CGLIB代理方便,先加入依赖:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>

同样在DefaultListableBeanFactory加入处理切面的方法。

我们从advisorsCacheMap中拿到需要AOP的包,然后遍历InstanceMap,将里面的实例类改成代理的类,这个代理的类我们在MethodInterceptor中对其处理,获取切面类加了before注解的方法在目标类前执行,after在目标类后执行,更新InstanceMap,将里面的实例类更新为代理的类。

代码如下:

//处理切面
private void resolveInstantiationAspect() {

    for (Map.Entry<String, Class<?>> entry : advisorsCacheMap.entrySet()) {
        String packagePattern = entry.getKey();
        Class<?> aspectClass = entry.getValue();

        for (Map.Entry<String, Object> beanEntry : InstanceMap.entrySet()) {
            Object bean = beanEntry.getValue();
            String beanName = beanEntry.getKey();

            if (bean.getClass().getPackage().getName().matches(packagePattern.replace("*", ".*"))) {
                System.out.println("Applying aspect to bean: " + beanName);
                // 创建 Enhancer 对象,用于生成代理类
                Enhancer enhancer = new Enhancer();
                // 设置目标类的父类,即被代理的类
                enhancer.setSuperclass(bean.getClass());
                // 设置回调,即拦截器
                enhancer.setCallback(new CustomInterceptor(bean, aspectClass));
                // 生成代理对象
                Object proxy = enhancer.create();
                InstanceMap.put(beanName, proxy);
            }
        }
    }
}

// MethodInterceptor 实现类,用于处理方法调用
class CustomInterceptor implements MethodInterceptor {
    private final Object target;
    private final  Class<?> aspectClass;

    public CustomInterceptor(Object target,  Class<?> aspectClass) {
        this.target = target;
        this.aspectClass = aspectClass;
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        Method beforeMethod = null;
        Method afterMethod = null;
        Method[] methods = aspectClass.getMethods();

        for (Method m : methods) {
            if (m.isAnnotationPresent(Before.class)) {
                beforeMethod = m;
            } else if (m.isAnnotationPresent(After.class)) {
                afterMethod = m;
            }
        }

        Object aspectInstance = null;
        try {
            aspectInstance = aspectClass.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        beforeMethod.invoke(aspectInstance);
        // 调用目标对象的方法
        Object result = proxy.invokeSuper(obj, args);
        afterMethod.invoke(aspectInstance);
        return result;
    }
}

然后调用就行

this.resolveInstantiationAspect();

同样在该类下,在实例化保存至InstanceMap后,在自动注入逻辑前

 

 

5.测试

在测试模块

添加切面类

package com.dreams.aspect;

import com.dreams.springframework.aspectj.annotation.After;
import com.dreams.springframework.aspectj.annotation.Aspect;
import com.dreams.springframework.aspectj.annotation.Before;
import com.dreams.springframework.aspectj.annotation.Pointcut;
import com.dreams.springframework.stereotype.Component;

/**
 * @author PoemsAndDreams
 * @description //日志
 */
@Aspect
@Component
public class MyAspect {

    @Pointcut("com.dreams.service.impl")
    public void pointCut(){}

    @Before("pointCut()")
    public void before(){
        System.out.println("方法执行前!");
    }

    @After("pointCut()")
    public void after(){
        System.out.println("方法执行后!");
    }

}

注意这是我们的方法

运行测试

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇