1. 程式人生 > >基於spring註解方式配置和使用spring AOP

基於spring註解方式配置和使用spring AOP

spring AOP,面向切面程式設計,通常應用於系統的日誌,事物資訊輸出部分,如下圖中所示,當A呼叫B的f2方法時,如果我們想要讓系統在f2呼叫前輸出一些呼叫前的資訊,在f2呼叫結束後輸出一些呼叫結束後的資訊,一般的想法就是在呼叫f2前去呼叫C的f3,然後在呼叫f2後去呼叫C的f4。從整體上來看,執行f1時將會先呼叫f3,然後f2,然後f4,給人一種從上至下的縱向感。這也是我們通常程式設計時的思維,線性。

這裡寫圖片描述

但是這種方式使得程式碼耦合度增加,不利於後期維護,然後如果還有類C,D,E,F…..中的方法都呼叫了f2,那麼我們還要不厭其煩的在每個地方加上f3,f4?明顯不可能的,這樣程式碼冗餘了,再說,以後維護時稍作修改(比如要將f3,f4替換成f5,f6)每個類呼叫的地方都要改,這樣維護成本太高。因此面向切面程式設計的思路出現了。如下圖中所示,日誌輸出函式f3,f4從執行流程中分離出來,而在呼叫f2時將會觸發f3,f4橫向的切入進執行流程中,這樣理論上我們只需要給f3,f4指定一個觸發器(即f2被呼叫時進行切入),這樣無論在哪個地方f2被呼叫,都能實現f3,f4的呼叫。同時,當想要修改時,也只需要修改觸發器的配置即可,這樣一來就大大降低了維護成本。
這裡寫圖片描述

spring 註解形式配置和使用aop

我這邊是在spring mvc框架的基礎上配置和使用的spring aop,其實都差不多啦,就是多個配置檔案罷了。

先準備下所需的jar包

下載
spring-framework-4.3.9.RELEASE
然後將其中lib中的jar都放到我們自己的web工程lib資料夾內
然後是這4個,都能很容易的百度到
aopalliance-1.0.jar
aspectjweaver-1.7.4.jar
cglib-nodep-2.2.2.jar
commons-logging-1.1.1.jar
最後效果如下,紅框中的4個,其他的都是spring-framework-4.3.9.RELEASE lib中的
這裡寫圖片描述

各種檔案建立

先放一張總體的結構圖
這裡寫圖片描述

大致講解下,從上至下
1.HelloAspect.java
對應的就是上面例子中的class C,裡面定義了前置方法(f3),後置方法(f4)等等,因為我們用的是註解方式配置,所以該檔案中還含有對應的所有配置註解。
2.HelloSpringService.java
就是我們通常開發中的service(業務邏輯層)的介面檔案。
3.HelloSpringServiceImpl.java
HelloSpringService.java介面的實現。
對應例子中的class B
4.indexController.java
controller層,同struts中的action層,業務調動層。
對應例子中的class A
5.applicationContext.xml
spring配置,和下面的dispatcherServlet-servlet.xml一起分離了bean掃描範圍
6.index.jsp
就是個簡單的示例頁面
7.dispatcherServlet-servlet.xml
主要就是spring mvc的相關配置,與applicationContext.xml一起分離了bean掃描範圍
8.web.xml
各種配置檔案的讀取設定等等

將下重點HelloAspect.java ,程式碼如下

package com.java.spring.aop;
import org.aspectj.lang.ProceedingJoinPoint;  
import org.aspectj.lang.annotation.After;  
import org.aspectj.lang.annotation.AfterReturning;  
import org.aspectj.lang.annotation.AfterThrowing;  
import org.aspectj.lang.annotation.Around;  
import org.aspectj.lang.annotation.Aspect;  
import org.aspectj.lang.annotation.Before;  
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect 
public class HelloAspect {
    //對應例子中所說的f3,f4'觸發器'配置,即對com.java.spring.service.impl.HelloSpringServiceImpl下的所有方法都新增前置函式,後置函式...通俗講就是在呼叫HelloSpringServiceImpl中的任意一個函式時都會在不同的時間段呼叫下面設定的函式(前置,後置...)
    @Pointcut("execution(* com.java.spring.service.impl.HelloSpringServiceImpl.*(..))")  
    private void anyMethod(){}//定義一個切入點  

