1. 程式人生 > 其它 >Spring5 AOP程式設計:關於org.springframework.beans.factory.BeanNotOfRequiredTypeException報錯

Spring5 AOP程式設計:關於org.springframework.beans.factory.BeanNotOfRequiredTypeException報錯

Spring5 AOP程式設計:關於org.springframework.beans.factory.BeanNotOfRequiredTypeException報錯

先上錯誤詳細資訊:

org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'bookDaoImpl' is expected to be of type 'com.atguigu.dao.BookDaoImpl' but was actually of type 'com.sun.proxy.$Proxy19'

直譯過來大概意思是說:這個叫做bookDaoImpl的Bean,應該是BookDaoImpl型別,然而現在是com.sun.proxy.$Proxy19型別

疑惑????檢視對應原始碼:下圖第七行報錯!

    @Test
    public void addTest() { // 在資料庫中,新增 現代光電測試
        // 1. Spring讀取配置檔案
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config.xml");

        // 2. 獲取BookDaoImpl物件  (錯誤行)
        BookDaoImpl bookDaoImpl = context.getBean("bookDaoImpl", BookDaoImpl.class);

        // 3. 測試方法
        bookDaoImpl.add(new Book(18, "現代光電測試", 120, 4));
    }

從這段程式碼上,bookDaoImpl確實應該是BookDaoImpl型別,按理說程式碼沒寫錯,為啥getBean()方法返回的不是BookDaoImpl型別。直覺告訴我,應該是增強類程式碼出現問題,上增強類程式碼:

@Service // 建立物件
@Aspect // 這是一個代理物件
public class BookProxy { // 增強類

    @Before(value = "execution(* com.atguigu.dao.BookDaoImpl.add(..))") // 注意引數 .. 別忘了
    @Order(1)
    public void addBefore() {
        System.out.println("Proxy01:新增方法準備就緒!");
    }

    @Before(value = "execution(* com.atguigu.dao.BookDaoImpl.add(..))")
    @Order(2)
    public void addBefore01() {
        System.out.println("Proxy02:執行!");
    }
}

檢查程式碼之後,發現增強類也沒問題啊............

沒想明白

最後求助百度,發現有人說這個錯誤可能是因為JDK動態代理不支援類注入導致的

檢查被增強類程式碼,發現確實如此,一切的源頭:下圖第六行程式碼,JdbcTemplate已經是一個類物件,使用JDK動態代理無法進行注入類物件的操作。

原增強類程式碼:

@Service(value = "bookDaoImpl")
public class BookDaoImpl implements BookDao{

    // 注入JdbcTemplate
    @Autowired
    private JdbcTemplate jdbcTemplate;  // JDK動態代理並不能進行類的注入!!
    
    .......
}

因為JDK動態代理只支援建立介面實現類代理物件,從而增強類的方法。**

於是,解決方案如下:

新建一個被增強類,命名為BookServiceProxy:其中BookDao是介面,也就是原被增強類BookDaoImpl實現的介面。

解決方案的核心思想:通過一個類封裝介面物件,並在這個類中的各個方法中,呼叫這個介面對應的方法,然後通過增強這個類的各個方法,從而達到 對 實現這個介面的類 的方法 的增強(有點繞,相當於套娃)。

至於為什麼這麼做?是因為JDK動態代理的原理,就是通過建立介面的另一個實現類,被將其作為代理物件,然後在類代理物件中,編寫增強邏輯,從而對原實現類的方法進行增強。

@Service(value = "bookServiceProxy")
public class BookServiceProxy {

    // 注入BookDao
    @Autowired
    private BookDao bookDao;  // 被增強類所實現的BookDao介面,因為是介面型別,能夠進行注入!

    // add
    public void addBook(Book book) { // Book 為 entity
        bookDao.add(book);
    }

    // update
    public void updateBook(int classhours, String name) {
        bookDao.update(classhours, name);
    }

    // delete
    public void deleteBook(String name) {
        bookDao.delete(name);
    }
}

重寫編寫增強類程式碼,對新的被增強類BookServiceProxy進行切面操作。

@Service // 建立物件
@Aspect // 這是一個代理物件
public class BookProxy { // 增強類

    @Before(value = "execution(* com.atguigu.service.BookServiceProxy.addBook(..))") // 注意引數 .. 別忘了
    @Order(1)
    public void addBefore() {
        System.out.println("Proxy01:新增方法準備就緒!");
    }

    @Before(value = "execution(* com.atguigu.service.BookServiceProxy.addBook(..))")
    @Order(2)
    public void addBefore01() {
        System.out.println("Proxy02:執行!");
    }
}

測試程式碼:

    @Test
    public void BookServiceProxyTest() {
        // 1. Spring讀取配置檔案
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config.xml");

        // 2. 獲取BookDaoImpl物件
        BookServiceProxy bookServiceProxy = context.getBean("bookServiceProxy", BookServiceProxy.class);

        // 3. 測試方法
        bookServiceProxy.addBook(new Book(18, "現代光電測試", 120, 4));
    }

執行結果:

Proxy01:新增方法準備就緒!
Proxy02:執行!
10月 15, 2021 9:50:01 下午 com.alibaba.druid.support.logging.JakartaCommonsLoggingImpl info
資訊: {dataSource-1} inited
插入操作執行成功!

Process finished with exit code 0