前两天在学习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包如下: