Spring AOP详解、ProxyFactoryBean介绍、AOP实现方式(代理模式)

Spring AOP 目标

  • 将分散在程序各处的横切关注点剥离出来并以集中的方式进行表达
  • 使得开发人员能够专注于业务逻辑的实现而非繁杂的非功能代码, 简化了程序编写与单元测试
  • 应用场景
    • 日志
    • 安全
    • 事务

AOP 核心概念

  • Advice(通知):定义在连接点处的行为,围绕方法调用而进行注入
  • Pointcut(切点):确定在哪些连接点处应用通知
  • Advisor(通知器):组合Advice与Pointcut

Spring AOP实现

  • ProxyFactoryBean
    • FactoryBean implementation that builds an AOP proxy based on beans in Spring BeanFactory.(From Spring doc)
    • FactoryBean实现,基于Spring BeanFactory中的Bean构建AOP代理
    • Spring AOP的底层实现与源头

ProxyFactoryBean介绍

BeanFactory是spring Ioc的工厂,它里面管理着Spring所创建出来的各种Bean对象,当我们在配置文件(注解)中声明了某个bean的id后,通过 这个id就可以获取到与该id所对应的class对象的实例(可能新建,也可能从缓存中获取)

FactoryBean本质上也是一个Bean, 它同其他Bean一样,也是由BeanFactory所管理和维护的,当然它的实例也会缓存到spring的工厂中(如果是单例) 它与普通的Bean的唯一区别在于,当spring创建一个FactoryBean的实例后,它接下来会判断当前所创建的Bean是否是一个FactoryBean实例,如果 不是,那么就直接将创建出来的Bean返回给客户端;如果是,那么它会对其进行进一步的处理,根据配置文件所配置的target, advisor与interfaces等 信息,在运行期动态构建出一个类,并生成该类的一个实例,最后将该实例返回给客户端;因此,我们在声明一个FactoryBean时,通过id获取到的并非 这个FactoryBean的实例,而是它动态生成出来的一个代理对象(通过三种方式来进行生成:JDK动态代理、CGLIB、ObjenesisCglibAopProxy)

ProxyFactoryBean的典型配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--  即将要被代理的目标类   -->
    <bean id="myService" class="com.kernel.spring.aop.service.impl.MyServiceImpl"/>

    <!--  通知器 : 对目标类里的方法增强(调用目标方法前后做一些额外的逻辑)  -->
    <bean id="myAdvisor" class="com.kernel.spring.aop.advisor.MyAdvisor"/>

    <!--  AOP  ProxyFactoryBean 目标对象  代理对象所实现的接口 通知器-->
    <bean id="myAop" class="org.springframework.aop.framework.ProxyFactoryBean">

        <!--  代理对象所实现的接口   -->
        <property name="proxyInterfaces" value="com.kernel.spring.aop.service.MyService"/>

        <!--  通知器列表   -->
        <property name="interceptorNames">
            <list>
                <value>myAdvisor</value>
            </list>
        </property>

        <!--  目标对象   -->
        <property name="target" ref="myService"/>
    </bean>

</beans>

ProxyFactoryBean的构成

  • target:目标对象,需要对其进行切面增强
  • proxylnterfaces:代理对象所实现的接口
  • interceptorNames:通知器(Advisor)列表,通知器中包含了通知 (Advice) 与切点(Pointcut)

ProxyFactoryBean的作用

针对目标对象来创建代理对象,将对目标对象方法的调用转到对相应代理对象方法的调用,并且可以在代理对象方法调用前后执行与之匹配的 各个通知器中定义好的方法

未使用AOP的情形:直接调用目标对象的特定方法
在这里插入图片描述
使用AOP的情形:目标对象执行前后可以增加自定义逻辑
在这里插入图片描述

目标代理对象的创建

  • Spring通过3种方式来创建目标代理对象
    • JDK动态代理
    • CGLIB
    • ObjenesisCglibAopProxy(Spring4.0引入)

在这里插入图片描述
DefaultAopProxyFactory.java

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
   if (NativeDetector.inNativeImage() || !config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
       return new JdkDynamicAopProxy(config);
   } else {
       Class<?> targetClass = config.getTargetClass();
       if (targetClass == null) {
           throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
       } else {
           return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
       }
   }
}

代理模式详解

设计模式之代理模式https://blog.csdn.net/ZSA222/article/details/122674562

  • 代理模式的作用是:为其他对象提供一种代理以控制对这个对象 的访问。
  • 在某些情况下,一个客户不想或者不能直接引用另一个对象,而 代理对象可以在客户端和目标对象之间起到中介的作用。

JDK动态代理

如果目标对象实现了接口,那么Spring就会通过JDK动态代理为目 标对象生成代理对象

