动态代理和静态代理

动态代理和静态代理


静态代理的实现:
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实 现更为复杂和灵活,不过基本原理是一致的。

  • 16
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值