静态代理的实现:
1、接口代码:
public interface IHello {
/**
* 业务方法
* @param str
*/
void sayHello(String str);
}
2、目标类代码:
public class Hello implements IHello{
@Override
public void sayHello(String str) {
System.out.println("hello"+str);
}
}
3、代理类代码,在方法开始前后执行特定的方法:
public class ProxyHello implements IHello{
private IHello hello;
public ProxyHello(IHello hello) {
super();
this.hello = hello;
}
@Override
public void sayHello(String str) {
System.out.println("start"+str);// 添加特定的方法
hello.sayHello(str);
System.out.println("end"+str);
}
}
4、测试代码:
public class Test {
public static void main(String[] args) {
IHello hello = new ProxyHello(new Hello());能,则使用代理类
hello.sayHello("明天");
}
}
这样会存在一个问题:如果我们像Hello这样的类很多,那么,我们是不是要去写很多个HelloProxy这样的类呢。其实也是一种很麻烦的事。在jdk1.3以后,jdk跟我们提供了一个API java.lang.reflect.InvocationHandler的类, 这个类可以让我们在JVM调用某个类的方法时动态的为些方法做些什么事。下面我们就来实现动态代理的实现。
JDK实现动态代理
动态代理实现主要是实现InvocationHandler,并且将目标对象注入到代理对象中,利用反射机制来执行目标对象的方法。
接口实现与静态代理相同
1、代理类代码:
public class DynaProxyHello implements InvocationHandler{
private Object target;//目标对象
/**
* 通过反射来实例化目标对象
* @param object
* @return
*/
public Object bind(Object object){
this.target = object;
return Proxy.newProxyInstance(this.target.getClass().getClassLoader(),this.target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.println("start"+str);//添加额外的方法
//通过反射机制来运行目标对象的方法
result = method.invoke(this.target,args);
System.out.println("end"+str);
return result;
}
}
2、测试类代码:
public class DynaTest {
public static void main(String[] args) {
IHello hello = (IHello) new DynaProxyHello().bind(new Hello());
hello.sayHello("明天");
}
}
通过上面例子,可以发现通过动态代理和反射技术,已经基本实现了AOP的功能,如果我们只需要在方法执行前进行一些操作,则可以不实现打印end方法,这样就可以控制打印的时机了。如果我们想让指定的方法打印日志,我们只需要在invoke()方法中加一个对method名字的判断,method的名字可以写在xml文件中,这样我们就可以实现以配置文件进行解耦了,这样我们就实现了一个简单的spring aop框架。
当然我们以后写代码都是基于Aspect,直接写注解的,当然这并不代表这我们不需要知道AOP底层的实现原理,至于注解用的是哪种实现方式,使用配置来解决的,这里就不详解了。
讲完了动态代理,接下来通过代理模式看一些事务的机制:
public class TxHandler implements InvocationHandler {
private Object originalObject;
public Object bind(Object obj) {
this.originalObject = obj;
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
if (!method.getName().startsWith("save")) {
UserTransaction tx = null;
try
{
tx = (UserTransaction) (new InitialContext().lookup("java/tx"));
result = method.invoke(originalObject, args);
tx.commit();
}
catch (Exception ex) {
if (null != tx) {
try
{
tx.rollback();
}catch (Exception e) {
}
}
}
} else {
result = method.invoke(originalObject, args);
}
return result;}
}
首先来看一下这段代码:
return Proxy.newProxyInstance( obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);
方法根据传入的接口类型 (obj.getClass.getInterfaces()))动态构造一个代理类实例返回,这也说明了为什么动态代理实现要求其所代理的对象一定要实现 一个接口。这个代理类实例在内存中是动态构造的,它实现了传入的接口列表中所包含的所有接口。
再看:
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
……
result = method.invoke(originalObject, args);
……
return result;
}
InvocationHandler.invoke方法将在被代理类的方法被调用之前触发。通过这个方法,我们可以在被代理类方法调用的前后进行一些处 理,如代码中所示,InvocationHandler.invoke方法的参数中传递了当前被调用的方法(Method),以及被调用方法的参数。同 时,可以通过method.invoke方法调用被代理类的原始方法实现。这样就可以在被代理类的方法调用前后写入任何想要进行的操作。
CGLIB动态代理
CGLIB(Code Generation Library)是一个高性能开源的代码生成包,采用非常底层的字节码技术,对指定的目标类生成一个子类,并对子类进行增强。在Spring Core包中已经集成了CGLIB所需要的JAR包,不需要另外导入JAR包。
以下是CGlib动态代理实现:
1.创建目标类
在src目录下,创建一个dynamic.cglib包,在该包中创建目标类TestDao,该类不需要实现任何接口。
package dynamic.cglib;
public class TestDao {
public void save() {
System.out.println("保存");
}
public void modify() {
System.out.println("修改");
}
public void delete() {
System.out.println("删除");
}
}
2.创建代理类
在dynamic.cglib包中,创建代理类CglibDynamicProxy,该类实现MethodInterceptor接口。(下面用的是调用方法的方式得到要代理的对象,实际上使用构造器得到这个对象也可以)
package dynamic.cglib;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import aspect.MyAspect;
public class CglibDynamicProxy implements MethodInterceptor{
/**
* 创建代理的方法,生成CGLIB代理对象
* target目标对象,需要增强的对象
* 返回目标对象的CGLIB代理对象
*/
public Object createProxy(Object target) {
//创建一个动态类对象,即增强类对象
Enhancer enhancer = new Enhancer();
//确定需要增强的类,设置其父类
enhancer.setSuperclass(target.getClass());
//确定代理逻辑对象为当前对象,要求当前对象实现MethodInterceptor的方法
enhancer.setCallback(this);
//返回创建的代理对象
return enhancer.create();
}
/**
* intercept方法会在程序执行目标方法时被调用
* proxy CGLIB根据指定父类生成的代理对象
* method拦截方法
* args拦截方法的参数数组
* methodProxy方法的代理对象,用于执行父类的方法
* 返回代理结果
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//创建一个切面
MyAspect myAspect = new MyAspect();
//前增强
myAspect.check();
myAspect.except();
//目标方法执行,返回代理结果
Object obj = methodProxy.invokeSuper(proxy, args);
//后增强
myAspect.log();
myAspect.monitor();
return obj;
}
}
3 . 创建切面类(也可以不创建,直接写在代理类的intercept方法也可以,此处只是为了模拟spring的aop)
package aspect;
/**
* 切面类,可以定义多个通知,即增强处理的方法
*/
public class MyAspect {
public void check() {
System.out.println("模拟权限控制");
}
public void except() {
System.out.println("模拟异常处理");
}
public void log() {
System.out.println("模拟日志记录");
}
public void monitor() {
System.out.println("性能监测");
}
}
4.创建测试类
package dynamic.cglib;
public class CglibDynamicTest {
public static void main(String[] args) {
//创建代理对象
CglibDynamicProxy cdp = new CglibDynamicProxy();
//创建目标对象
TestDao testDao = new TestDao();
//获取增强后的目标对象
TestDao testDaoAdvice = (TestDao)cdp.createProxy(testDao);
//执行方法
testDaoAdvice.save();
System.out.println("==============");
testDaoAdvice.modify();
System.out.println("==============");
testDaoAdvice.delete();
}
}
5、输出结果
System.out.println("模拟权限控制");
System.out.println("模拟异常处理");
System.out.println("保存");
System.out.println("模拟日志记录");
System.out.println("性能监测");
System.out.println("==============");
System.out.println("模拟权限控制");
System.out.println("模拟异常处理");
System.out.println("修改");
System.out.println("模拟日志记录");
System.out.println("性能监测");
System.out.println("==============");
System.out.println("模拟权限控制");
System.out.println("模拟异常处理");
System.out.println("删除");
System.out.println("模拟日志记录");
System.out.println("性能监测");
System.out.println("==============");
总结:
Spring的事务管理机制实现的原理,就是通过这样一个动态代理对所有需要事务管理的Bean进行加载,并根据配置在invoke方法中对当前调用的 方法名进行判定,并在method.invoke方法前后为其加上合适的事务管理代码,这样就实现了Spring式的事务管理。Spring中的AOP实 现更为复杂和灵活,不过基本原理是一致的。