求高人来指点一下?我实在没搞明白
本帖最后由 xingshen100 于 2013-12-24 21:18:16 编辑 这个问题核心就是关于元注解@Inherited()的。
这里有篇关于我探究的关于@Inherited()的文章:http://blog.csdn.net/xingshen100/article/details/17506011
试验结论是:对于使用元注解@Inherited()修饰的自定义注解。作用在类上的自定义注解可以被继承下来。作用在接口上自定义注解不能被实现它的类继承下来。
类和接口中方法上的自定义注解不能被重写/实现了其方法的子类继承(就是说,接口因为被子类实现的时候,方法必被实现,所以其方法上的自定义注解必定不能被继承。而子类如果没有重写父类的方法的话,子类就可以继承父类方法上的自定义注解)。
这几篇代码主要目的就是通过动态代理来实现权限拦截的。
但是我在一个小工程中却发现在动态代理中却能检测到实现了接口的子类能够继承接口中方法上的自定义注解。我感觉这与我试验中的结论相悖。求高人指点???现将几篇相关代码贴在下面:
1.自定义注解:
package cn.it.utils;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited //代表注解是可以被继承的。
public @interface UserPrivilegeInfo {
public String privilege(); // 它代表的是权限.
}
package cn.it.service;
import cn.it.domain.User;
import cn.it.utils.UserPrivilegeInfo;
public interface BookService {
@UserPrivilegeInfo(privilege = "修改图书")
public void update(User user) throws Exception;
@UserPrivilegeInfo(privilege = "查看图书")
public void find(User user) throws Exception;
@UserPrivilegeInfo(privilege = "删除图书")
public void delete(User user) throws Exception;
@UserPrivilegeInfo(privilege = "添加图书")
public void add(User user) throws Exception;
}
package cn.it.service;
import cn.it.dao.BookDao;
import cn.it.domain.User;
import cn.it.utils.UserPrivilegeInfo;
public class BookServiceImpl implements BookService {
public void delete(User user) {
// 1.得到这个方法上的注解.
// 2.得到当前操作这个方法的用户。
// 3.查询数据库,判断用户具有什么权限,与注解上的权限比较。
BookDao dao = new BookDao();
dao.delete();
}
public void update(User user) {
BookDao dao = new BookDao();
dao.update();
}
public void find(User user) {
BookDao dao = new BookDao();
dao.find();
}
public void add(User user) {
BookDao dao = new BookDao();
dao.add();
}
}
package cn.it.service.factory;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;
import javax.persistence.Temporal;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ColumnListHandler;
import org.junit.Test;
import cn.it.domain.User;
import cn.it.service.BookService;
import cn.it.service.BookServiceImpl;
import cn.it.utils.DataSourceUtils;
import cn.it.utils.UserPrivilegeInfo;
public class BookServiceFactory {
private static BookService service = new BookServiceImpl();
// 提供一个静态的方法,可以获取BookService对象.
@Test
public static BookService getBookService() {
BookService bs = (BookService) Proxy.newProxyInstance(service
.getClass().getClassLoader(), service.getClass()
.getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 得到注解上的权限名称.
if (method.isAnnotationPresent(UserPrivilegeInfo.class)) {
// 得到注解的属性值.
String privilegeName = method.getAnnotation(
UserPrivilegeInfo.class).privilege();
System.out.println(method.getName() + "这个方法它要执行,需要权限"
+ privilegeName);
// 得到当前method方法被执行user对象.
User user = (User) args[0];
// 在数据库中查找当前用户具有的权限.
String sql = "select privileges.name from users,privileges,userprivilege where users.id=userprivilege.user_id and privileges.id=userprivilege.privilege_id and users.id=?;";
QueryRunner runner = new QueryRunner(DataSourceUtils
.getDataSource());
List<Object> ps = runner.query(sql,
new ColumnListHandler(), user.getId());
if (ps.contains(privilegeName)) {
return method.invoke(service, args);
} else {
throw new RuntimeException("权限不足");
}
}
return method.invoke(service, args);
}
});
return bs;
}
}
[解决办法]
仔细看了LZ的博客,思考了下,那个注解之所以在Method层面不行,是因为对父类或接口的重写方法时,并没有手动加注解(因为java中的重写,只是针对方法本身,注解是后来出现的东西,并不会影响重写方法合法性判断上)。如果想要保留,需要在子类方法中明确。
对于不重写的方法,其本质上是使用的父类的方法,因此注解保留是理所当然的。
至于代理注解,恰好最近也用到了。实际上代理最终会通过一个类似$Proxy0的这样一个类实现代理功能的。我想,jvm在生成$Proxy0这个类时,是严格拷贝了其接口对应的方法定义(你即使去掉Inherited注解,代理依然会传递接口中的注解)。
假设存在接口:
public interface MyIntf{
@myannotation("abc")
void test();
}
Proxy生成$Proxy0的时候,也会严格将其实现接口的注解继承下来:
class $Proxy0 extends Proxy implements MyIntf{
@myannotation("abc")
void test(){}
}