2011-01-16 8 views
29

Tôi đang chơi với ý tưởng sử dụng Spring @Configurable@Autowire để tiêm DAO vào đối tượng miền để chúng không cần kiến ​​thức trực tiếp về lớp kiên trì.Tự động chạy mùa xuân bằng cách sử dụng @Configurable

Tôi đang cố gắng theo dõi http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-atconfigurable, nhưng mã của tôi dường như không có hiệu lực.

Về cơ bản, tôi có:

@Configurable 
public class Artist { 

    @Autowired 
    private ArtistDAO artistDao; 

    public void setArtistDao(ArtistDAO artistDao) { 
     this.artistDao = artistDao; 
    } 

    public void save() { 
     artistDao.save(this); 
    } 

} 

Và:

public interface ArtistDAO { 

    public void save(Artist artist); 

} 

@Component 
public class ArtistDAOImpl implements ArtistDAO { 

    @Override 
    public void save(Artist artist) { 
     System.out.println("saving"); 
    } 

} 

Trong ứng dụng context.xml, tôi có:

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springsource.org/dtd/spring-beans-2.0.dtd"> 
<beans> 

    <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" /> 
    <bean class="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect" factory-method="aspectOf"/> 

</beans> 

Quét đường dẫn lớp và khởi tạo được thực hiện bởi mô đun lò xo cho Play! khuôn khổ, mặc dù đậu autowired khác làm việc, vì vậy tôi khá chắc chắn đây không phải là nguyên nhân gốc rễ. Tôi đang sử dụng Spring 3.0.5.

Trong mã khác (bên trong một phương pháp trong đậu đó là tiêm vào bộ điều khiển của tôi sử dụng Spring, trên thực tế), tôi đang làm điều này:

Artist artist = new Artist(); 
artist.save(); 

này mang lại cho tôi một NullPointerException cố gắng để truy cập artistDao trong Artist .tiết kiệm().

Bất kỳ ý tưởng nào tôi đang làm sai?

Martin

+0

liên quan;) http://techblog.bozho.net/?p=180 – Bozho

+0

Bạn có thể sử dụng chú thích '@ Value' trong lớp' @ Configurable' được đánh dấu là Artist không? – NightWolf

Trả lời

1

Có lẽ sử dụng @Repository chú thích cho DAO sẽ làm điều đó.

+0

Tôi đồng ý. Anh ta nên sử dụng @Repository thay vì – chris

+2

Làm thế nào nó có thể liên quan đến '@ Configurable'? – axtavt

+5

Bạn có thể giải thích tại sao điều đó sẽ tạo nên sự khác biệt? – Paul

7

Bạn cần bật thời gian tải dệt (hoặc các loại dệt khác) để sử dụng @Configurable. Đảm bảo bạn đã bật chính xác, như được mô tả trong 7.8.4 Load-time weaving with AspectJ in the Spring Framework.

+1

Điều này là sai. Bạn không cần LTW để hỗ trợ @Configurable. Spring Roo làm những gì anh ta muốn và nó không sử dụng LTW. Rất có thể vấn đề của anh ta là anh ta không biên dịch mã với trình biên dịch AspectJ và jar Spring Aspects. –

+4

@AdamGent Ông đã nói LTW (hoặc các loại dệt khác). . . Bạn cần thời gian tải hoặc thời gian biên dịch dệt cho chú thích có thể định cấu hình. –

+0

@ JasperBlues bạn nói đúng rằng LTW sẽ hoạt động. Tôi không bao giờ có thể làm cho nó hoạt động trong môi trường của tôi và nghĩ rằng tôi đọc trong * AspectJ trong hành động * mà lời khuyên xây dựng là không thể với LTW. –

4

Trước tiên, bật ghi nhật ký gỡ lỗi mùa xuân. Tôi sử dụng Log4j để làm điều đó. Tôi đã tạo một trình ghi nhật ký như vậy (với cấu hình Log4j xml để tôi có thể sử dụng RollingFileAppender):

<log4j:configuration> 
    <appender name="roll" class="org.apache.log4j.rolling.RollingFileAppender"> 
    blah blah configuration blah blah 
    </appender> 
    <logger name="org.springframework"> 
    <level value="debug" /> 
    <appender-ref ref="roll" /> 
    </logger> 