在运行时生成class,在生成它时你必须提供一组interface给它,然后该 class就会声明它实现了这些 interface。 因此我们可以将该class的实例当 作这些interface中的任何一个来用。当然,这个动态代理其实就是一个 Proxy, 它不会替你作实质性的工作,在生成它的实例时你必须提供一个 handler, 由它接管实际的工作

JDK动态代理实现步骤

  1. 创建一个实现接口InvocationHandler的类,它必须实现invoke方 法
  2. 创建被代理的类以及接口
  3. 通过Proxy的静态方法
static Object newProxylnstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
  1. 通过代理调用方法

代码举例

/**
 * @ClassName ITeacher
 * @author: shouanzh
 * @Description ITeacher
 * @date 2022/1/24 16:44
 */
public interface ITeacherDao {

    void teach();

}

/**
 * @ClassName TeacherDao
 * @author: shouanzh
 * @Description TeacherDao 目标对象
 * @date 2022/1/24 16:45
 */
public class TeacherDao implements ITeacherDao{
    @Override
    public void teach() {
        System.out.println("老师正在讲课。。。");
    }
}

/**
 * @ClassName ProxyFactory
 * @author: shouanzh
 * @Description ProxyFactory 代理工厂
 * @date 2022/1/24 16:47
 */
public class ProxyFactory {

    // 维护一个目标对象
    private Object target;

    // 构造器 对 target 初始化
    public ProxyFactory(Object target) {
        this.target = target;
    }

    // 给目标对象生成一个代理对象
    public Object getProxyInstance() {
        // ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法固定
        // Class<?>[] interfaces:目标对象实现的接口类型,使用泛型方法确认类型
        // InvocationHandler h:事情处理,执行目标对象的方法时触发事情处理器方法,把当前执行的目标对象方法作为参数传入
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("JDK代理开始");
                Object invoke = method.invoke(target, args);
                System.out.println("JDK代理结束");
                return invoke;
            }
        });
    }

}

/**
 * @ClassName Client
 * @author: shouanzh
 * @Description Client
 * @date 2022/1/24 17:04
 */
public class Client {

    public static void main(String[] args) {

        // 创建目标对象
        ITeacherDao target = new TeacherDao();
        // 给目标对象创建代理对象
        ProxyFactory proxyFactory = new ProxyFactory(target);
        // 可以转成ITeacherDao
        ITeacherDao teacherDao = (ITeacherDao) proxyFactory.getProxyInstance();

        teacherDao.teach();
    }

}

Spring源码分析:JdkDynamicAopProxy.java

public Object getProxy(@Nullable ClassLoader classLoader) {
    if (logger.isTraceEnabled()) {
        logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
    }

    return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}

在这里插入图片描述

CGLIB代理

如果目标类并未实现接口,那么Spring就会使用CGLIB库为其创建 代理。

CGLIB代码举例


/**
 * @ClassName TeacherDao
 * @author: shouanzh
 * @Description TeacherDao 被代理的对象
 * @date 2022/1/24 20:27
 */
public class TeacherDao {

    public String teach() {
        System.out.println("老师授课中...");
        return "Good";
    }

}

/**
 * @ClassName ProxyFactory
 * @author: shouanzh
 * @Description ProxyFactory 代理工厂
 * @date 2022/1/24 20:27
 */
public class ProxyFactory implements MethodInterceptor {

    /**
     * 目标对象
     */
    private Object target;

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

    /**
     * 返回代理对象
     * @return
     */
    public Object getProxyInstance() {
        // 1、创建工具类
        Enhancer enhancer = new Enhancer();
        // 2、设置父类
        enhancer.setSuperclass(target.getClass());
        // 3、设置回调函数
        enhancer.setCallback(this);
        // 4、创建子类对象,即代理对象
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib代理开始...");
        Object retVal = method.invoke(target, objects);
        System.out.println("cglib代理结束...");
        return retVal;
    }
}

/**
 * @ClassName Client
 * @author: shouanzh
 * @Description Client
 * @date 2022/1/24 20:30
 */
public class Client {

    public static void main(String[] args) {

        // 创建目标对象
        TeacherDao teacherDao = new TeacherDao();
        // 通过代理工厂创建代理对象
        TeacherDao proxyInstance = (TeacherDao) new ProxyFactory(teacherDao).getProxyInstance();
        // 通过代理对象调用目标对象方法 触发intercept 从而实现对目标对象的调用
        String retVal = proxyInstance.teach();
        System.out.println("retVal=" + retVal);

    }

}

小结

  • Spring核心IOC与AOP是整个Spring的基石
  • Spring AOP实现方式有3种
    • 动态代理
    • CGLIB库
    • ObjenesisCglibAopProxy
  • 事务是Spring AOP应用的最佳范例
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值