SpringAop 类代理模式与接口代理模式

本文详细探讨了Spring AOP在接口代理和类代理模式下的工作原理。通过实例展示了当Bean实现接口时,Spring默认使用JDK的接口代理,返回接口的代理对象。而当设置`proxyTargetClass`为`true`时,Spring会切换到使用CGLIB进行类代理,返回目标类的代理对象。实验结果证实了这一理论。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前两天在学习Spring的切面编程,利用@EnableAspectJAutoProxy标签开启切面。却始终得不到想要的结果。不多说,直接上demo代码。

spring配置类

package com.xwh.cofig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.env.Environment;

import com.xwh.Interface.CompactDisc;
import com.xwh.aspect.EncoreableIntroducer;
import com.xwh.aspect.PlayerAspect;
import com.xwh.bean.CDPlayer;
import com.xwh.bean.MyBean;
import com.xwh.bean.StgPepper;
@Configuration
@EnableAspectJAutoProxy //启动切面代理
public class MainConfig {
    @Bean(name="cdPlayer")
    public CDPlayer cdPlayer(CompactDisc disc) {
        return new CDPlayer(disc);
    }
    @Autowired
    private Environment environment;
    @Bean
    public MyBean myBean(){
        MyBean bean=new MyBean();
        bean.setMessage(environment.getProperty("user.language"));
        return bean;
    }
    @Bean
    public CompactDisc stgPepper() {            
        return new StgPepper();
    }
    @Bean
    public PlayerAspect playerAspect(){
        return new PlayerAspect();
    }
    @Bean
    public EncoreableIntroducer encoreableIntroducer(){
        return new EncoreableIntroducer();

    }
}

接口CompactDisc

package com.xwh.Interface;
public interface CompactDisc {
    void  play();
}

接口MediaPlayer

package com.xwh.Interface;
public interface MediaPlayer {
    void play();
}

CompactDisc 实现类

package com.xwh.bean;

import com.xwh.Interface.CompactDisc;

public class StgPepper implements CompactDisc {

    private String title ="Stg.Peppers Loney Hearts Club Band";
    private String artist = "The Beatles";

    public void play() {
         System.out.println("playing " + title + " by " + artist);
    }

}

MediaPlayer 实现类

package com.xwh.bean;
import com.xwh.Interface.CompactDisc;
import com.xwh.Interface.MediaPlayer;    

public class CDPlayer implements MediaPlayer {  
    private CompactDisc disc;   
    public CDPlayer() {
    }
    public CDPlayer(CompactDisc disc) {
        this.disc =disc;        
    }   
    public void play() {
        disc.play();
    }    
    public void play(String title,String artist) {
       System.out.println("playing " + title + " by" + artist);
    }    
}

MyBean实体类

package com.xwh.bean;

public class MyBean {
    private String message;

    public MyBean() {
    }
    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

横切面定义

package com.xwh.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class PlayerAspect {
    @Pointcut("execution(* com.xwh.bean.CDPlayer.play(..))")
    public void perform(){};
    @Before("perform() and args(title,artist)")    //方法调用前,或者方法参数
    public void beforePlaying(JoinPoint joinPoint,String title,String artist){
        System.out.println("start playing..."+ title + "  by " +artist);
    }
}

测试类

package spting.test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.xwh.Interface.MediaController;
import com.xwh.aspect.EncoreableIntroducer;
import com.xwh.aspect.PlayerAspect;
import com.xwh.bean.CDPlayer;
import com.xwh.cofig.MainConfig;

public class MainTest {
    @Test
    public void testCase() {
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
        CDPlayer player_1 = context.getBean(CDPlayer.class);        
        player_1.play();
    }
}

测试结果:
这里写图片描述
抛出异常: No qualifying bean of type [com.xwh.bean.CDPlayer] is defined。
原因分析:
我们将@Pointcut(“execution(* com.xwh.bean.CDPlayer.play(..))”)横切点改成。切面如下:

@Pointcut("execution(* com.xwh.bean.MyBean.*(..))")
    public void perform(){};
    @Before("perform()")    //方法调用前,或者方法参数
    public void beforePlaying(JoinPoint joinPoint){
        System.out.println("start invoke bean method...");
    }

结果:这里写图片描述
切入成功!代理的Bean 如下:
这里写图片描述
返回的是MyBean类,这是类代理。
为啥@Pointcut(“execution(* com.xwh.bean.CDPlayer.play(..))”)不能切入成功呢?经过多次尝试,发现是由于CDPlayer是实现了MediaPlayer接口的缘故,此时Spring默认是采用jdk的接口代理模式,代理返回的代理是一个接口。为了验证这个猜想,我们仍然采用第一个切入点,将测试类做以下修改:

MediaPlayer player_1 = context.getBean(MediaPlayer.class);  
player_1.play();

测试结果:
这里写图片描述
切入成功,再来看看代理返回的代理类:
这里写图片描述
返回的jdkDynamicAopProxy,验证了我们的猜想。
那么我们要想用类代理来,即返回的是CDPlayer的代理该如何处理,spring aop在我们代理的是接口的实现是默认采用的jdk的接口动态代理,我们可以对这个默认设置修改,如下:

@EnableAspectJAutoProxy(proxyTargetClass=true)

proxyTargetClass属性设置为true即可,我们验证一下:

测试类修改为:

CDPlayer player_1 = context.getBean(CDPlayer.class);        
player_1.play();

测试结果:
这里写图片描述
切入成功,返回的代理类为:
这里写图片描述
类代理,采用的Cglib动态库代理。

结论:MyBean未实现任何接口,spring采用CGLIBl类代理模式,当bean实现了接口,spring默认采用jdk的接口代理,当设置proxyTargetClass=true时,spring采用cglib类代理。
Demo采用spring的jar包如下:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值