【Spring AOP】學習記錄(一)

由於公司有自己的框架,整個架構跟Spring幾乎沾不上邊,在業餘時間對Spring框架的學習,最近開始學習切面編程“Spring AOP”,並且在博文中記錄一下,以便日後快速上手

概述

這一章先了解一下什麼是AOP,以及寫一個簡單的例子


什麼是AOP?

aop叫aspect oriented program,面向切面的編程。那什麼是面向切面編程,切面就是可插可拔,俗一點說就是想要就要,想不要就不要。直接看看下圖理解一下

圖片標題


作用?

我們在編程的時候很多重複的代碼如:

  • 日誌記錄
  • 事務控制
  • 異常處理
  • 函數執行時間/監控
  • 權限校驗
  • 等等

當項目開發到一定程度的時候,我們如果突然要說加上這麼多東西,那多要命啊。那麼這個時候我們可以考慮加入AOP了


實現框架?

有人問,如果不用spring,那還有其他框架嗎?那是必須的
筆者瞭解到的可以做到的有下面幾個


例子

該例子是從spring官網下拿下來進行測試的,筆者嘗試了可以成功運行


配置1(application-context.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"
       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">
    <!--支持掃描包-->
    <context:component-scan base-package="com.carl.spring"/>
    <!--支持註解-->
    <context:annotation-config/>

    <!--其他配置文件-->
    <import resource="services.xml"></import>
</beans>

spring 入口配置文件


配置2(services.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:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--aop start-->
    <bean id="createUserProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="proxyInterfaces" value="com.carl.spring.hello.aop.ISampleCreateUser"/>
        <!-- Use inner bean, not local reference to target -->
        <property name="target" ref="sampleCreateUser">
        </property>
        <property name="interceptorNames">
            <list>
                <value>debugInterceptor</value>
            </list>
        </property>
    </bean>
    <!--aop end-->
</beans>

該配置是AOP的一個代理配置,獲取對象的時候,直接獲取createUserProxy就可以獲取target執行的代理對象,(因爲ProxyFactoryBean實現了FactoryBean,可以直接獲取對應的對象);interceptorNames就是切面的對象了Advice/Advisor/Interceptor


切面對象(DebugInterceptor.java)

package com.carl.spring.hello.aop;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.stereotype.Component;

/**
 * @author Carl
 * @date 2016/9/11
 */
@Component("debugInterceptor")
public class DebugInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("Before: invocation=[" + methodInvocation + "]");
        Object rval = methodInvocation.proceed();
        System.out.println("Invocation returned");
        return rval;
    }
}

methodInvocation.proceed();爲執行代理方法,在執行代理方法前和執行方法後,執行了一些系統輸出


代理接口(ISampleCreateUser.java)

package com.carl.spring.hello.aop;

/**
 * @author Carl
 * @date 2016/9/11
 */
public interface ISampleCreateUser {

    String create();
}

這裏只做一個接口的定義


接口實現(SampleCreateUserImpl.java)

package com.carl.spring.hello.aop;

import org.springframework.stereotype.Service;

/**
 * @author Carl
 * @date 2016/9/11
 */
@Service("sampleCreateUser")
public class SampleCreateUserImpl implements ISampleCreateUser {
    @Override
    public String create() {
        System.out.println("invoking SampleCreateUserImpl.create()");
        return "return res for SampleCreateUserImpl.create()";
    }
}

注意,這裏有一個註解@Service("sampleCreateUser"),否則在service.xml獲取不了代理對象


測試基礎類(BaseTest.java)

package com.carl.spring.hello.bean;

import org.junit.Before;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author Carl
 * @date 2016/8/28
 */
public abstract class BaseTest {
    protected ApplicationContext context;

    @Before
    public void setUp() throws Exception {
        context =
                new ClassPathXmlApplicationContext(new String[] {"application-context.xml"});

    }
}

測試實例(ISampleCreateUserTest.java)

package com.carl.spring.hello.aop;

import com.carl.spring.hello.bean.BaseTest;
import org.junit.Test;

import static org.junit.Assert.*;

/**
 * Created by Administrator on 2016/9/11.
 */
public class ISampleCreateUserTest extends BaseTest {
    @Test
    public void create() throws Exception {
        ISampleCreateUser createUser = context.getBean("createUserProxy", ISampleCreateUser.class);
        assertNotNull(createUser);
        String res = createUser.create();
        assertEquals("return res for SampleCreateUserImpl.create()", res);
        System.out.println(res);
    }
}

Junit的真實測試類


測試結果

該結果是從控制檯中直接輸出

Before: invocation=[ReflectiveMethodInvocation: public abstract java.lang.String com.carl.spring.hello.aop.ISampleCreateUser.create(); target is of class [com.carl.spring.hello.aop.SampleCreateUserImpl]]
invoking SampleCreateUserImpl.create()
Invocation returned
return res for SampleCreateUserImpl.create()

總結

最終還是實現了切面,可插拔的東西,但是對象必須交給spring進行代理

缺點

  • 每個對象都需要配置一遍代理
  • 每個代理都要主動調用一次methodInvocation.proceed()
  • 需求可能只需要前置執行/後置執行/異常時執行/前後都執行
  • 需要配置xml,註解行不行

這上面的內容spring都可以很好的解決,在後面的博文中會記下來

參考

http://blog.csdn.net/Intlgj/article/details/5671248

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章