java自定義注解解析及相關(guān)場景實(shí)現(xiàn)

注解(Annotation)是java1.5之后提供的一種語法蚤氏。其主要作用是編譯檢查(比如@override)和代碼分析(通過代碼中添加注解,利用注解解析器對添加了注解的代碼進(jìn)行分析踊兜,獲取想要的結(jié)果竿滨,一般自定義的注解都是這一種功能)。

1.1 JDK提供的注解

JDK提供的注解最常用的是3個,@Override于游,@Deprecated和@SuppressWarnings.

1.1.1 @Override

@Override表示子類重寫了父類的方法毁葱,或者實(shí)現(xiàn)了接口的方法。幫助開發(fā)者確認(rèn)子類是否正確的覆蓋了父類的方法贰剥,若父類中沒有此方法倾剿,編譯器即報錯。但是蚌成,子類與父類有同樣的方法前痘,但子類的方法上沒有@Override注解,是不會報錯担忧。
以基類Object的方法toString ()為例:

//正確的寫法
public class ChildClass  {
    
    
    //@Override是幫助開發(fā)者確認(rèn)子類是否正確的覆蓋了父類的方法

    public void read(){
        System.out.println("this is a parent method!");
    }
    
    @Override
    public String toString(){
        
        return "ChildClass";
    }
    
}

但是如果toString()不加Override,也沒問題芹缔,只是簡單的子類重寫父類(Object)的方法。

public class ChildClass  {
    
    
    //@Override是幫助開發(fā)者確認(rèn)子類是否正確的覆蓋了父類的方法

    public void read(){
        System.out.println("this is a parent method!");
    }
    
    public String toString(){
        
        return "ChildClass";
    }
    
}

但是瓶盛,如果把toString()方法改成toString1()方法就會報錯最欠。

public class ChildClass  {
    
    
    //@Override是幫助開發(fā)者確認(rèn)子類是否正確的覆蓋了父類的方法

    public void read(){
        System.out.println("this is a parent method!");
    }
    
    @Override
    public String toString1(){
        
        return "ChildClass";
    }
    
}

提示錯誤:The method toString1() of type ChildClass must override or implement a supertype method
翻譯過來就是toString1()方法必須是重寫重寫父類的方法或者實(shí)現(xiàn)相關(guān)接口。即提示父類中沒有toString1() 方法蓬网。這樣就通過提示來確保開發(fā)者能正確重寫toString()方法窒所。

1.1.2 @Deprecated

@Deprecated用于提示開發(fā)者,標(biāo)注此注解的方法已經(jīng)被棄用了帆锋。請使用另外推薦的方法

    @Deprecated()
    public String toString1(){
        
        return "ChildClass";
    }
    
    public static void main(String[] args){
        
        ChildClass child=new ChildClass();
        child.toString1();
    }

使用toString1()方法吵取,編譯器會提示,將toString1()畫一條橫線锯厢,表示此方法已經(jīng)過期皮官。

1.1.3 @SuppressWarnings

@SuppressWarnings是抑制警告的意思。比如我們新建一個變量实辑,但是沒有用捺氢,編譯器會提示此變量未使用的警告。如果在方法中剪撬,添加了@SuppressWarnings的相關(guān)注解摄乒,這個警告就不會再提示了。

@SuppressWarnings({"unused"})
    public static void main(String[] args){
           
        List<Integer>list=new ArrayList<>();
    }
 

2. 自定義注解

除了使用java自帶的注解残黑,我們也可以自定義注解馍佑,用于幫助為相關(guān)代碼打上標(biāo)簽,然后我們在解析注解的邏輯中就可以通過這些標(biāo)簽來完成相關(guān)的工作梨水,比如拭荤,權(quán)限控制,日記記錄等等疫诽。

2.1 自定義注解語法

定義一個自定義注解舅世,與定義一個接口類似旦委,只不過在interface前加是哪個@。其內(nèi)部可以添加屬性值雏亚,其屬性值的定義為
修飾符 返回值類型 屬性名() [default value]
其中缨硝,修飾符只能用public 和abstract。 返回值為基本類型评凝、字符串追葡、枚舉腺律、注解以及以上類型的一維數(shù)組奕短。
定義自定義注解,還需要用到元注解匀钧,用于修飾自定義注解翎碑,一般我們會用到兩個。@Retention和@Target之斯。
@Retention
用于確定注解的生命周期日杈。其有三個枚舉變量可選

