- 背景
作业:
使用Java动态代理实现一个简单的AOP框架
定义两个注解 @Transaction ,@Log
定义一个接口Aspect,它有两个接口方法:before(); after();
实现连个Apect实现类:TransactionAspect,LogAspect;
TransactionAspect:before():输出事务开始,after():输出事务结束
LogAspect: before():输出调用前, after():输出调用后
实现测试类,这个类里有四个方法:
– 第一个方法:标有@Transaction
– 第二个方法:标有@Log
– 第三个方法:标有@Transaction 和@Log
– 第四 个方法:没有任何注个解
写main方法,使用aop框架执行测试类方法,要求:
有@Transaction 方法执行的时候, TransactionAspect的两个方法被执行;
有@Log方法执 行的时候, LogAspect的两个方法被执行。
- 代理的概念和作用
总结: 在这里代理对象存在的价值:主要用于拦截对真实业务对象的访问。
总结:
代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。
- 静态代理和动态代理
根据加载被代理类的时机不同,将代理分为静态代理和动态代理。如果我们在代码编译时就确定了被代理的类是哪一个,那么就可以直接使用静态代理;如果不能确定,那么可以使用类的动态加载机制,在代码运行期间加载被代理的类这就是动态代理,比如RPC框架和Spring AOP机制。
- 静态代理的实现
静态代理: 由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。
这里实现一个用户管理类UserManagerImp,实现接口UserManager的两个方法,添加用户addUser()和删除用户deleteUser()。也可通过第三方代理类UserManagerImpProxy(实现了同样的UserManager接口)来代理执行添加用户和删除用户的操作。
首先创建一个UserManager接口。这个接口就是被代理类(UserManagerImp)和代理类(UserManagerImpProxy)的公共接口,它们都有添加用户和删除用户的行为。
package com.glodon.hongq.day01;
public interface UserManager {
void addUser();
void delUser();
String findUser();
}
UserManagerImp实现UserManager接口:
package com.glodon.hongq.day01;
public class UserManagerImp implements UserManager{
@Override
public void addUser() {
System.out.println("UserManagerImp.addUser");
}
@Override
public void delUser() {
System.out.println("UserManagerImp.delUser");
}
@Override
public String findUser() {
return "UserManagerImp.UserName";
}
}
UserManagerImpProxy类也实现了UserManager接口,同时拥有一个UserManger引用,通过这个能够代理实现UserManager接口的任何类
package com.glodon.hongq.day01;
public class UserManagerImpProxy implements UserManager {
private UserManager userManager;
public UserManagerImpProxy(UserManager userManager){
this.userManager = userManager;
}
@Override
public void addUser() {
System.out.println("我是代理商UserManagerImpProxy");
System.out.println("start->addUser");
userManager.addUser();
System.out.println("success->addUser");
}
@Override
public void delUser() {
userManager.delUser();
}
@Override
public String findUser() {
return userManager.findUser();
}
}
测试类
package com.glodon.hongq.day01;
public class ClientProxy {
public static void main(String[] args){
UserManager u = new UserManagerImpProxy(new UserManagerImp());
// UserManagerImpProxy对象代理了UserManagerImp执行了addUser()方法
u.addUser();
}
}
运行结果:
说明:
代理模式最主要的就是有一个公共接口(UserManager),一个具体的类(UserManagerImp),一个代理类(UserManagerImpProxy),代理类持有具体类的实例,代为执行具体类实例方法。上面说到,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。这里的间接性就是指不直接调用实际对象的方法,那么我们在代理过程中就可以加上一些其他用途。就这个例子来说,在执行addUser()方法的前后都添加了相应的说明,如下图所示:
可以看到,只需要在代理类中运行addUser()之前,执行其他操作就可以了。这种操作,也是使用代理模式的一个很大的优点。最直白的就是在Spring中的面向切面编程(AOP),我们能在一个切点之前执行一些操作,在一个切点之后执行一些操作,这个切点就是一个个方法。这些方法所在类肯定就是被代理了,在代理过程中切入了一些其他操作。
以下结构图是一个典型的静态代理模式:
其中:Subject角色负责定义RealSubject和Proxy角色应该实现的接口;RealSubject角色用来真正完成业务服务功能;Proxy角色负责将自身的Request请求,调用realsubject 对应的request功能来实现业务功能,自己不真正做业务。
当在代码阶段规定这种代理关系,Proxy类通过编译器编译成class文件,当系统运行时,此class已经存在了。这种静态的代理模式固然在访问无法访问的资源,增强现有的接口业务功能方面有很大的优点,但是大量使用这种静态代理,会使我们系统内的类的规模增大,并且不易维护;并且由于Proxy和RealSubject的功能 本质上是相同的,Proxy只是起到了中介的作用,这种代理在系统中的存在,导致系统结构比较臃肿和松散。
为了解决这个问题,就有了动态地创建Proxy的想法:在运行状态中,需要代理的地方,根据Subject 和RealSubject,动态地创建一个Proxy,用完之后,就会销毁,这样就可以避免了Proxy 角色的class在系统中冗杂的问题了。
- 动态代理
动态代理:代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理。
动态代理模式的结构跟上面的静态代理模式稍微有所不同,多引入了一个InvocationHandler角色。
有上图可以看出,代理类处理的逻辑很简单:在调用某个方法前及方法后做一些额外的业务。换一种思路就是:在触发(invoke)真实角色的方法之前或者之后做一些额外的业务。那么,为了构造出具有通用性和简单性的代理类,可以将所有的触发真实角色动作交给一个触发的管理器,让这个管理器统一地管理触发。这种管理器就是Invocation Handler。
动态代理工作的基本模式就是将自己的方法功能的实现交给 InvocationHandler角色,外界对Proxy角色中的每一个方法的调用,Proxy角色都会交给InvocationHandler来处理,而InvocationHandler则调用具体对象角色的方法。如下图所示:
JDK的动态代理创建机制----通过接口
1. 获取 RealSubject上的所有接口列表;
2. 确定要生成的代理类的类名,默认为:com.sum.$ProxyXXXX ;
3. 根据需要实现的接口信息,在代码中动态创建该Proxy类的字节码;
4 . 将对应的字节码转换为对应的class 对象;
4. 创建InvocationHandler 实例handler,用来处理Proxy所有方法调用;
5. Proxy 的class对象 以创建的handler对象为参数,实例化一个proxy对象
在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。
newProxyInstance(ClassLoader loader,Class>[] interfaces,InvocationHandler h)
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序
在调用代理对象中的每一个方法时,在代码内部,都是直接调用了InvocationHandler 的invoke方法,而invoke方法根据代理类传递给自己的method参数来区分是什么方法。
invoke(Object proxy,Method method,Object[] args)
在代理实例上处理方法调用并返回结果
动态代理的实例
根据本文“背景”一栏的作业,运用Java动态代理实现简单的AOP框架。
package com.glodon.hongq.aop;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Transaction {
String value() default "Transaction";
}
package com.glodon.hongq.aop;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
String value() default "Log";
}
package com.glodon.hongq.aop;
public interface Aspect {
void before();
void after();
}
TransactionAspect:before():输出事务开始,after():输出事务结束
package com.glodon.hongq.aop;
public class TransactionAspect implements Aspect {
@Override
public void before() {
// TODO Auto-generated method stub
System.out.println("事物开始");
}
@Override
public void after() {
// TODO Auto-generated method stub
System.out.println("事物结束");
}
}
LogAspect: before():输出调用前, after():输出调用后
package com.glodon.hongq.aop;
public class LogAspect implements Aspect {
@Override
public void before() {
// TODO Auto-generated method stub
System.out.println("调用前");
}
@Override
public void after() {
// TODO Auto-generated method stub
System.out.println("调用后");
}
}
实现测试类,这个类里有四个方法:
– 第一个方法:标有@Transaction
– 第二个方法:标有@Log
– 第三个方法:标有@Transaction 和@Log
– 第四个方法:没有任何注个解
package com.glodon.hongq.aop;
public interface Test {
@Transaction()
public void method1() ;
@Log
public void method2() ;
@Transaction()
@Log
public void method3() ;
public void method4() ;
}
package com.glodon.hongq.aop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class AopTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Test t1 = new Test() {
@Transaction()
public void method1() {
System.out.println("method1");
}
@Log
public void method2() {
System.out.println("method2");
}
@Transaction()
@Log
public void method3() {
System.out.println("method3");
}
public void method4() {
System.out.println("method4");
}
};
InvocationHandler testHandler = new AopInvocationHandler(t1);
Test testProxy = (Test)Proxy.newProxyInstance(Test.class.getClassLoader(), new Class>[] {Test.class}, testHandler);
testProxy.method1();
testProxy.method2();
testProxy.method3();
testProxy.method4();
}
}
- 总结
上面的动态代理的例子,其实就是AOP的一个简单实现了,在目标对象的方法的注解进行识别判断,并通过注解的不同执行相应的操作。Spring的AOP实现其实也是用了Proxy和InvocationHandler这两个东西的。
- 参考资料
Java动态代理机制详解(JDK 和CGLIB,Javassist,ASM)