Whiteship's Note


ProxyFactoryBean을 이용한 초간단 AOP 구현

모하니?/Coding : 2009. 12. 16. 16:01


스프링 AOP가 어렵다고 생각하시는 분들은 저 클래스를 사용하는 방법부터 익히시면 도움이 될 것 같습니다.
아주 간략하게 ProxyFactoryBean을 사용해서 AOP를 적용해 보겠습니다.

AOP는 OOP 같은 프로그래밍 기법이지 무슨 기술이 아닙니다. 따라서 이 글에서 구현하는 내용은 AOP 적용 방법 중 하나라고 생각하시면 됩니다.

    interface Service {
        public void hi();
        public void hi2();
    }

이런 인터페이스가 있고

    class ServiceImpl implements Service {
        public void hi(){
            System.out.println("my business");
        }

        @Override
        public void hi2() {
            System.out.println("my2");
        }
    }

이런 구현체가 있을 때. hi() 호출 전 후에만 메시지를 출력하고 hi2()는 걍 저대로 출력하고 싶다면 ...  우선 해당 작업을 수행할 MethodIterceptor 인터페이스의 구현체를 만듭니다.

    class ServiceLoggingAdvice implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("before hi");
            invocation.proceed();
            System.out.println("after hi");
            return null; 
        }
    }

그리고 hi()와 hi2()를 선별해줄 NameMatchMethodPointcut을 만듭니다.

    class ServiceLoggingPointcur extends NameMatchMethodPointcut {

        ServiceLoggingPointcur() {
            setMappedName("hi");
        }
    }

이제 마지막으로 ProxyFactoryBean으로 위에서 만들었던 타겟(Service 구현체), 어드바이스(MethodInterceptor 구현체), 포인트컷(NameMatchMethodPointcut 구현체)을 ProxyFactoryBean에 설정 해주면 끝!

    Service service;
    ProxyFactoryBean proxyFactoryBean;

    @Before
    public void setUp(){
        proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setTarget(new ServiceImpl());
        proxyFactoryBean.addAdvisor(new DefaultPointcutAdvisor(
                new ServiceLoggingPointcur(),
                new ServiceLoggingAdvice()));
    }

    @Test
    public void logging(){
        Service service = (Service) proxyFactoryBean.getObject();
        service.hi();
        service.hi2();
    }

이렇게 하면 콘솔에


요런식으로 출력됩니다.

자세한 내용은 언젠간 출간될 Toby님의 스프링 책을 참조하세요.




top

  1. Favicon of http://blog.lckymn.com BlogIcon Kevin 2009.12.17 19:43 PERM. MOD/DEL REPLY

    저에겐 역시 AspectJ 가 쉽고 편하고 좋은거 같습니다. :)

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.12.17 20:32 신고 PERM MOD/DEL

    저두요.ㅋㅋ

    이건 그냥 스프링 AOP가 너무 마술 같아서 멀게 느끼시는 분들을 위한 초간단 예제에요.

Write a comment.


[스프링 3.0 테스트 확장] 애노테이션 설정 기반 테스트 러너 만들기 4 - 일단 끝

모하니?/Coding : 2009. 12. 16. 13:53


/**
 * @author Keesun Baik(Whiteship)
 * @author Seongyoon Kim(Is윤군)
 */
public class AnnotationContextLoader extends AbstractContextLoader {

    private static final String JAVA_FILE_SUFFIX = ".java";
    private static final String APP_CONFIG_FILE_PREFIX = "AppConfig";

    @Override
    public String getResourceSuffix() {
        return APP_CONFIG_FILE_PREFIX + JAVA_FILE_SUFFIX;
    }

    @Override
    protected String[] generateDefaultLocations(Class<?> clazz) {
        Assert.notNull(clazz, "Class must not be null");
        String suffix = getResourceSuffix();
        Assert.hasText(suffix, "Resource suffix must not be empty");
        return new String[] { clazz.getName() + suffix };
    }