</log4j:configuration> 

Điều này sẽ cho phép bạn xem Spring đang làm gì và khi nào.

Thứ hai, bạn có ArtistDAO tự động nhưng tôi không thấy nơi bạn có một bean tên là ArtistDAO. Hạt thành phần DAO của bạn sẽ được đặt tên là "artistDaoImpl" theo mặc định. Hãy thử thay đổi @Component để @Component("artistDao") và áp dụng @Autowired để setter thay vì:

private ArtistDAO artistDao; 

@Autowired 
public void setArtistDao(ArtistDAO artistDao) 
{ 
    this.artistDao = artistDao; 
} 
3

Bạn chỉ nên xem xét như thế nào Spring Roo làm nó vì nó thực hiện chính xác những gì bạn muốn làm.

Có rất nhiều điều mà có thể gây ra NPE của bạn có nhưng phần lớn thời gian nó đã làm với không biên dịch đúng với AspectJ biên dịch và không có khía cạnh mùa xuân jar trong con đường lib AspectJ bạn (điều này khác với classpath của bạn).

Trước tiên, hãy thử làm cho nó hoạt động với Maven và trình biên dịch trình biên dịch AspectJ. Đó là lý do tại sao tôi đề nghị Spring Roo vì nó sẽ tạo ra một tệp POM với thiết lập chính xác.

Tôi đã tìm thấy @Configurable không thực sự hoạt động với LTW (mặc dù một trong những câu trả lời cho biết). Bạn cần biên dịch thời gian dệt cho @Configurable để làm việc vì lời khuyên đang diễn ra tại thời điểm xây dựng đối tượng (lời khuyên của nhà xây dựng không thể được thực hiện với Springs LTW).

+0

Bạn gặp phải vấn đề gì khi tải thời gian? Tôi đã sử dụng nó khá thành công. Tôi sử dụng LTW để kiểm tra tích hợp và biên dịch thời gian dệt để triển khai. . Bằng cách này, báo cáo phủ sóng thử nghiệm của tôi xuất hiện độc đáo, nhưng triển khai của tôi là mạnh mẽ. –

+0

Vì '@ Configurable' cho phép bạn sử dụng các hàm tạo Java bình thường để khởi tạo đối tượng. Lời khuyên được áp dụng trên constructor Objects. ** Tôi nghĩ bạn chỉ có thể áp dụng lời khuyên xây dựng với thời gian biên dịch dệt ** nhưng có vẻ như bạn cũng có thể làm điều đó với LTW. Vì vậy, bạn là đúng. Những gì bạn không thể làm (và đây là lý do tại sao tôi đã nhầm lẫn) là làm cho AspectJ ITDs với LTW. –

+0

Ahhh, tôi hiểu rồi. . Không biết điều đó. –

1

thử: @Configurable (autowire = Autowire.BY_TYPE). Tự động mặc định được tắt: <

1

Tôi gặp sự cố tương tự mà tôi đã giải quyết hôm nay. Điều quan trọng là bạn cần phải kích hoạt thời gian tải dệt và đảm bảo các lớp aspectj thích hợp được nạp. Trong tệp pom.xml, bạn cần thêm aspectjweaver artifact:

... 
<dependency> 
    <groupId>org.aspectj</groupId> 
    <artifactId>aspectjweaver</artifactId> 
    <version>1.6.12</version> 
</dependency> 
.... 

Bạn có thể thay đổi phiên bản nếu cần. Sau đó, tôi sẽ đi các tuyến đường xsd trong bạn application-context.xml thay vì tuyến đường DTD:

<?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" 
     xsi:schemaLocation="http://www.springframework.org/schema/beans 
     http://www.springframework.org/schema/beans/spring-beans-2.5.xsd 
     http://www.springframework.org/schema/context 
     http://www.springframework.org/schema/context/spring-context-3.0.xsd"> 

    <!--Scans the classpath for annotated components @Component, @Repository, @Service, and @Controller --> 
    <context:component-scan base-package="your.base.package"/> 
    <!--Activates @Required, @Autowired, @PostConstruct, @PreDestroy and @Resource --> 
    <context:annotation-config/> 
    <!--This switches on the load-time weaving for @Configurable annotated classes --> 
    <context:load-time-weaver/> 