public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     * SOURCE級別表示代碼級別可見,經(jīng)過編譯器編譯生成字節(jié)碼對象時佑刷,此注解就沒了莉擒。
     * 比如@override就是代碼級別可見
     */
    SOURCE,  

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     * CLASS表示字節(jié)碼對象級別可見,但是字節(jié)碼對象被虛擬機(jī)加載時瘫絮,
     * 這個注解會被拋棄涨冀,這是默認(rèn)的可見級別
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     * RUNTIME表示運(yùn)行時也可見,當(dāng)虛擬機(jī)加載字節(jié)碼對象時麦萤,此注解仍然可見鹿鳖。
     * 因此可以通過反射獲取注解信息,然后完成相應(yīng)的注解解析工作壮莹,一般自定義的注解都是運(yùn)行時可見翅帜。
     */
    RUNTIME
}

@Target
用于修飾此注解可以用于什么類型上。比如注解可以用在類級別命满、方法涝滴、成員字段或者構(gòu)造函數(shù)上。

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration 可以修飾類*/
    TYPE,  

    /** Field declaration (includes enum constants) 可以修飾字段*/
    FIELD,

    /** Method declaration 可以修飾方法*/
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration 構(gòu)造方法*/
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

下面是一個簡單的自定義注解:


/**
 * @author sks
 * 這個注解用于日志管理  胶台,從修飾注解的元注解可知歼疮,這個注解可以修飾方法和類。其可見范圍到運(yùn)行時都可見概作。
 *
 */
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Myanno {
    
    /** 下面時候注解的屬性 **/  
    /** 要執(zhí)行的操作類型比如:add操作 **/  
    public String operationType() default "";  
     
    /** 要執(zhí)行的具體操作比如:添加用戶 **/  
    public String operationName() default "";
}

2.2 自定義注解解析

上述定義的自定義注解腋妙,只是一個空的定義,沒有任何的意義讯榕。因此需要我們自己定義相關(guān)的自定義注解的解析骤素。上面提到匙睹,自定義的注解需要定義注解的可見范圍。一般我們都定義為運(yùn)行時可見济竹。因此痕檬,通過反射,我們可以拿到注解的內(nèi)容送浊。通過反射拿到代碼的注解內(nèi)容梦谜,進(jìn)行相關(guān)的邏輯處理工作,以達(dá)到注解的目的袭景。
通過反射獲得注解內(nèi)容的常用方法有

T getAnnotation(Class<T>) : 獲得當(dāng)前對象的指定的注解唁桩。
Annotation[] getAnnotations() X: 獲得當(dāng)前對象的所有注解
boolean isAnnotationPresent(annotationClass): 當(dāng)前對象是否有注解。

2.3自定義注解舉例

