注解(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方法才能代理成功。