的对象范围,这里,的作用范围是类、接口(包括注解类型) 或enum @Retention(RetentionPolicy.RUNTIME) //自定义注解的有效期,Runtime:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在 @Documented //标注生成javadoc的时候是否会被记录 public @interface EasyExceptionResult { }
@Target
表明该注解可以应用的java元素类型
Target类型 | 描述 |
| ElementType.TYPE | 应用于类、接口(包括注解类型)、枚举 |
| ElementType.FIELD | 应用于属性(包括枚举中的常量) |
| ElementType.METHOD | 应用于方法 |
| ElementType.PARAMETER | 应用于方法的形参 |
| ElementType.CONSTRUCTOR | 应用于构造函数 |
| ElementType.LOCAL_VARIABLE | 应用于局部变量 |
| ElementType.ANNOTATION_TYPE | 应用于注解类型 |
| ElementType.PACKAGE | 应用于包 |
| ElementType.TYPE_PARAMETER | 1.8版本新增,应用于类型变量) |
| ElementType.TYPE_USE | 1.8版本新增,应用于任何使用类型的语句中(例如声明语句、泛型和强制转换语句中的类型) |
@Retention
表明该注解的生命周期
| 生命周期类型 | 描述 |
| RetentionPolicy.SOURCE | 编译时被丢弃,不包含在类文件中 |
| RetentionPolicy.CLASS | JVM加载时被丢弃,包含在类文件中,默认值 |
| RetentionPolicy.RUNTIME | 由JVM 加载,包含在类文件中,在运行时可以被获取到 |
@Document
表明该注解标记的元素可以被Javadoc 或类似的工具文档化
@Inherited
表明使用了@Inherited注解的注解,所标记的类的子类也会拥有这个注解
在我们定义好注解之后就需要考虑如何将注解和类绑定到一起,在运行期间达到我们想要的效果,这里就可以引入动态代理的机制,将注解想要做的操作在方法执行前,类编译时就进行一个织入的操作如下。
public static void main(String[] args) {
Class easyServiceImplClass=EasyServiceImpl.class;
//判断该对象是否有我们自定义的@EasyExceptionResult注解
if(easyServiceImplClass.isAnnotationPresent(EasyExceptionResult.class)){
final EasyServiceImpl easyService=new EasyServiceImpl();
//cglib的字节码加强器
Enhancer enhancer=new Enhancer();
将目标对象所在的类作为Enhaner类的父类
enhancer.setSuperclass(EasyServiceImpl.class);
通过实现MethodInterceptor实现方法回调,MethodInterceptor继承了Callback
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
try{
method.invoke(easyService, args);
System.out.println("事务结束...");
}catch (Exception e){
System.out.println("发生异常了");
}
return proxy;
}
});
Object obj= enhancer.create();;
EasyServiceImpl easyServiceProxy=(EasyServiceImpl)obj;
easyServiceProxy.testEasyResult();
}
}运行效果:

public class EasyServiceImplProxy implements InvocationHandler {
private EasyServiceImpl target;
public void setTarget(EasyServiceImpl target)
{
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 这里可以做增强
System.out.println("已经是代理类啦");
try{
return method.invoke(proxy, args);
}catch (Exception e){
System.out.println("发生异常了");
return null;
}
}
/**
* 生成代理类
* @return 代理类
*/
public Object CreatProxyedObj()
{
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
}java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
如何强制使用CGLIB实现AOP?
(1)添加CGLIB库,SPRING_HOME/cglib/*.jar
(2)在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>
JDK动态代理和CGLIB字节码生成的区别?
(1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类
(2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
因为是继承,所以该类或方法最好不要声明成final
@ExceptionHandler注解的使用可参考文章java中优雅的参数校验方法_WX5991的博客-CSDN博客
至此自定义注解就结束了,下章再总结一下Spring中的Aop,拦截器,过滤器。