注: 本文只是原理性质, 并不实用, 读者可用成熟稳定的开源权限系统如SpringSecurity. 但可参考实现自己的一些小框架.
在上课的时候, 一位同学拿着一篇JavaEye的很长的CGLIB讲解代码来问我这是怎么回事?我看了下, 觉得那样的例子实在太让人难以一下子看懂了, 于是就自己重造了个轮子.
Spring 2.5最大的亮点就是基于注解实现配置, 其实这个谈不上什么亮点, EJB3/JPA早就实现了, 而且在原理上也只是利用反射里面的method.getAnnotation(MyAnnotaion.class)即可.
第二个一直大张旗鼓吹捧的就是AOP, 其实AOP就是个JDK接口方法拦截器/子类增强, 不过Spring加了个配置文件封装, 而且和它的一贯作风一致, 都是集成第三方的框架, 这里基于类增强的是CGLIB.
CGLIB是什么呢?
cglib is a powerful, high performance and quality Code Generation Library, It is used to extend JAVA classes and implements interfaces at runtime. See samples and API documentation to learn more about features.
This library is free software, freely reusable for personal or commercial purposes.
cglib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java 接口. 而CGLIB本身又用了ASM框架, 来避免直接解析字节码.
OK, 废话少说, 我们来通过一个例子看看基于注解和AOP的权限系统(本代码只需要CGLIB及其依赖类库ASM, 或者直接下载cglib-nodep-2.2.jar http://sourceforge.net/project/showfiles.php?group_id=56933&package_id=98218&release_id=601998).
先看业务层:
public class MyClass {
private String role;// 运行时角色
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
@Role(name="admin")
public String adminMethod(String name) {
System.out.println("MyClass.adminMethod()" + name);
return "adminMethod 结果";
}
@Role(name="guest")
public String guestMethod(String name) {
System.out.println("MyClass.guestMethod()" + name);
return "guestMethod 结果";
}
}
基本上就是普通的方法加上了角色注解, 当然注解类也是我们自己写的:
import java.lang.annotation.*;
@Documented
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD})//指定目标, 必须包含方法
@Retention(RetentionPolicy.RUNTIME)//设置保持性
@Inherited
public @interface Role {
String name();
}
所有的Magic都在后台通过反射和CGLIB的方法拦截器来实现, 先通过JDK本身的反射来检查注解, 然后读取注解的值, 随后进行判断并决定是否执行此方法, 如果中间加上Spring容器, 那就是实现了所谓的专门的权限控制Bean了. 代码:
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.MethodInterceptor;
public class Main {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyClass.class);
enhancer.setCallback( new MethodInterceptorImpl() );
MyClass my = (MyClass)enhancer.create();
my.setRole("admin");
System.out.println(my.adminMethod("名字"));// 这一行将会执行通过
System.out.println(my.guestMethod("名字"));// 这一行将会执行失败
/* --> new 父类 --> obj -->
* MyClass_proxy
* MyClass obj = new MyClass();// 本来父类
* MethodInterceptor interceptor;
public String method(String name) {
Object value = interceptor.intercept(obj, ojb.getClass().getMethod("method"), new Object[] {name},
proxy);
if(value != null) {
return value;
}
return obj.method(name);
}
*/
}
private static class MethodInterceptorImpl implements MethodInterceptor {
// obj => 父类的实例, method是被调用的方法, args = {String name}, proxy 专门执行方法的工具类
public Object intercept(Object obj,
Method method,
Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("即将调用方法" + method);
// 检查是否有Role注解 @Role(name="角色名")
Role role = method.getAnnotation(Role.class);
if(role != null) {
// 有注解
System.out.println("发现方法上" +method.getName() + " 有一个注解 Role, 它的值是" + role.name());
// 把被调用对象类型还原
MyClass my = (MyClass)obj;
if(!role.name().equals(my.getRole())) {
System.err.println("对不起, 您的权限不足, 不能调用此方法!");
throw new Exception("对不起, 您的权限不足, 不能调用此方法!");
}
else {
// ojb ==> MyClass my
// Before
Object value = proxy.invokeSuper(obj, args);// 正在调用方法
// invoke 等价于 String return = obj.method(args);
System.out.println("已经调用了方法, 原本应该返回的值:" + value);
// After
return value;
}
} else {
System.out.println("发现方法" +method.getName() + " 没有角色限制");
Object value = proxy.invokeSuper(obj, args);
return value;
}
}
}
class A {
String a() {
return "";
}
}
class A_proxy extends A {
A a = new A();
String a() {
String oldValue = a.a();
return oldValue;
}
}
}
另外注解其实是支持数组方式的变量定义的, 那样的话就可以写成:
@Role(name = {"guest", "admin"})
代码末尾的类A和A_proxy试图说明代理这种模式的实现方式. OK, 运行代码, 应该可以得到我们期望的结果了. admin方法执行成功, 而另一个则失败了.
运行输出:
即将调用方法public void MyClass.setRole(java.lang.String)
发现方法setRole 没有角色限制
即将调用方法public java.lang.String MyClass.adminMethod(java.lang.String)
发现方法上adminMethod 有一个注解 Role, 它的值是admin
即将调用方法public java.lang.String MyClass.getRole()
发现方法getRole 没有角色限制
MyClass.adminMethod()名字
已经调用了方法, 原本应该返回的值:adminMethod 结果
adminMethod 结果
即将调用方法public java.lang.String MyClass.guestMethod(java.lang.String)
发现方法上guestMethod 有一个注解 Role, 它的值是guest
即将调用方法public java.lang.String MyClass.getRole()
发现方法getRole 没有角色限制
对不起, 您的权限不足, 不能调用此方法!
Exception in thread "main" java.lang.Exception: 对不起, 您的权限不足, 不能调用此方法!
at Main$MethodInterceptorImpl.intercept(Main.java:60)
at MyClass$$EnhancerByCGLIB$$e6aea994.guestMethod(<generated>)
at Main.main(Main.java:20)
小结: 基于类的权限系统可通过CGLIB/AOP实现; 基于URL的权限系统可通过过滤器实现. 其实, Servlet的Filter也是AOP的一种基于Web的实现.
后记: 最近网上有一种很普遍的论调, 当大家说XX不好的时候, 有人说XX不行, 有本事你也去跑啊, 去导啊, 不行你就闭嘴. 我想这是一种转移话题的谬论, 举个例子: 如果病人被庸医给看死了, 他的家人是不是还不能说这医生治的不好, 不应该追究责任, 因为他们不会看病啊? 吃菜的不需要会做菜, 但是吃菜的有权利评价菜是否好吃, 所以真正应该闭嘴的是说这种话的人. 所以我们评论框架好坏的时候, 有人就说了: 你说XXX框架不好, 那你有本事也自己去做一个啊; 这是相当荒谬的跑题的论调.
分享到:
相关推荐
通过对这个简单的AOP拦截器实现的学习,我们可以进一步探索如何结合注解驱动的AOP、环绕通知(`Around Advice`)、代理模式的实现细节、以及如何在实际项目中利用AOP解决实际问题。AOP是Spring框架的强大工具,理解...
Spring提供了两种主要的AOP实现方式:基于代理(Proxy-based)和基于注解(Annotation-based)。 - **基于代理的AOP**:Spring使用JDK动态代理或CGLIB动态代理创建目标对象的代理,代理对象在调用目标方法前后执行...
在本项目中,我们将探讨如何实现Spring AOP的各个组件,包括自定义注解的使用以及不使用自定义注解的情况。 首先,让我们理解AOP的基本概念。AOP允许程序员定义“方面”,这些方面封装了关注点,比如日志、性能度量...
- **struts2-core-2.1.6.jar**:这是Struts2的核心库,提供了框架的主要功能,如请求处理、拦截器等。 - **commons-fileupload-1.2.1.jar**:用于处理HTTP文件上传。 - **commons-io-2.4.jar**:提供了对文件输入...
通过@Pointcut定义切入点,配合@annotation注解用于匹配标注了特定注解的方法,@Around注解可以实现环绕通知,即在目标方法执行前后进行逻辑处理。 整个AOP使用过程实际上是开发者定义好的通知逻辑与业务逻辑的无缝...
- **配置DispatcherServlet**:配置前端控制器,负责拦截所有请求并将请求分发给相应的控制器处理。 - **定义控制器类**:使用`@Controller`注解标注控制器类,通过`@RequestMapping`注解映射URL。 - **定义服务...
`MethodBeforeAdviceInterceptor`、`AfterReturningAdviceInterceptor`等类实现了不同类型的拦截器。 十、实战分析 导入Spring AOP源码到Eclipse工程后,可以通过调试和阅读源码,了解通知的创建、切点的匹配、代理...
当事务方法被调用时,拦截器会检查方法上的`@Transactional`注解,根据其属性启动或加入合适的事务。 最后,Spring还支持自定义注解。你可以创建自己的注解,并使用`@ComponentScan`的`@Annotation`参数告诉Spring...
- **JoinPoint**:这是AOP中的一个关键概念,指的是应用程序中的特定点,如方法调用、构造器调用等,这些点可以被拦截并插入额外的行为。 - **方法调用(Method Call)**:当一个方法被调用的瞬间。 - **方法执行...
例如,当我们自定义一个拦截器时,通常会实现`MethodInterceptor`接口,并通过Spring的配置文件或者注解将其注册到Spring容器中,从而实现对目标方法的拦截和增强。 此外,压缩包中包含的`springframework-license....
- 配置拦截器等。 4. **整合测试**: - 编写简单的增删改查操作来测试整合是否成功。 通过以上步骤,可以实现Struts2、Spring和Hibernate的整合,为开发复杂的Java Web应用提供强大的技术支持。
3. **MethodInterceptor(方法拦截器)**: 使用`MethodInterceptor`,我们可以自定义拦截逻辑,决定在哪些时机执行额外功能,如在方法前后或只在特定条件下执行。通过实现`InvocationHandler`接口并重写`invoke()`...
在Spring的XML配置文件中,可以在`<tx:annotation-driven>`元素下声明事务管理器,使得带有`@Transactional`注解的方法能够被事务管理。 ```xml <aop:config> <aop:pointcut id="userServiceMethods" expression=...
this.advised.getInterceptorsAndDynamicInterceptionAdvice 获取的是当前目标方法对应的拦截器,这里是根据之前获取到的切面来获取相应的拦截器。如果获取不到拦截器,则不会创建 MethodInvocation,直接调用目标...
- **方法二**:使用CGLIB实现事务管理,以解决某些情况下无法保存数据的问题。 - **Spring相关的参考资料** - 官方文档:[https://docs.spring.io/spring-framework/docs/current/reference/html/]...
3. **代理机制**: 代理机制是Spring实现声明式事务的关键,它通过AOP(面向切面编程)创建事务拦截器。Spring支持两种代理模式:JDK动态代理和CGLIB代理。JDK代理适用于实现了接口的类,而CGLIB代理则用于未实现接口...
然而,这只是基础配置,实际开发中,你可能还需要配置数据源、事务管理、拦截器、消息源、异常处理等更复杂的功能。随着对Spring MVC理解的深入,你将能够灵活地应对各种Web开发需求。学习和掌握Spring MVC,不仅...
### Java常用框架学习笔记 #### Hibernate ##### 1.1 Hibernate 实现数据库操作步骤 - **导入所需的Jar包**:为了使用Hibernate框架,首先需要在项目中导入Hibernate库。这通常意味着添加一系列与Hibernate相关的...
通知可以被模型化为拦截器,并在连接点周围形成一个拦截器链。 织入(`Weaving`)是将切面与其他应用类型或对象连接的过程,创建出被通知的对象。Spring AOP在运行时完成织入,通过JDK动态代理或CGLIB代理。 目标...
Spring AOP有两种实现方式:基于代理(proxy-based)和基于注解(annotation-driven)。基于代理的AOP通过JDK动态代理或CGLIB生成目标对象的代理,拦截并执行通知。注解驱动的AOP则是利用Spring的`@Aspect`注解来...