AOP代理混用遇到的坑

947 阅读1分钟

一、源码:

1、目标类接口:
package localhost.anno;

public interface TargetInterface {
    public void save();
}
2、目标类实现
package localhost.anno;

import org.springframework.stereotype.Component;

@Component("annoTarget")
public class Target implements TargetInterface {
    @Override
    public void save() {
        System.out.println("save running ……");
    }
}
3、切面类
package localhost.anno;

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

//在spring容器中声明一个bean对象
@Component("myAspect")
//声明当前类是一个切面类(增强方法)
@Aspect
public class MyAspect {

    //声明前置通知:切点表达式
    @Before("execution(* localhost.anno.*.*(..))")
    public void before() {
        System.out.println("前置增强……");
    }

    //声明后置通知,已经引用定义的切点表达式
    @AfterReturning("MyAspect.pointcut()")
    public void afterReturning() {
        System.out.println("后置增强方法…………");
    }


    //定义切点表达式:将各个通知类型的切点表达式抽取统一定义
    @Pointcut("execution(* localhost.anno.*.*(..))")
    public void pointcut() {
    }
}
4、ApplicationContext.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
">

    <!--    开启组件扫描-->
    <context:component-scan base-package="localhost.anno"/>

    <!--    aop自动代理-->
    <!--    其中proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强;如目标类没有声明接口,spring则自动使用Cglib动态代理-->
    <!--    当为true时,表示使用Cglib动态代理技术织入增强-->
    <aop:aspectj-autoproxy proxy-target-class="false"/>
</beans>
5、测试类
package test.annoconf;


import localhost.anno.Target;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;

//声明springjunit测试类
@RunWith(SpringJUnit4ClassRunner.class)
//声明配置文件
@ContextConfiguration("classpath:applicationContext-anno.xml")
public class TestAnnoConf {


    @Resource(name = "annoTarget")
    //此处需要使用接口类类型来声明成员变量,才会成功使用jdk的动态代理,用目标类类型来声明则需要声明强制使用cglib动态代理
    private Target targetInterface;

    @Test
    public void aTest() {
        if (targetInterface != null) {
            targetInterface.save();
        }
    }
}
6、测试结果
 Error creating bean with name 'test.annoconf.TestAnnoConf': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'annoTarget' is expected to be of type 'localhost.anno.Target' but was actually of type 'com.sun.proxy.$Proxy23'
7、原因:
由于在TestAnnoConf测试类中成员变量targetInterface声明的类型为Target目标对象类型,
且未在配置文件中声明前置使用CgLib动态代理,导致注入的数据类型错误
因为使用jdk的动态代理,就需要注入一个接口类型的变量
8、解决办法:
  • 1、将测试类中成员变量targetInterface的数据类型更新为该目标类实现的接口类型
private TargetInterface targetInterface;
  • 2、在配置文件中声明前置使用CgLib动态代理
<!--    aop自动代理-->
    <!--    其中proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强;如目标类没有声明接口,spring则自动使用Cglib动态代理-->
    <!--    当为true时,表示使用Cglib动态代理技术织入增强-->
    <aop:aspectj-autoproxy proxy-target-class="true"/>