1、 定義一個注解,用于獲取水果的名字憨降。

   @Target({ElementType.METHOD,ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface FruitName {
    
    public String value() default "fruit";

}

上面這個注解定義說明此注解可以用在類,方法和成員地段上单山。是運(yùn)行時可見的,其內(nèi)有一個屬性幅疼,默認(rèn)值是“fruit”米奸。
2、然后我們定義一種叫蘋果的水果爽篷。其內(nèi)部有個成員變量appleName悴晰。我們在這個成員字段上加上@FruitName注解。

public class Apple {
    
    @FruitName("Apple")
    private String appleName;

}

3狼忱、注解的解析

public class FruitInfoUtil {
    
    
    public static void main(String[] args){
        
        FruitInfoUtil util=new FruitInfoUtil();
        util.getInfomation(Apple.class);
    }
    
    /*
    *這個方法用于注解的解析膨疏,其輸入是Class類型的對象。通過反射這個Class對象獲取相關(guān)的注解钻弄,進(jìn)行注解的解析
    */
    public void getInfomation(Class<?>clazz){
        
        //因?yàn)樽⒔馐窃诔蓡T字段上佃却,因此需要獲得類的所有字段信息
        Field[] fields = clazz.getDeclaredFields();
        
        for (Field field : fields) {
            //判斷這個字段上是否有相應(yīng)的注解信息(FruitName.class)
            if (field.isAnnotationPresent(FruitName.class)) {
                
                FruitName fruitName = field.getAnnotation(FruitName.class);
                System.out.println("水果名字是"+fruitName.value());
            }
            
        }
        
    
        
    }

}

其結(jié)果是:

水果名字是Apple

這個例子簡單的說明了自定義注解如何使用。但是在實(shí)際應(yīng)用中窘俺,也許會有很多類都應(yīng)用了自定義注解饲帅,即我們不知道是具體哪個類或者哪個方法使用了自定義注解。一般可以通過SpringMVC的攔截器或者SpringAOP獲取添加了注解的方法瘤泪,在攔截器或者AOP的通知里對注解進(jìn)行處理灶泵。具體可看下面的章節(jié)。

3.自定義注解應(yīng)用

一般web開發(fā)都用到spring框架对途。結(jié)合spring的SpringMVC的攔截器或者SpringAOP赦邻,注解可以完成權(quán)限控制,日志記錄等功能实檀。

3.1 自定義注解+SpringMVC攔截器實(shí)現(xiàn)權(quán)限控制功能

我們想實(shí)現(xiàn)這么一個注解惶洲。方法上添加了此注解的方法按声,不需要登錄權(quán)限即可執(zhí)行,否則就要查看其http請求的session中是否包含相關(guān)登錄信息恬吕,以確定是否執(zhí)行方法里的內(nèi)容签则。

/**
 * @author sks
 * 這個注解用于權(quán)限控制
 *
 */
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface NoLogin {
    

}

實(shí)現(xiàn)HandlerInterceptor 接口,完成自定義SpringMVC攔截器铐料,在攔截器內(nèi)部實(shí)現(xiàn)注解的解析功能渐裂。

public class MyInceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        
        if (!(handler instanceof HandlerMethod)) {
              System.out.println("當(dāng)前操作handler不為HandlerMethod=" + handler.getClass().getName() + ",req="
                        + request.getQueryString());
                return false;
        }
        //獲得經(jīng)過攔截器的方法
        HandlerMethod handlerMethod=(HandlerMethod) handler;
        
        String methodName  = handlerMethod.getMethod().getName();
        //通過反射的getAnnotation方法獲得其方法上的指定的NoLogin類型的注解。
        NoLogin  myanno= handlerMethod.getMethod().getAnnotation(NoLogin.class);
        if (myanno!=null) {  //如果獲得的注解不為空的話钠惩,說明此方法不需要權(quán)限就可執(zhí)行柒凉。
            System.out.println("當(dāng)前操作不需要登錄");
            return true;
        }
        //否則就要看其session 的屬性里是否有關(guān)于LOGIN屬性的信息,若沒有妻柒,則攔截此方法扛拨,不執(zhí)行方法的操作
        if (request.getSession().getAttribute("LOGIN")==null) {
            System.out.println("當(dāng)前操作" + methodName + "用戶未登錄,ip=" + request.getRemoteAddr());
            return false;
        }
        
        System.out.println("當(dāng)前操作" + methodName + "用戶登錄:" + request.getSession().getAttribute("LOGIN"));
          return true;
    }


    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        // TODO Auto-generated method stub

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        // TODO Auto-generated method stub

    }

}

然后,在springMVC的配置文件里配置攔截器的相關(guān)信息举塔。

