JDK5.0注解基础知识:
先定义一个简单的注解:
package com.springzoo.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface NeedTest{
boolean value() default false;
}
Java新语法规定使用@interface修饰定义注解类,一个注解可以拥有多个成员,成员声明和接口方法声明类似,这里,我们仅仅定义了一个成员,成员有几个限制:
1,成员以无入参无跑出异常方式声明
2,可以通过default指定一个默认值
3,成员类型只能是基本类型以及其包装类、String、Class、enums、注解类型,以及由以上类型的数组类型。
上面的@Retention和@Target称为Java的元注解,它们被Java编译器使用哦,会对注解类的行为产生影响。@Retention(RetentionPolicy.RUNTIME)表示这个注解可以在运行期间被JMV读取。
注解保留期限解释:
* SOURCE:注解信息仅保留在目标类代码的原文件中,对应的字节码文件将不再保留;
* CLASS:注解信息将进入目标类代码的字节码文件中,但类加载器加载字节码文件时不会将注解加载到JVM中,也就是说运行期间不能获得注解信息。
* RUNTIME:注解信息在目标类加载到JMV后依然保留,在运行期间可以通过反射机制读取类中的注解信息。
Target表示该注解可以用在什么地方,有以下几种:
* TYPE:类、接口、注解类、Enum声明处,称为类型注解
* FIELD:类成员变量或常量声明处,称为域值注解
* METHOD:方法声明处,称为方法注解
* PARAMETER:参数声明处,称为参数注解
* CONSTRUCTOR:构造函数声明处
* LOCAL_VARIABLE:局部变量声明出
* ANNOTATION_TYPE:注解类声明处,注意TYPE已经包含了ANNOTATION_TYPE
* PACKAGE:包声明处
下面看一个使用刚刚定义的注解的类:
package com.springzoo.anno;
public class ForumService {
@NeedTest(value=true)
public void deleteForum(int forumId){
System.out.println("删除论坛模块:"+forumId);
}
/**
*
* @param topicId
*/
@NeedTest(value=false)
public void deleteTopic(int topicId){
System.out.println("删除论坛主题:"+topicId);
}
}
自己写工具处理注解:
package com.springzoo.anno;
import java.lang.reflect.Method;
public class TestTool {
public static void main(String[] args) {
Class clazz = ForumService.class;
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
NeedTest nt = method.getAnnotation(NeedTest.class);
if (nt != null) {
if (nt.value()) {
System.out.println(method.getName() + "()需要测试");
} else {
System.out.println(method.getName() + "()不需要测试");
}
}
}
}
}
》着手使用@AspectJ
使用前准备:spring在处理@AspectJ注解表达式的时候,需要将spring的asm模块添加到类路径中。asm是轻量级的字节码处理框架,因为Java的反射机制无法获取入参名,spring就利用了asm处理@AspectJ中所描述的方法入参名。另外还需引入AspectJ类库:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-asm</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<!-- cglib依赖-->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
<!-- aspecetj-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.12</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.12</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.6.12</version>
</dependency>
下面开始一个简单的例子:
package com.springzoo.aspectj.example;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class PreGreetingAspect{
@Before("execution(* greetTo(..))")
public void beforeGreeting(){
System.out.println("How are you");
}
}
可以看到,上面的类就是一普通的POJO类。
再定义一个其他类:
package com.springzoo;
import com.springzoo.anno.NeedTest;
public class NaiveWaiter implements Waiter {
public void greetTo(String clientName) {
System.out.println("NaiveWaiter:greet to "+clientName+"...");
}
@NeedTest
public void serveTo(String clientName){
System.out.println("NaiveWaiter:serving "+clientName+"...");
}
public void smile(String clientName,int times){
System.out.println("NaiveWaiter:smile to "+clientName+ times+"times...");
}
}
然后写个测试类:
package com.springzoo.aspectj.example;
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
import com.springzoo.NaiveWaiter;
import com.springzoo.Waiter;
public class AspectJProxyTest {
public static void main(String[] args) {
Waiter target = new NaiveWaiter();
AspectJProxyFactory factory = new AspectJProxyFactory();
factory.setTarget(target);
factory.addAspect(PreGreetingAspect.class);
Waiter proxy = factory.getProxy();
proxy.greetTo("John");
proxy.serveTo("John");
}
}
上面是编程实现,但一般来讲都是配置实现最方便简单:
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
//http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<aop:aspectj-autoproxy/>
<!--bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/-->
<bean id="waiter" class="com.springzoo.NaiveWaiter" />
<bean class="com.springzoo.aspectj.example.PreGreetingAspect" />
</beans>
》@AspcetJ的切点函数详解:
* @annotation()
表示标注了某个注解的所有方法
* execution()
这个是最常见的切点函数,其语法如下:
execution(<修饰符>? <返回类型模式> <方法名模式>(参数模式) <异常模式>?)
execution(public * *(..))
execution(* *To(..))
execution(* com.springzoo.Waiter.*(..))
匹配Waiter接口的所有方法,但仅限于接口中定义的那些方法。
execution(* com.springzoo.Waiter+.*(..))
匹配Waiter接口的所有方法,同时还匹配它实现类的所有其他方法
在类名模式下,.*代表包下的所有类,而..*代表包、子孙包下的所有类。
execution(* com.springzoo.*(..))
execution(* com.springzoo..*(..))
execution(* com..*.*Dao.find*(..))
匹配包名前缀为com的任何包下类名后缀为Dao的所有以find开头的方法
execution(* joke(String,int))
如果入参类型在java.lang包中,可以直接使用类名
execution(* joke(String,*))
两个参数,第一个参数为String类型
execution(* joke(String,..))
第一个参数为String即可
execution(* joke(Object+))
* args() 和 @args()
args()接受一个类名,表示目标类方法入参对象是指定类或者其子类时匹配
args(com.springzoo.Waiter),等价于execution(* *(com.sprongzoo.Waiter+)),也等价于args(com.sprongzoo.Waiter+)
@args()接受一个注解类的类名。
当类继承树中注解点高于入参类型点时候,目标方法不可能匹配切点@args(M)
当类型继承树中注解点低于入参类型点时候,则注解点所在的类及其子孙类作为方法入参时,匹配@args(M)切点。
* within()
通过类匹配模式串声明切点,最小粒度为类
within(com.springzoo.*)
within(com.springzoo..*)
* @within()和@target()
这两个都只接受注解类,其中@target(M)匹配任意标注了@M的目标类,而@within(M)匹配标注了@M的类及其子孙类。由于@within()和@target()以及@annotation()都是针对目标类而言,而非运行时的引用类型而言,那么如果标注了@M的是一个接口,那么所有实现了该接口的类并不匹配@within(M)。
* target() 和 this()
target(M)表示目标类M及其子类匹配切点,类级别
this()是指代理对象的类是否按类型匹配指定类,如果匹配,则代理对象的所有连接点匹配切点。
》@AspectJ进阶