Part I

我并不是在卖弄自己的英语有多少的了不起,只不过对Annotation这一次的解释真的很懊恼,“注解”和“注释”这两个对Annotation的翻译我听着不爽,所以全文都用Annotation来表示。

Part II

相信Java的开发人员对Annotation这个名词一定是非常的熟悉了,如今许多优秀的开源框架,都会提供了Annotation的支持。如Spring、Hibernate、JUnit4等。但是这又是为什么那么多的程序员都热衷于Annotation的使用呢?我个人的原因是因为他确实的简化了我们的操作,虽然这样做使得代码和配置的分离难以实现。

Part III

下面我们就用一个权限控制的例子来说明一下,如何使用Annotation来简化我们的开发

预期功能:

1. 对于每个用户都设定一个对应的权限。

2. 每个Dao的操作都加入对权限的检查。权限不足则抛出安全异常。

思考:

1. Dao层的方法只关心Dao的操作,对于权限的检查则不需要关心。因此我们可以用AOP来实现对权限的检查(在Java中使用动态代理来实现),实现权限检查和Dao操作的解耦。

2. 每个用户都要有相应的权限,而且每个用户的操作都是在不同的线程上进行,因为我们必须要提供一个用户的权限上下文(RoleContext)来提供对权限的设置和获取。

3. 对于Dao层的实现可以采用面向接口的编码方式,实现各层之间的解耦。由于每个Dao层所对应的实现类只有一个,因此,我们可以把实现类的信息作为元数据写入Dao接口中,所以这里最适合用Annotation来实现。

4. Dao层的方法所需要的权限信息与实现无关,因此这里也可以把权限的信息作为方法的元数据写入,所以这里也十分适合用Annotation来实现。

Part IV

首先我们把项目基本的架子搭建:

package com.gzmu.annotation.dao;
public interface BaseDao { }
package com.gzmu.annotation.dao;
import com.gzmu.annotation.annotation.Implement;
import com.gzmu.annotation.annotation.Permission;
import com.gzmu.annotation.dao.impl.UserDaoImpl;
import com.gzmu.annotation.util.Role;
@Implement(UserDaoImpl.class)
public interface UserDao extends BaseDao {

	@Permission({Role.ADMINISTRATOR, Role.SYSTEM})
	void save();

	@Permission(Role.SYSTEM)
	void delete();

	@Permission({Role.USER, Role.ADMINISTRATOR, Role.SYSTEM})
	void query();

}
package com.gzmu.annotation.dao.impl;
import com.gzmu.annotation.dao.UserDao;
public class UserDaoImpl implements UserDao {

	@Override
	public void save() {
		System.out.println("UserDaoImpl.save()");
	}

	@Override
	public void delete() {
		System.out.println("UserDaoImpl.delete()");
	}

	@Override
	public void query() {
		System.out.println("UserDaoImpl.query()");
	}

}

RoleContext作为一个提供用户权限上下文的单元存在,使用枚举来实现单例模式,ThreadLocal提供了对当前线程权限数据的访问。

package com.gzmu.annotation.context;
import com.gzmu.annotation.util.Role;
public enum RoleContext {

	INSTANCE;

	private ThreadLocal<Role> role = new ThreadLocal<Role>();

	public Role getCurrentRole() {
		return role.get();
	}

	public void setCurrentRole(Role role) {
		this.role.set(role);
	}

}

Implment用来指定Dao接口对应的实现类。

package com.gzmu.annotation.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.gzmu.annotation.dao.BaseDao;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Implement {

	Class<? extends BaseDao> value();

}

Permission用于指定Dao层的方法的可访问的人员的访问权限。

package com.gzmu.annotation.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.gzmu.annotation.util.Role;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Permission {

	Role[] value();

}

到这里,这个基本的架子就搭建完成了。接下来,我们就要开始使用动态代理、反射以及Annotation来实现对权限的检查。

Part V

下面我们就要详细的解释一下以下的代码:

DaoProxyFactory.newRoleDaoProxy():

1. 我们提供一个简单的工厂,用于生产一个代理对象。传入一个需要代理的接口,用于产生实现该接口的代理对象。

2. 由于我们的接口上使用Implement这个Annotation来指定这个接口所对应的实现类,所以我们可以获取这个实现类会创建一个实际被代理的对象。

RoleInvocationHandler

1. 顾名思义,这个类就是用来做权限控制的,这个类实现了InvocationHandler。

2. 因为我们已经在接口上定义了哪些方法对应哪些被允许执行这个方法的权限,因此我们可以通过method.getAnnotation(Permission.class)这个方法来获得权限的信息。

3. 迭代方法的允许权限,并与当前线程用户的权限做比较,如果发现两者相等,说明当前用户的权限与方法执行的权限一致,因此跳出循环,执行outter标签后面的方法,允许用户执行。

4. 迭代完成后,当前线程用户的权限没有与方法中定义的权限一致,说明用户无权执行这样的操作,因此跑出安全异常。

package com.gzmu.annotation.util;
import java.lang.annotation.AnnotationFormatError;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.gzmu.annotation.annotation.Implement;
import com.gzmu.annotation.annotation.Permission;
import com.gzmu.annotation.context.RoleContext;
import com.gzmu.annotation.dao.BaseDao;
public abstract class DaoProxyFactory {

	@SuppressWarnings("unchecked")
	public static <T> T newRoleDaoProxy(Class<T> dao) {
		Implement implAnnotation = dao.getAnnotation(Implement.class);

		if (implAnnotation == null)
			throw new AnnotationFormatError("该接口未定义实现类的注解");

		BaseDao implClass = null;
		try {
			implClass = implAnnotation.value().newInstance();
		} catch (Exception e) {
			throw new RuntimeException("该接口所定义的实现类不能被实例化", e);
		}

		return (T) Proxy.newProxyInstance(
				DaoProxyFactory.class.getClassLoader(),
				new Class<?>[] { dao },
				new RoleInvocationHandler(implClass)
		);
	}

	private static final class RoleInvocationHandler implements InvocationHandler {
		private BaseDao target;

		public RoleInvocationHandler(BaseDao target) {
			this.target = target;
		}

		@Override
		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
			Permission permitAnnotation = method.getAnnotation(Permission.class);

			outter:
			if (permitAnnotation != null) {
				Role currentRole = RoleContext.INSTANCE.getCurrentRole();
				for (Role permitRole : permitAnnotation.value()) {
					if (permitRole.equals(currentRole))
						break outter;
				}
				throw new SecurityException("当前用户不允许执行此操作");
			}

			return method.invoke(target, args);
		}

	}

}

Part VI

通过这个例子,我们可以看到,用Annotation来简化我们的开发是如此的简单,世界是如此的美好。很多的程序员都觉得学习Annotation是一种负担,或者说XML可以完全取代Annotation的存在。但是我认为,一个事物的存在,必然有他的价值,没有任何的一个事物是能够完全取代另外一个事物。与其在作无谓的争论,不如花时间去研究如何更好的利用?而且Annotation的队伍这个在不断的壮大,这就是一种最好的证明。

原文地址:http://www.verydemo.com/demo_c89_i223660.html