</beans> 
6

Tôi đã gặp vấn đề này với Tomcat 7 bằng cách sử dụng LTW cố gắng để autowire đậu vào các lớp miền của tôi.

Có một số cập nhật cho tài liệu cho 3.2.x tại http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/aop.html#aop-configurable-container cho thấy rằng người dùng có thể sử dụng @EnableSpringConfigured thay vì cấu hình xml.

Vì vậy, tôi có chú thích sau đây về đối tượng tên miền của tôi:

@Configurable(preConstruction=true,dependencyCheck=true,autowire=Autowire.BY_TYPE) 
@EnableSpringConfigured 

@EnableSpringConfigured là một substitue cho

<context:spring-configured /> 

và đừng quên để thêm video này vào tập tin xml ngữ cảnh của bạn:

<context:load-time-weaver weaver-class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver" aspectj-weaving="on"/> 

Tất nhiên tôi cần thiết lập Tomcat để tải thời gian tải trước.

Ngoài ra, tôi chạy vào một lỗi trong 3.2.0 (null pointer) vì vậy tôi cần phải nâng cấp lên mùa xuân 3.2.1 (https://jira.springsource.org/browse/SPR-10108)

Tất cả là tốt bây giờ!

0

Ngoài ra, vui lòng xác minh rằng phiên bản AspectJ của bạn là hiện tại. Tôi lãng phí một vài giờ cố gắng để làm cho công việc này, và nguyên nhân là một phiên bản cũ của Aspectjweaver.jar. Tôi cập nhật lên 1.7.2 và mọi thứ hoạt động như một sự quyến rũ.

3

Tôi gặp sự cố tương tự và không bao giờ quản lý được mã hoạt động với @Configurable và @Autowired. Cuối cùng tôi đã quyết định viết một khía cạnh bản thân để xử lý các chú thích @Configurable và @Autowired. Đây là mã:

import java.lang.annotation.Annotation; 
import java.lang.reflect.Field; 

import org.apache.log4j.Logger; 
import org.aspectj.lang.JoinPoint; 
import org.aspectj.lang.annotation.Aspect; 
import org.aspectj.lang.annotation.Before; 
import org.aspectj.lang.annotation.Pointcut; 
import org.springframework.beans.BeansException; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.context.ApplicationContext; 
import org.springframework.context.ApplicationContextAware; 

@SuppressWarnings("rawtypes") 
@Aspect 
public class AutoInjectDependecyAspect implements ApplicationContextAware { 
    private static final Logger LOGGER = Logger.getLogger(AutoInjectDependecyAspect.class); 

    private ApplicationContext applicationContext = null; 

    @Pointcut("execution( (@org.springframework.beans.factory.annotation.Configurable *).new())") 
    public void constructor() { 
    } 

    @Before("constructor()") 
    public void injectAutoWiredFields(JoinPoint aPoint) { 
     Class theClass = aPoint.getTarget().getClass(); 
     try{ 
      Field[] theFields = theClass.getDeclaredFields(); 
      for (Field thefield : theFields) { 
       for (Annotation theAnnotation : thefield.getAnnotations()) { 
        if (theAnnotation instanceof Autowired) { 
         // found a field annotated with 'AutoWired' 
         if (!thefield.isAccessible()) { 
          thefield.setAccessible(true); 
         } 

         Object theBean = applicationContext.getBean(thefield.getType()); 
         if (theBean != null) { 
          thefield.set(aPoint.getTarget(), theBean); 
         } 
        } 
       } 
      } 
     } catch (Exception e) { 
      LOGGER.error("An error occured while trying to inject bean on mapper '" + aPoint.getTarget().getClass() + "'", e); 
     } 

    } 

    @Override 
    public void setApplicationContext(ApplicationContext aApplicationContext) throws BeansException { 
     applicationContext = aApplicationContext; 
    } 

} 

Tiếp theo trong bối cảnh mùa xuân của bạn xác định các khía cạnh để các springcontext sẽ được tiêm vào các khía cạnh

<bean class="[package_name].AutoInjectDependecyAspect" factory-method="aspectOf"/>