<?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:context="http://www.springframework.org/schema/context"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd">

    <context:component-scan base-package="com.test.controller" />
    <context:component-scan base-package="com.test.spring*" />
    <context:component-scan base-package="com.test.aop*" />
    <mvc:annotation-driven />
    <!-- 配置注解驅(qū)動 -->
    <!--  <mvc:annotation-driven conversion-service="conversionService"/> -->

    <mvc:resources location="/img/" mapping="/img/**"/> 
    <mvc:resources location="/css/" mapping="/css/**"/> 
    <mvc:resources location="/js/" mapping="/js/**"/>
    <mvc:resources location="/views/" mapping="/views/**"/>
    <mvc:resources location="/ui/" mapping="/ui/**"/>
    <mvc:resources location="/fonts/" mapping="/fonts/**"/>
    
    <mvc:resources location="/bower_components/" mapping="/bower_components/**"/>
    <mvc:resources location="/dist/" mapping="/dist/**"/>
    <mvc:resources location="/documentation/" mapping="/documentation/**"/>
    
    <!-- mvc:interceptors攔截器 ,注意其寫法求泰,要先寫攔截的路徑央渣,然后再排除相關(guān)的不攔截的路徑-->
     <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>       <!-- 攔截器攔截所有的方法 -->
            <!-- 一般login申請和退出登錄都不應(yīng)該攔截。比如登錄頁面最終的http請求是/user/login
            那么就要寫成path="/*/login"渴频,而寫成path="/login"則是不行的-->
             <mvc:exclude-mapping path="/*/login" /> 
            <mvc:exclude-mapping  path="/img/**"/>      <!--靜態(tài)資源也不應(yīng)該攔截 -->
            <mvc:exclude-mapping path="/css/**"/> 
            <mvc:exclude-mapping path="/js/**"/>
            <mvc:exclude-mapping path="/views/**"/> 
            <mvc:exclude-mapping path="/ui/**"/>
            <mvc:exclude-mapping  path="/fonts/**"/>
            
            <mvc:exclude-mapping  path="/bower_components/**"/>
            <mvc:exclude-mapping  path="/dist/**"/>
            <mvc:exclude-mapping  path="/documentation/**"/>
            <bean class="com.test.intercept.MyInceptor"/>
            
            
        </mvc:interceptor>
    </mvc:interceptors> 
     
    
    <!-- 加載配置文件 -->
    <context:property-placeholder location="classpath:conf/resource.properties" />
    
    
    <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
    
</beans>

最后芽丹,看看Controller的內(nèi)容,

@Controller
public class LoginController {
        
    @RequestMapping("/user/login")
    @ResponseBody
    private String loginCheck(HttpServletRequest request){
        
        //EcgUser ecgUser=userService.queryUserById(itemId);
          String username=request.getParameter("username");
          String password=request.getParameter("password");
        
          HttpSession session = request.getSession();
          
          if (username.equals("andy")&&password.equals("123")) {
            
              session.setAttribute("LOGIN", username);
              return "success";
        }
          
        return "false";
    }
    
    
    @NoLogin
    @RequestMapping("/login/nologin")
    @ResponseBody
    private String test(Model model) throws JsonProcessingException{        
        
        
        System.out.println("有申請");
        List<Product> list = new ArrayList<Product>();
        
        //這里把“類別名稱”和“銷量”作為兩個屬性封裝在一個Product類里卜朗,每個Product類的對象都可以看作是一個類別(X軸坐標(biāo)值)與銷量(Y軸坐標(biāo)值)的集合
        list.add(new Product("襯衣", 10));
        list.add(new Product("短袖", 20));
        list.add(new Product("大衣", 30));
        list.add(new Product("dayin", 30));
        
        ObjectMapper mapper=new ObjectMapper();
        
        
        String writeValueAsString = mapper.writeValueAsString(list);
        
         return writeValueAsString;
    }
    
    @RequestMapping("/login/{itemId}")
    @ResponseBody
    private String queryUserById(@PathVariable String itemId,Model model){      
         
            
         return "未登錄";
    }

}

上面的類中拔第,第一個方法登錄方法,此方法不經(jīng)過攔截器场钉,(如果此方法攔截了蚊俺,這時候確實(shí)還處于未登錄狀態(tài),那么loginCheck方法就不會被執(zhí)行了逛万。因此不能攔截)泳猬。
類中的第二個方法和第三個方法都會經(jīng)過攔截器。由于第二個方法加上了@NoLogin注解宇植,表示這個方法未經(jīng)過登錄也可以執(zhí)行得封。第三個方法沒有@NoLogin注解,如果用戶未登錄指郁,那么是不會被執(zhí)行的忙上。如果用戶已登錄,http請求中的session包含了LOGIN屬性,那么就會執(zhí)行闲坎。具體的結(jié)果就不貼了疫粥。
這樣洋腮,通過自定義注解和springmvc的攔截器,可以實(shí)現(xiàn)一個簡單的權(quán)限控制功能手形。

3.2 自定義注解+SpringAOP實(shí)現(xiàn)日志記錄功能

springAOP:面向切面編程啥供,是spring的兩大核心模塊之一,用于將系統(tǒng)中通用的模塊或者功能抽取出來库糠。其基本原理是AOP代理(分為動態(tài)代理和cglib代理)伙狐。利用aop里的通知,實(shí)現(xiàn)自定義注解的解析瞬欧,可以完成相關(guān)的工作贷屎。
這里我們設(shè)計一個注解,即在需要進(jìn)行日志記錄的地方加上此注解即可實(shí)現(xiàn)日志自動記錄的功能艘虎。
首先唉侄,仍是定義相關(guān)注解:


/**
 * @author sks
 * 這個注解用于日志管理
 *
 */
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Myanno {
    

    /** 要執(zhí)行的操作類型比如:add操作 **/  
    public String operationType() default "";  
     
    /** 要執(zhí)行的具體操作比如:添加用戶 **/  
    public String operationName() default "";
}

其次,定義相關(guān)的aop代理通知野建。這里属划,把注解的解析工作放在事后通知上,即下面的after方法候生。這里用簡單的system.out來模擬日志記錄功能同眯。

/**
 * @author sks
 *
 */
public class MyAdvice4Anno {
    
    public void before(JoinPoint joinPoint){
        
        System.out.println("獲取參數(shù)--》前置通知");
    
    for (int i = 0; i < joinPoint.getArgs().length; i++) {
        
        System.out.println("獲取參數(shù)--》"+joinPoint.getArgs()[i].toString());
    }   
    
  
    }
    
    /*
    *自定義的注解放在事后通知中
    */
    public void after(JoinPoint joinPoint){
        System.out.println("后置通知");
        //通過連接點(diǎn)JoinPoint 獲得代理的方法,進(jìn)而獲取方法上的注解信息
        Method[] methods = joinPoint.getTarget().getClass().getMethods();
        for (Method method : methods) {
            Myanno annotation = method.getAnnotation(Myanno.class);
            if (annotation!=null) {
                String operationName = annotation.operationName();
                String operationType = annotation.operationType();
                
                  //*========控制臺輸出=========*//  
                 System.out.println("=====controller后置通知開始=====");  
                 System.out.println("請求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType);  
                 System.out.println("方法描述:" + operationName);  
                
                break;
            }
            
            
        }
    }
            
    }
    //異常通知
    public void afterException(){
        System.out.println("出事啦!出現(xiàn)異常了!!");
    }

}

然后在springMvc的配置文件中唯鸭,做好aop的配置工作须蜗。

<?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:context="http://www.springframework.org/schema/context"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd">

    <context:component-scan base-package="com.test.controller" />
    <context:component-scan base-package="com.test.spring*" />
    <context:component-scan base-package="com.test.aop*" />
    <mvc:annotation-driven />
    <!-- 配置注解驅(qū)動 -->
    <!--  <mvc:annotation-driven conversion-service="conversionService"/> -->

    <mvc:resources location="/img/" mapping="/img/**"/> 
    <mvc:resources location="/css/" mapping="/css/**"/> 
    <mvc:resources location="/js/" mapping="/js/**"/>
    <mvc:resources location="/views/" mapping="/views/**"/>
    <mvc:resources location="/ui/" mapping="/ui/**"/>
    <mvc:resources location="/fonts/" mapping="/fonts/**"/>
    
    <mvc:resources location="/bower_components/" mapping="/bower_components/**"/>
    <mvc:resources location="/dist/" mapping="/dist/**"/>
    <mvc:resources location="/documentation/" mapping="/documentation/**"/>
    
    <!-- 加載配置文件 -->
    <context:property-placeholder location="classpath:conf/resource.properties" />
        
    <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
    

    
     <bean id="myAdvice4Anno" class="com.test.zhujie.myanno.MyAdvice4Anno"></bean>
    
    <aop:config>
        配置切入點(diǎn)
        <aop:pointcut expression="execution(* com.test.controller.AnnoController.*(..))" id="pointcut2"/>
        
        <aop:aspect  ref="myAdvice4Anno">
            <aop:before method="before" pointcut-ref="pointcut2"/>
            <aop:after method="after" pointcut-ref="pointcut2"/> 
            <aop:after-throwing method="afterException" pointcut-ref="pointcut2"/>
        </aop:aspect>

    </aop:config> 
        
</beans>

最后,看相關(guān)的控制層代碼目溉。

**
 * @author sks
 *
 */
@Controller
public class AnnoController {
    
    
    
    private static Logger logger = LoggerFactory.getLogger("log");