JavaAnnotation和反射简化开发的更多相关文章

  1. Struts1应用、实现简单计算器、使用DispatchAction、显示友好的报错信息、使用动态Form简化开发

    实现简单的支持加.减.乘.除的计算器 复制一份Struts1Demo修改:Struts1Calc 方案1: Struts1Calc 创建ActionForm: CalcForm extends Act ...

  2. Struts1基础、使用Struts实现登录、使用Struts HTML标签简化开发

    Struts 1基础 为什么重拾Struts 1 曾经是最主流的MVC框架 市场份额依然很大 很多遗留系统中依旧使用 维护和升级都需要熟悉Struts 1 与Struts 2相比 编码.配置繁琐 侵入 ...

  3. JAVA框架 Spring 调用jdbcsuport简化开发

    一)使用DAO的jdbcsuport来简化开发 首先来清楚一个概念: 我们在进行配置文件来进行依赖注入的时候,主要是通过set方法来进行设置的. 正常我们使用spring的jdbctemplate的时 ...

  4. 搭建事务管理转账案例的环境(强调:简化开发,以后DAO可以继承JdbcDaoSupport类)

    1. 步骤一:创建WEB工程,引入需要的jar包 * IOC的6个包 * AOP的4个包 * C3P0的1个包 * MySQL的驱动包 * JDBC目标2个包 * 整合JUnit测试包2.步骤二:创建 ...

  5. 如何利用反射简化Servlet操作

    如何利用反射简化Servlet操作   一.反射的实现 新建类BaseServlet,继承HttpServlet(不需要在web.xml文件中配置) 1.在doPost()方法中处理请求乱码,并调用d ...

  6. Hibernate 使用MyEclipse简化开发

    在平时开发中写配置文件比较繁琐,在这里写一下如何使用myEclipse简化开发. 1.打开MyEclipse,创建数据库连接 单机测试连接按钮,如果出现成功建立连接,则连接成功. 然后Finish 2 ...

  7. 20个简化开发任务的 JavaScript库

    所谓JavaScript库就是预先写好的可以简化基于JavaScript的应用程序开发的,尤其是Ajax和其它以web为中心的技术的 JavaScript代码集.JavaScript主要用于写内嵌于H ...

  8. 简化开发:Lombok的使用

    Java中优雅的使用Lombok 1.简介 Lombok 是一种 Java实用工具,可用来帮助开发人员消除Java的冗长,尤其是对于简单的Java对象(POJO), 它通过注释实现这一目的.一个标准的 ...

  9. 基于Extjs+SpringMVC+MyBatis+Oracle的B/S信息系统简化开发思路

    要在上层简化就得有下层强大的架构作为支撑,通过采用企业级的各种框架,虽然学习成本高一些,但用好了效率也自然高. 数据层简化: 首先所有表都有名称为ID的主键字段.有与表同名的序列作为自增key. 数据 ...

随机推荐

  1. Angular JS 学习之简介

    1.Angular JS是一个JavaScript框架,它是一个以JavaScript编写的库,它可以通过<script>标签添加到HTML页面: <script src=" ...

  2. Codeforces Round #354 (Div. 2)-D

    D. Theseus and labyrinth 题目链接:http://codeforces.com/contest/676/problem/D Theseus has just arrived t ...

  3. [转]Tangram框架应用开发的一般模式

    //转的,怕想百度博客一样搬家,赶紧先复制下来. 框架其实就是一种开发模式,用tangram框架开发应用程序意味着选择一种面向接口.模块化的开发方式.这和传统的Delphi应用程序开发方式有一定区别, ...

  4. 【Clr in c#】泛型

    使用泛型的好处是“代码重用”,极大的提高了开发效率,泛型为开发者提供了以下优势: 1,源代码保护  算法的源代码不需要提供给使用泛型算法的开发人员,使用c++模板的泛型技术需要提供.(目前c++模板的 ...

  5. 【CLR Via C#】第5章 基元类型、引用类型、值类型

    第二遍看这本书,决定记录一下加深印象. 1,基元类型 什么事基元类型?基元类型是直接映射到FrameWork类库(FCL)中存在的类型,编译器直接支持的数据类型.比如int直接映射到System.In ...

  6. [POJ1015]Jury Compromise

    题目大意:要求你从n个人中选出m个,每个人有两个值p[i],D[i],要求选出的人p总和与D总和的差值最小.若有相同解,则输出p总+D总最大的方案. 动态规划. 一直在想到底是n枚举外面还是m放外面, ...

  7. Hive0.11安装配置学习笔记

    转:http://springsfeng.iteye.com/blog/1734517 1 . 首先请安装好MySQL并修改root账户密码,使用root账户执行下面命令:  su - root    ...

  8. [转]shell 变量替换

    转自:http://blog.csdn.net/xuhongning/article/details/6191515 1,参数替换: 不含有“:”的,只要定义了,就生效,不管是否为空 含有“:”的,即 ...

  9. JS 用sort方法排序字符串

    JavaScript提供了一种更简便的方法用于比较两个字符串——localeCompare(),localeCompare()使用本地特定的顺序来比较两个字符串,语法如下:string.localeC ...

  10. VS2010 C++环境下DLL和LIB文件目录及名称修改

    VS2010 C++环境下DLL和LIB文件目录及名称修改 转自:http://blog.csdn.net/archielau/article/details/8507581 DLL工程,Debug版 ...