    @Before("anyMethod() && args(name)")  
    public void doAccessCheck(String name){  
        System.out.println(name);  
        System.out.println("前置通知");  
    }  

    @AfterReturning("anyMethod()")  
    public void doAfter(){  
        System.out.println("後置通知");  
    }  

    @After("anyMethod()")  
    public void after(){  
        System.out.println("最終通知");  
    }  

    @AfterThrowing("anyMethod()")  
    public void doAfterThrow(){  
        System.out.println("例外通知");  
    }  

    @Around("anyMethod()")  
    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable{  
        System.out.println("進入環繞通知");  
        Object object = pjp.proceed();//執行該方法  
        System.out.println("退出方法");  
        return object;  
    }  
}

致於上面檔案中每個函式的執行順序,這裡給出執行例項結果

//indexController.java檔案
package com.springMVC.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.java.spring.service.HelloSpringService;
@Controller
public class indexController{
    @Autowired
    HelloSpringService helloSpringService;

    @RequestMapping( value="/index")
    public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1)
            throws Exception {
        // TODO Auto-generated method stub
        ModelAndView model=new ModelAndView();
        helloSpringService.printHello("123");//對應例子中的f1中呼叫f2部分,引數123
        model.addObject("demoStr", "HelloWorld");
        model.setViewName("index");
        return model;
    }
}
//HelloSpringServiceImpl.java檔案
package com.java.spring.service.impl;
import org.springframework.stereotype.Service;
import com.java.spring.service.HelloSpringService;
@Service
public class HelloSpringServiceImpl implements HelloSpringService{
    @Override
    public String printHello(String xx) {
        // TODO Auto-generated method stub
        System.out.println("Hello Spring,this is impl");
        return "123";
    }
}

這裡寫圖片描述

可以看到大致的執行順序類似下圖
這裡寫圖片描述

3個配置檔案內容,主要的說明都寫在註釋裡了,這裡就不多講了。
applicationContext.xml配置檔案,主要針對非@controller註釋的掃描

<?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" xmlns:jdbc="http://www.springframework.org/schema/jdbc"  
    xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
        http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"
    default-lazy-init="true">

    <!-- 使用annotation 自動註冊bean, 並保證@Required、@Autowired的屬性被注入 -->
    <!-- 不掃描com.java.spring包下的@Controller註釋,其他的註釋都掃描,除controller註釋掃描都放在applicationContext.xml中(掃描功能分離),despatch servlet中專職controller註釋的掃描 -->
    <context:component-scan base-package="com.java.spring">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
    </context:component-scan>   
     <!-- 使AspectJ註解起作用:自動為匹配的類生產代理物件 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>

dispatcherServlet-servlet.xml配置檔案,主要針對@controller註釋的掃描

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

    <!-- 自動掃描且只掃描@Controller -->
    <!-- 當且僅當掃描com.springMVC.controller下的@Controller註解 -->
    <!-- use-default-filters="false" 指除了context子項,其他註釋都不掃描-->
    <context:component-scan base-package="com.springMVC.controller" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
    </context:component-scan>

    <!--Spring3.1開始的註解 HandlerMapping -->  
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>  
    <!--Spring3.1開始的註解 HandlerAdapter -->  
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>  

    <!-- 定義JSP檔案的位置 --> 
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
        <property name="prefix" value="/WEB-INF/jsp/"/>  
        <property name="suffix" value=".jsp"/>  
    </bean> 
</beans>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    version="2.5">
    <!-- 掃描applicationContext.xml檔案-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath*:config/applicationContext.xml,
        </param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!-- 設定攔截器&對應的配置檔案-->   
    <servlet>  
        <servlet-name>dispatcherServlet</servlet-name>  
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
        <load-on-startup>1</load-on-startup>  
    </servlet>  
    <servlet-mapping>  
        <servlet-name>dispatcherServlet</servlet-name>  
        <url-pattern>/</url-pattern>  
    </servlet-mapping> 
</web-app>