    @RequestMapping("/anno/queryData")
    @ResponseBody
    private E3Result queryData(Model model,String username,String password) throws JsonProcessingException{     
        logger.warn("查詢用戶id");
        
    //  userService.
        
        List<Product> list = new ArrayList<Product>();
        
        //這里把“類別名稱”和“銷量”作為兩個屬性封裝在一個Product類里明肮,每個Product類的對象都可以看作是一個類別(X軸坐標(biāo)值)與銷量(Y軸坐標(biāo)值)的集合
        list.add(new Product("襯衣", 10));
        list.add(new Product("短袖", 20));
        list.add(new Product("大衣", 30));
        
        E3Result.ok(list);
        
        ObjectMapper mapper=new ObjectMapper();
        
        
        String writeValueAsString = mapper.writeValueAsString(list);
        
         return  E3Result.ok(list);
    }
//  
    @Myanno(operationType="add操作",operationName="添加用戶")
    @RequestMapping("/anno/test")
    @ResponseBody
    public String test() throws JsonProcessingException{        
        logger.warn("查詢用戶id");
        
        System.out.println("有申請");
        List<Product> list = new ArrayList<Product>();
        
        //這里把“類別名稱”和“銷量”作為兩個屬性封裝在一個Product類里,每個Product類的對象都可以看作是一個類別(X軸坐標(biāo)值)與銷量(Y軸坐標(biāo)值)的集合
        list.add(new Product("襯衣", 10));
        list.add(new Product("短袖", 20));
        list.add(new Product("大衣", 30));
        list.add(new Product("dayin", 30));
        
        ObjectMapper mapper=new ObjectMapper();
        
        
        String writeValueAsString = mapper.writeValueAsString(list);
        
         return writeValueAsString;
    }


    

}

上述的test方法假設(shè)需要進(jìn)行日志記錄操作缭付,就在test()方法上加上@Myanno(operationType="add操作",operationName="添加用戶")柿估。
此時,訪問這個方法蛉腌,編譯器輸出:

=====controller后置通知開始=====
請求方法:com.test.controller.AnnoController.test().add操作
方法描述:添加用戶

成功官份。
注意事項(xiàng):切點(diǎn)所代表的方法需要是public方法,aop代理才能成功烙丛。之前舅巷,test的方法用的是private修飾,一直aop代理不成功河咽。后查才發(fā)現(xiàn)是此問題钠右。原因是aop代理要么是動態(tài)代理,要么是cglib代理忘蟹,一個是接口實(shí)現(xiàn)飒房,一個是繼承父類搁凸,都需要其方法是public方法才能代理成功。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末狠毯,一起剝皮案震驚了整個濱河市护糖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嚼松,老刑警劉巖嫡良,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異献酗,居然都是意外死亡寝受,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進(jìn)店門罕偎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來很澄,“玉大人,你說我怎么就攤上這事颜及∷粒” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵器予,是天一觀的道長浪藻。 經(jīng)常有香客問我,道長乾翔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任施戴,我火速辦了婚禮反浓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘赞哗。我一直安慰自己雷则,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布肪笋。 她就那樣靜靜地躺著月劈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪藤乙。 梳的紋絲不亂的頭發(fā)上猜揪,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天,我揣著相機(jī)與錄音坛梁,去河邊找鬼而姐。 笑死,一個胖子當(dāng)著我的面吹牛划咐,可吹牛的內(nèi)容都是我干的拴念。 我是一名探鬼主播钧萍,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼政鼠!你這毒婦竟也來了风瘦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤公般,失蹤者是張志新(化名)和其女友劉穎万搔,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體俐载,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蟹略,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了遏佣。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挖炬。...
    茶點(diǎn)故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖状婶,靈堂內(nèi)的尸體忽然破棺而出意敛,到底是詐尸還是另有隱情,我是刑警寧澤膛虫,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布草姻,位于F島的核電站,受9級特大地震影響稍刀,放射性物質(zhì)發(fā)生泄漏撩独。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一账月、第九天 我趴在偏房一處隱蔽的房頂上張望综膀。 院中可真熱鬧,春花似錦局齿、人聲如沸剧劝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽讥此。三九已至,卻和暖如春谣妻,著一層夾襖步出監(jiān)牢的瞬間萄喳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工拌禾, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留取胎,地道東北人。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像闻蛀,于是被迫代替她去往敵國和親匪傍。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容