Spring容器中使用接口的实现类来接受增强类对象引发的问题

问题描述:

我使用spring默认的JDK动态代理对IndexServiceImpl进行增强,然后用IndexServiceImpl.class来获取IndexServiceImpl对象,提示我NoSuchBeanDefinitionException。

org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.dalingjia.spring.service.impl.IndexServiceImpl' available

	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:347)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:334)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1107)
	at com.dalingjia.spring.TestClient.test(TestClient.java:28)

代码如下:

配置类@EnableAspectJAutoProxy默认使用的是JDK动态代理

package com.dalingjia.spring.aop;

import org.springframework.context.annotation.*;

/**
 * 当我们需要强制使用CGLIB来实现AOP的时候,需要配置spring.aop.proxy-target-class=true
 * 或@EnableAspectJAutoProxy(proxyTargetClass = true)
 */
@Configuration
@ComponentScan(basePackages = "com.dalingjia.spring")
@EnableAspectJAutoProxy
public class TestConfiguration {

    public TestConfiguration() {
        System.out.println("spring容器启动初始化。。。。。。。。。。。。。。。。。");
    }
}

切面类:

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

/**
 * @author tanhq
 * @Description 定义一个切面
 * @Date 2019/10/28 下午6:18
 * @Version 1.0
 **/
@Component
@Aspect
public class NotVeryUsefulAspect {
    /**
     * 声明切入点,满足execution中的方法调用之后,就会去进行切面操作,类似于触发了切面
     *             第一个*  代表返回任意类型
     *             第一个.. 代表所有的子包
     *             第一个*  代表方法名任意
     *             第二个.. 代表方法的参数是任意数量和类型
     */
    @Pointcut("execution(* com.dalingjia.spring..*(..))") // the pointcut expression
    private void anyOldTransfer() {

    }

    @Before("anyOldTransfer()")
    public void doAccessCheck() {
        System.out.println("-----------------aop----------------");
    }
}

Service接口和实现类:

public interface IndexService {
    void indexService();
}

@Service
public class IndexServiceImpl implements IndexService {
    @Override
    public void indexService() {
        System.out.println("Constructor IndexService");
    }
}

测试类:

public class TestClient {
    /**
     * 1, 解析applicationgContext.xml,将xml中定义的bean(如loginService和loginResource)解析成Spring内部的BeanDefinition,
     *  并以beanName(如loginService)为key,BeanDefinition(如loginService相应的BeanDefinition)为value存储到DefaultListableBeanFactory中的beanDefinitionMap(其实就是一个ConcurrentHashMap)中,
     * 2, 同时将beanName存入beanDefinitionNames(List类型)中,然后遍历beanDefinitionNames中的beanName,进行bean的实例化并填充属性,
     *  在实例化的过程中,如果有依赖没有被实例化将先实例化其依赖,然后实例化本身,实例化完成后将实例存入单例bean的缓存中,
     *  当调用getBean方法时,到单例bean的缓存中查找,如果找到并经过转换后返回这个实例(如LoginResource的实例),之后就可以直接使用了
     */
    @Test
    public void test() {
        //1,加载配置文件,用ClassPathXmlApplicationContext
//        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//        System.out.println("***********************************************");

        //2,加载@Configuration注解,AnnotationConfigApplicationContext
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfiguration.class);

        IndexServiceImpl indexService = ac.getBean(IndexServiceImpl.class);

        indexService.indexService();
    }
}

问题原因分析:

如果只是单纯的注入,是可以用实现类接受注入对象的,但是往往在开发中会对实现类做增强,如事务,日志等,实现增强的AOP技术是通过动态代理实现的,而spring默认是JDK动态代理,对实现类对象做增强得到的增强类和实现类是兄弟关系,所以使用实现类接收增强类会报错,只能使用接口接收。

解决办法:

  1. 将测试类中的代码改成IndexService indexService = ac.getBean(IndexService.class);就不会报错。

  2. 当然,如果非要用实现类接收增强类,我们可以强制使用cglib动态代理,因为cglib动态代理的代理类和原实现类是父子关系,当然可以用父类(原实现类)去接收子类(增强的代理类)。具体做法如下:将配置类的注解改成@EnableAspectJAutoProxy(proxyTargetClass = true),使用cglib代理。

最后:

我们还是使用接口来接收注入的对象,因为接口本来就是来解耦合的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值