    @Override
    protected String[] modifyLocations(Class<?> clazz, String... locations) {
        String[] modifiedLocations = new String[locations.length];
        for (int i = 0; i < locations.length; i++) {
            String path = locations[i];

            if(path.endsWith("/"))
                path = path.substring(0, path.length() - 1);

            if (path.startsWith("/")) {
                modifiedLocations[i] = ClassUtils.convertResourcePathToClassName(path.substring(1));
            }
            else if (!ResourcePatternUtils.isUrl(path)) {
                modifiedLocations[i] = getClassName(clazz, path);
            }
            else {
                throw new UnsupportedOperationException();
            }
        }
        return modifiedLocations;
    }

    private String getClassName(Class clazz, String path) {
        return ClassUtils.convertResourcePathToClassName(
            StringUtils.cleanPath(ClassUtils.classPackageAsResourcePath(clazz) + "/" + path));
    }

    public final ConfigurableApplicationContext loadContext(String... locations) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        prepareContext(context);
        customizeBeanFactory(context.getDefaultListableBeanFactory());
        context.register(getAppConfigClasses(context.getClassLoader(), locations));
        context.scan(getAppConfigPackages(context.getClassLoader(), locations));
        AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
        context.refresh();
        context.registerShutdownHook();
        return context;
    }

    private String[] getAppConfigPackages(ClassLoader classLoader, String[] locations) {
        List<String> packages = new ArrayList<String>();
        for(String location : locations){
            if(!location.contains(JAVA_FILE_SUFFIX))
                packages.add(location);
        }
        return packages.toArray(new String[packages.size()]);
    }

    private Class<?>[] getAppConfigClasses(ClassLoader classLoader, String[] locations) throws ClassNotFoundException {
        List<Class<?>> classes = new ArrayList<Class<?>>();
        for(String location : locations){
            if(location.contains(JAVA_FILE_SUFFIX))
                classes.add(ClassUtils.forName(location.replace(JAVA_FILE_SUFFIX, ""), classLoader));
        }
        return classes.toArray(new Class<?>[classes.size()]);
    }

    protected void prepareContext(GenericApplicationContext context) {
    }
    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
    }
    protected void customizeContext(GenericApplicationContext context) {
    }

이걸 사용하시면 됩니다. 이제 스프링 이슈에 올려야지. 캬캬캬
스프링 레퍼런스 공부할 때 오타 찾아서 이슈 등록하던게 엊그제 같은데 이젠 코드도 기여할 수 있으려나..

코드를 보시면 아시겠지만, classpath:, file:, url: 등의 prefix 지원은 포기했습니다. 그래도..

1. 스프링 애노테이션 설정 파일만 가지고 쉽게 테스트 할 수 있으며
2. 스프링 애노테이션 설정 파일을 명시적으로 설정할 수 있으며
3. 임의의 패키지를 명시적으로 설정할 수 있습니다.

딱 제가 원하던 만큼이니 이정도면 저는 만족합니다. 이걸 스프링에서 고쳐서 넣어주던 말던~ 일단은 try.

ps: 같이 코딩해주고 상의해준 성윤군과, 스프링 러너와 로더 설정 방법 알려주신 사부님 썡큐!

top

  1. Favicon of http://helols.pe.kr BlogIcon is_Yoon 2009.12.16 14:44 PERM. MOD/DEL REPLY

    유겐이 쌩만 안해줘도 성공..ㅋㅋ

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.12.16 15:12 신고 PERM MOD/DEL

    내가 원하는 기능은 만들었으니 이미 성공ㅋ

  2. Favicon of http://blog.lckymn.com BlogIcon Kevin 2009.12.17 19:49 PERM. MOD/DEL REPLY

    본문중...
    『코드를 보시면 아시겠지만, classpath:, file:, url: 등의 prefix 지원은 포기했습니다. 그래도..』

    http://pds3.egloos.com/pds/200707/27/07/c0067007_0307454.jpg

    Favicon of http://whiteship.me BlogIcon 기선 2009.12.17 20:30 PERM MOD/DEL

    throw new UnSupportedException()을 던지고 있지요. 크하핫

Write a comment.