自己动手写一个spring之aop_1

写在前面

本文看下如何实现AOP相关内容。

1:为什么需要AOP

工作中有很多非主业务相关,但又必不可少的内容,比如日志打印,权限校验,事务处理,性能监控等。这些准确来说都是属于一些边缘的例行性代码,这些代码固定写死到程序中显然是不合适的,所以我们就需要AOP来动态的帮我们添加这些内容。

2:正文

正常通过getBean方法获取的就是配置的bean,那么想要实现AOP,其实也很简单,只需要在getBean时返回一个代理对象就可以了,具体该怎么做呢?可以这样子,首先定义一个接口,如果配置的bean该接口类型的,则不直接返回其作为最终bean,而是进一步的调用其内部方法获取最终bean,这个最终bean就是代理bean,定义接口如下:

java 复制代码
/**
 * 提供给用户返回自定义对象作为bean的接口,可以认为是一个为bean定制化预留的接口
 * @param <T>
 */
public interface FactoryBean<T> {
    T getObject() throws Exception;

    Class<?> getObjectType();

    default boolean isSingleton() {
        return true;
    }

}

其中方法getObject就是用来获取真正的目标bean,这里我们可以返回一个代理对象实现AOP。给出实现类:

java 复制代码
public class ProxyFactoryBean implements FactoryBean<Object> {
    // ...

    @Override
    public Object getObject() throws Exception {
        return getSingletonInstance();
    }

    private synchronized Object getSingletonInstance() {
        if (this.singletonInstance == null) {
            this.singletonInstance = getProxy(createAopProxy());
        }
        System.out.println("----------返回代理类 :" + this.singletonInstance + "--------" + this.singletonInstance.getClass());

        return this.singletonInstance;
    }

    protected Object getProxy(AopProxy aopProxy) {
        return aopProxy.getProxy();
    }
    
}

为了支持获取不同的代理类,定义接口AopProxy,如下:

java 复制代码
/**
 * 代理类接口,支持返回不同类型的代理类(jdk动态代理,基于asm的代理类等)
 */
public interface AopProxy {
	Object getProxy();
}

实现类:

java 复制代码
package com.hc.minispring.aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
    Object target;

    public JdkDynamicAopProxy(Object target) {
        this.target = target;
    }


    @Override
    public Object getProxy() {
        Object obj = Proxy.newProxyInstance(JdkDynamicAopProxy.class.getClassLoader(), target.getClass().getInterfaces(), this);
        System.out.println("----------创建基于jdk的动态代理类成功: ---------" + obj);
        return obj;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("doAction")) {
            System.out.println("-----通过jdk动态代理,代理了类的doAction方法........");
            return method.invoke(target, args);
        }
        return null;

    }
}

是基于jdk的动态代理。为了方便获取AopProxy创建工厂类:

java 复制代码
/**
 * 代理类创建对象的工厂类,方便对象创建
 */
public interface AopProxyFactory {
	AopProxy createAopProxy(Object target);

}

实现类:

java 复制代码
package com.hc.minispring.aop;

/**
 * 默认代理类工厂,返回基于jdk的动态代理类
 */
public class DefaultAopProxyFactory implements AopProxyFactory {

    @Override
    public AopProxy createAopProxy(Object target) {
        return new JdkDynamicAopProxy(target);
    }
}

因为增加了一种获取bean的方式,即基于factorybean的getObject方法,所以我们需要增加新的bean工厂类来支持这种方式,如下:

java 复制代码
package com.hc.minispring.aop;

import com.hc.minispring.ioc.four_beanfactory_and_event.beans.factory.BeansException;
import com.hc.minispring.ioc.four_beanfactory_and_event.beans.factory.support.DefaultSingletonBeanRegistry;

/**
 * 支持获取工厂bean的类,真正获取bean的逻辑由FactoryBean类的实现类提供(用户动态提供,或使用框架已经给出的默认扩展)
 */
