aop动态代理问题求解答

我今天跟着教程写了一遍aop的登陆验证,想到一个问题.aop实现动态代理的时候需要代理类实现接口,而controller层并没有实现接口,那应该使用cglib来代理,需要导入第三方依赖才行啊,但是教程里直接使用的aop依赖,这是为什么呢?

AOP(Aspect-Oriented Programming)是一种编程范式,通过将程序分解为各个方面(Aspect),然后再将各个方面独立开来进行编程,最后将它们合并到一起,从而实现代码的重用和降低复杂度的目的。

动态代理是 AOP 中实现切面(Aspect)的一种方式。Java 提供了两种动态代理方式:JDK 动态代理和 CGLIB 代理。其中,JDK 动态代理只能代理实现了接口的类,而 CGLIB 代理则可以代理任何类,包括没有实现接口的类。

在 Spring AOP 中,默认使用 JDK 动态代理来实现切面。如果要使用 CGLIB 代理来代理没有实现接口的类,则需要在配置文件中显式地声明。

在您提到的情况中,Controller 层并没有实现接口,但是 AOP 仍然可以使用 JDK 动态代理来实现切面,原因在于 Spring AOP 会自动为没有实现接口的类创建一个代理类,该代理类会继承目标类并实现 Spring AOP 所需的接口。这种方式称为基于接口的代理(interface-based proxy)。

因此,在您的教程中,虽然 Controller 层没有实现接口,但 Spring AOP 仍然可以使用 JDK 动态代理来实现切面,而不需要使用 CGLIB 代理。

AOP当你切入接口的时候使用的是原生JDK代理,当你切入普通方法的时候使用的是CGLIB代理。Spring里面有自己的AOP机制,在你导入spring相关操作的时候应该就导入的AOP相关依赖。比如

<dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>4.3.7.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>4.3.7.RELEASE</version>
    </dependency>

这两个依赖就是spring的aop依赖

AOP和动态代理的实现方式,

在Java中,AOP的实现方式有两种:基于接口的动态代理和基于类的动态代理(CGLIB)。

基于接口的动态代理要求目标对象必须实现一个接口,代理对象才能实现接口,并代理目标对象的方法。这种代理方式的优点是比较轻量级,代理对象不会产生过多的代理类,缺点是目标对象必须实现接口,对于没有实现接口的类就无法进行代理。

而CGLIB则是在运行时通过生成目标类的子类,实现动态代理。这种方式相对于基于接口的动态代理更加灵活,可以代理没有实现接口的类,但相应的生成代理对象的成本较高。

至于为什么你的教程里使用的是基于接口的动态代理实现AOP而不是CGLIB,可能有以下几个原因:

教程作者在设计时选择了基于接口的动态代理实现方式。

教程中使用的目标类可能已经实现了接口,因此可以使用基于接口的动态代理实现AOP。

教程中的实例较为简单,没有涉及到复杂的代理场景,因此基于接口的动态代理可以满足需求。

AOP可以使用基于接口的动态代理或CGLIB实现,具体选择哪种方式取决于具体场景和需求

  • 这个问题的回答你可以参考下: https://ask.csdn.net/questions/707859
  • 这篇博客也不错, 你可以看下Aop如何实现代理模式
  • 除此之外, 这篇博客: Aop自动代理配置中的 Aop自动代理配置 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • 一个ProxyFactoryBean工厂只能配置一个Target类,如果你想增强多个类的话,那么需要配置多个工厂。当需要配置一两个还可以,如果需要加强的类多了的话,那么需要的工厂就很麻烦,明明加强的方法一样,但是还要写多个Proxy工厂来配置,显的很low。那么这里就出现了自动代理配置,只需要建一个工厂就能对多个类实现加强。

    这里就不做多的介绍了,直接讲解在配置文件中如何配置
    若为普通切面没有切点要求

    <!--配置两个目标类,你要加强的类,因为自动代理可以实现多个类,所以这里假设两个,多个都可以-->
    <bean id="UserDaoImpl" class="com.maoge.demo1.UserDaoImpl" />
    <bean id="UserDaoImpl1"class="com.maoge.demo1.UserDaoImpl1"/>
    
    <!--配置切面,只有通知,没有切点,所以也可以把此看为切面-->
    <bean id="UserDaoImplProxy" class="com.maoge.demo1.UserDaoImplProxy"></bean>
    
    <!--配置工厂类,产生代理对象-->
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    <!-- 这里因为你要配置多个类,所以用list标签,这里没有设置id,因为它为 自动代理,意思就是你申明了这一个工厂的同时,它已经将要增强的方法加进去了-->
        <property name="beanNames" >
        <list>
            <value>UserDaoImpl</value>
            <value>UserDaoImpl1</value>
        </list>
    </property>      
        <!--配置切面-->
        <property name="interceptorNames" value="UserDaoImplProxy"/>
    </bean>

    从上面可以看出,自动代理的原理就是你申明自动代理工厂的同时,它已经将你加强操作放进去了,所以你在后面测试类的时候,申明对象时候,Resource注入它本身的bean就可以了,不用像前面文章中注入你申明的Proxy工厂类

    @Resource(name = "UserDaoImpl")
    private UserDaoImpl userDaoImpl;

    那么该时申明的对象已经是增强的对象
    不像前面文章的,用代理工厂来向上转型

    @Resource(name = "Proxy")
    private UserDaoImpl userDaoImpl;

    上面这个是没有切点的,那么有切点的自然懂了,从前面的文章中
    首先申明通知类,然后切面再将通知和切点进行封装,在自动代理工厂类里面调用切面即可。

    下面为带切点的切面进行自动代理,假设只将save方法进行增强

    <!--以下的两个和不带切点的一样,首先定义两个类-->
    <bean id="UserDaoImpl" class="com.maoge.demo1.UserDaoImpl" />
    <bean id="UserDaoImpl1" class="com.maoge.demo1.UserDaoImpl1"/>
    
    <!--配置通知类-->
    <bean id="UserDaoImplProxy" class="com.maoge.demo1.UserDaoImplProxy"></bean>
    
    <!--配置切面(将通知类+切点进行封装就是该切面)-->
    <bean id="Advisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
        <property name="advice" ref="UserDaoImplProxy"></property>
        <property name="mappedName" value="save"></property>
    </bean>
    
    <!--配置自动代理工厂,和上面一样将要实现加强的target+切面-->
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <property name="beanNames" >        //target
            <list>
                <value>UserDaoImpl</value>
                <value>UserDaoImpl1</value>
            </list>
        </property>       //切面配置
        <property name="interceptorNames" value="Advisor"/>
    </bean>

    如上所示,其实就是将不带切点的切面进行了加强。然后再用自动代理进行引用。