自己动手写一个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>

运行:

源码

写在后面

参考文章列表

相关推荐
mldong2 小时前
Go 开发者也有自己的轻量工作流引擎了:go get 一行,5 分钟跑通一条审批流
后端·go
starzy19902 小时前
Flink基础之Per-Job-Cluster原理详解:被 Application 取代的模式
java·大数据·flink
BingoGo8 小时前
PHP clone 之后,为什么改副本会影响原对象?
后端·php
JaguarJack8 小时前
PHP clone 之后,为什么改副本会影响原对象?
后端·php·服务端
小灰灰搞电子9 小时前
Rust+Slint 实现动态消息提示框源码分享
开发语言·后端·rust
魏 无羡9 小时前
webclient
java·webclient
小奏技术9 小时前
10 MB 的 Postman 替代品,启动不到 1 秒
后端
东风破_9 小时前
Text2SQL :用自然语言操作 SQLite 数据库
人工智能·后端
神仙别闹9 小时前
基于 C++ 实现两个有序链表序列的交集
java·c++·链表
swordbob10 小时前
ReentrantLock 与 AQS 完整学习手册
java·开发语言