public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry {

    protected Class<?> getTypeForFactoryBean(final FactoryBean<?> factoryBean) {
        return factoryBean.getObjectType();
    }

    protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName) {
        Object object = doGetObjectFromFactoryBean(factory, beanName);
        try {
            object = postProcessObjectFromFactoryBean(object, beanName);
        } catch (BeansException e) {
            e.printStackTrace();
        }
        return object;
    }

    private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName) {

        Object object = null;
        try {
            object = factory.getObject();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return object;
    }

    protected Object postProcessObjectFromFactoryBean(Object object, String beanName) throws BeansException {
        return object;
    }

    protected FactoryBean<?> getFactoryBean(String beanName, Object beanInstance) throws BeansException {
        if (!(beanInstance instanceof FactoryBean)) {
            throw new BeansException(
                    "Bean instance of type [" + beanInstance.getClass() + "] is not a FactoryBean");
        }
        return (FactoryBean<?>) beanInstance;
    }
}

接着修改abstractbeanfactory:

java 复制代码
//public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory, BeanDefinitionRegistry {
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements BeanFactory, BeanDefinitionRegistry {
    // ...

    public Object getBean(String beanName) throws BeansException {
        Object singleton = this.getSingleton(beanName);
        
        // ...
        
        // 支持FacotryBean
        if (singleton instanceof FactoryBean) {
            System.out.println("发现工厂bean ,尝试从工厂bean获取用户自定义bean-------------- " + beanName + "----------------"+singleton);
            return this.getObjectForBeanInstance(singleton, beanName);
        } else {
            System.out.println("普通的bean,直接从ioc容器中拿到的就是:-------------- " + beanName + "----------------"+singleton);
        }
        return singleton;
    }
}

3:测试

定义测试接口:

java 复制代码
public interface IAction {
	void doAction();
}

实现类:

java 复制代码
public class Action1 implements IAction {

    @Override
    public void doAction() {
        System.out.println("really do action");
    }

}

controller:

java 复制代码
package com.hc.minispring.web.v7_aop.controller;

public class HelloController {

    @Autowired
    private IAction action;

    @RequestMapping("/hello1746")

    public String doTest(/*HttpServletRequest request, HttpServletResponse response*/) {

        System.out.println("action: " + action.getClass());

        action.doAction();

        return "index";
    }

}

applicationContext.xml:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="realaction" class="com.hc.minispring.web.v7_aop.Action1" />
    <bean id="action" class="com.hc.minispring.aop.ProxyFactoryBean" >
        <property type="java.lang.Object" name="target" ref="realaction"/>
    </bean>


    <bean id="webBindingInitializer" class="com.hc.minispring.web.v5_databind.DateInitializer"></bean>
    <!-- ... -->
</beans>

运行:

源码

写在后面

参考文章列表

相关推荐
神奇小汤圆1 小时前
IDEA 运行报 Command line is too long?别慌,两招搞定(附原理)
后端
SelectDB2 小时前
Apache Doris 4.1 全面增强 Iceberg:支持 UPDATE、MERGE INTO 与 Iceberg V3
后端
国科安芯2 小时前
FreeRTOS RISC-V 浮点上下文切换移植:在 IAR 工程中完整保存 FPU 寄存器
java·开发语言·单片机·嵌入式硬件·算法·系统架构·risc-v
Conan在掘金2 小时前
ArkTS 进阶之道(13):ForEach 循环渲染边界——为啥 build 里不能写 for 循环
后端
deng12042 小时前
【Thread类的用法(1)-- Thread类的基本用法】
java·java-ee
妙码生花2 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(四十一):增加管理员账号管理接口
后端·go·gin
用户0678260743272 小时前
APP版本管理全链路(后端设计)
后端
suconnect2 小时前
Spring Boot接入企业RAG:文档切分、向量检索、权限过滤和答案溯源
java·spring boot·后端
weixin_BYSJ19873 小时前
springboot校园自习室管理小程序---附源码32142
java·javascript·spring boot·python·django·flask·php