理解注解:
1、首先注解是在JDK5.0及以后版本中引入的。他可以用于创建文档,跟踪代码中的依赖性,甚至执行基本的编译时检查,而且还可以作为在spring框架中进行注解试注入Bean属性,用来减少写配置文件。注解是以@+注解名在代码中存在的,根据注解参数的个数,我们可以将注解分为:标记注解,单值注解、完整注解三类。他们都不会直接影响到程序的语义,只是作为注解存在,可以通过反射机制编程实现对这些元数据的访问。另外,你可以在编译时选择代码哩的注释是否只存在于源代码级,或者它也能在class文件中出现。
在这里所说的元数据作用可以大致分为三类:生成文档,编译时检查,跟踪代码的依赖性,代替已有的配置文件。
2、注释的3中基本类型
a、标记注释 --没有变量,只有名称标示 例如:@annotation
b、单一值注释 --在标记注释的基础上提供一段数据。 例如:@annotation("data")
c、完整注释 --可以包括多个数据成员,每个数据成员由名称和值构成
例如:@annotation(val1="data1",val2="data2")
3、java中提供了3个内置注释类型
a、override,这个大家都不陌生,他是只能用在方法上,对方法的重写
作用:可以保证编译时Override函数的声明正确性
用法:@override
b、Deprecated 同样只能作用于方法
作用:对不应在使用的方法进行注解
用法:@Deprecated 并且他必须得和这个函数放在同一行,现在不在经常使用
c、SuperssWarnings 可以注释一段代码
作用:关闭特定的告警信息,
用法:@SuperssWarnings (value={"unchecked"})代码
这三个只是简单的注解类型
4、在java中最重要的是元注解,元注解是什么呢?
元注解是用来描述注解的注解,就是用来注解其他的注解,为什么会出现它呢,因为我们在项目中要用到自定义的注解,所以这些自定义的注解就是要用这些元注解来注解的。
要写一个自定义的注解,必须通过@interface关键字来定义,在你这个类之前,就需要通过这些个元注解来描述该注解的使用范围(@Target)、生命周期(@Retention)及其他。
a、@Target 表示该注释可以用于什么地方,可用的参数ElementType参数包括:
CONSTRUCTOR:说明此注解只能用于构造器
FIELD:说明只能用于域的声明
LOCAL_VARIABLE:局部变量声明
METHOD:方法声明
PACKAGE:包声明
PARAMETER:参数声明
TYPE:类、接口(包括注解类型)或者是enum声明
b、@Retention 表示需要在什么级别保存该注释信息。可选的RetentionPolicy参数包括
SOURCE:注释将被编译器丢掉
CLASS:注释在class文件中可用,但会被vm丢弃
RUNTIME:VM将在运行时也保障注释,因此可以通过反射机制读取注释的信息。
c、@Documented 将注释包含在JavaDoc中
d、@Inheried 允许子类继承父类中的注释。
5、写个简单的自定义注解例子:
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationName {
String value();
}
//这个注解只能用在类、接口或者是enum、方法名、之前,并且在运行时保持注解
使用这个注解:
@AnnotationName(value="test")
public class Test {
public Test(){
}
}
6、如果你要记录日志,那么这个注解中的值你可以写成你此刻运行的方法,在这里就有问题出现了,你注解到这里你怎么去读取你注解里的value值呢,
这里就用到了注解处理器,注解处理器其实就是一段处理自己编写的注解类的代码而已。并不用太多复杂的概念或者技术可以使用反射的方法去读取注解里边的值:在这里就必须将RetentionPolicy指定为RUNTIME
例子:
A、这是你执行的那个注解处理器
public class TestExcute {
public void getLog(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("****************************注解解释器执行开始*********************************");
//获取目标对象对应的类名
System.out.println("应用注解的类名:"+joinPoint.getTarget().getClass());
//获取实现类继承的接口名
@SuppressWarnings("rawtypes")
Class[] c = joinPoint.getTarget().getClass().getInterfaces();
System.out.println("注解对象所实现的接口名:"+c[0]);
//获取到了注解在这个service实现类上的annotation
Annotation[] a = joinPoint.getTarget().getClass().getAnnotations();
//获取这个类上的注解的个数
System.out.println("应用注解类上的注解个数:"+a.length);
//判断这个类上面的注释是否是AnnotationName这个自定义的注解,如果是返回这个注解,如果不是返回null
if(joinPoint.getTarget().getClass().getAnnotation(AnnotationName.class)!=null){
//获取到这个类上的注解
AnnotationName anns = joinPoint.getTarget().getClass().getAnnotation(AnnotationName.class);
//输出这个类上的注解的值
System.out.println("注释在实现类上的annotation:"+anns.value());
}
//判断这个接口上是否存在此注解
if(c[0].getAnnotation(AnnotationName.class)!=null){
AnnotationName an = (AnnotationName) c[0].getAnnotation(AnnotationName.class);
System.out.println("注解对象所实现接口上的注解值:"+an.value());
}
//获取目标对象上正在执行的方法名
String methodString = joinPoint.getSignature().getName();
System.out.println("目标对象上正在执行的方法名:"+methodString);
//获取到这个类上面的方法全名
Method meths[] = joinPoint.getSignature().getDeclaringType().getMethods();
System.out.println("方法上面的全名:"+meths[0]);
//获取到这个类上面的方法上面的注释
Annotation[] anns = meths[0].getDeclaredAnnotations();
System.out.println("正在执行方法上面的注释:"+((AnnotationName)anns[0]).value());
//让你注释的那个方法执行
joinPoint.proceed();
//完毕
System.out.println("****************************注解解释器执行完毕*********************************");
}
}
B、这个注解解释器所使用的是jdk自己的代理类生成机制,所以你所要记录的类必须是继承自一个接口,因为他这个生成的代理类可以理解为接口和你这个实现类之间的一个类,所以这个记录的类必须是要实现一个接口
接口:
@AnnotationName(value="Test1测试interface")
public interface Test1 {
@AnnotationName(value="Test1测试testAspect")
public void testAspect();
}
实现类:
@AnnotationName(value="Test测试业务Test2")
public class Test2 implements Test1{
@Override
public void testAspect() {
// TODO Auto-generated method stub
System.out.println("Test2 is excuting。。。。。。。。");
}
}
C、Action:
public class Test extends ActionSupport{
private static final long serialVersionUID = 1L;
private Test1 test2;
public Test1 getTest2() {
return test2;
}
public void setTest2(Test1 test2) {
this.test2 = test2;
}
public void getTest(){
test2.testAspect();
System.out.println("=========================执行Action======================");
}
}
D、在spring配置文件中的配置:
<!-- 打开AOP的拦截功能,proxy-target-class:是用来控制注入的时实现类还是接口,默认是false表示只能注入接口,true:表示可以用类来注入-->
<aop:aspectj-autoproxy proxy-target-class="false"/>
<aop:config>
<!-- 声明一个切面 -->
<aop:aspect id="testExcuteAspect" ref="testExcute">
<!-- 声明一个装备,是在什么时候执行日志处理 -->
<!-- 声明一个切入点,
expression: 第一个*表示:通配符,表示返回任何类型,
第二个*表示:通配符,表示这个包下的任意类
第三个*表示:通配符,表示这个类下的方法可以有0个或多个参数
(..):表示这个包下的所有子包
-->
<aop:pointcut expression="execution(* com.ckpt.logPackage.*.*(..))" id="logTest" />
<aop:around method="getLog" pointcut-ref="logTest" />
</aop:aspect>
</aop:config>
<bean id="testExcute" class="com.ckpt.logPackage.TestExcute" scope="prototype"></bean>
<bean id="test" class="com.ckpt.Action.Test" scope="prototype">
<property name="test2" ref="test2"></property>
</bean>
<bean id="test2" class="com.ckpt.logPackage.Test2" scope="prototype"></bean>
E、struts2配置文件:
<action name="test" class="test" method="getTest" >
<result type="plainText"></result>
</action>
F、发布项目,在浏览器中直接访问Action打印出日志
7、ProceedingJoinPoint :使用这个类取处理日志,只能处理环绕通知
ProceedingJoinPoint is only supported for around advice