Java动态代理学习2——静态代理和动态代理并对照spring的通知

 

一、代理模式 

代理模式是常用的java设计模式,特征是代理类委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。

代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。 

按照代理的创建时期,代理类可以分为两种:

静态代理:由程序员创建或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。 
动态代理:在程序运行时运用反射机制动态创建而成。 

 

 

二、静态代理

public interface CountDao
{
	// 查看账户方法
	public void queryCount();

	// 修改账户方法
	public void updateCount();

}

public class CountDaoImpl implements CountDao
{
	public void queryCount()
	{
		System.out.println("查看账户方法...");
	}

	public void updateCount()
	{
		System.out.println("修改账户方法...");
	}
}

public class CountProxy implements CountDao
{
	private CountDao countDao;

	public CountProxy(CountDao countDao)
	{
		this.countDao = countDao;
	}

	@Override
	public void queryCount()
	{
		System.out.println("事务处理之前");
		countDao.queryCount();
		System.out.println("事务处理之后");
	}

	@Override
	public void updateCount()
	{
		System.out.println("事务处理之前");
		countDao.updateCount();
		System.out.println("事务处理之后");
	}
}

public class TestCount
{
	public static void main(String[] args)
	{
		CountProxy countProxy = new CountProxy(new CountDaoImpl());
		countProxy.updateCount();
		countProxy.queryCount();
	}
}

 

观察代码可以发现每一个代理类只能为一个接口服务,这样一来程序开发中必然会产生过多的代理,而且所有的代理操作除了调用的方法不一样之外其他的操作都一样,则此时肯定是重复代码。解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能,那么此时就必须使用动态代理完成。 

 

三、动态代理

与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。

 

1 JDK动态代理

JDK动态代理中包含一个类和一个接口

 

InvocationHandler接口

public interface InvocationHandler {
    public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
}

Object proxy:指被代理的对象。
Method method:要调用的方法
Object[] args:方法调用时所需要的参数

可以将InvocationHandler接口的子类想象成一个代理的最终操作类,替换掉ProxySubject

 

Proxy类

Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
InvocationHandler h) throws IllegalArgumentException

ClassLoader loader:类加载器
Class<?>[] interfaces:得到全部的接口
InvocationHandler h:得到InvocationHandler接口的子类实例

public interface PersonService
{
	public void save();
}

public class PersonServiceImpl implements PersonService
{
	public void save()
	{
		System.out.println("人员增加");
	}
}

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class PersonProxy implements InvocationHandler
{
	// 目标对象
	private Object target;

    // 返回一个代理类对象
	public Object createProxyInstance(Object target)
	{
		this.target = target;
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
	}

	// 上述代码中调用的this就是当前代理对象,会自动调用该方法
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
	{
		Object result = null;
		System.out.println("前置通知");
		try
		{
			result = method.invoke(target, args);
			System.out.println("后置通知");
		}
		catch(Exception e)
		{
			System.out.println("例外通知");
		}
		finally
		{
			System.out.println("最终通知");
		}
		return result;
	}
}

public class TestProxy
{
	public static void main(String[] args)
	{
		PersonProxy bp = new PersonProxy();
		PersonService ps = (PersonService)bp.createProxyInstance(new PersonServiceImpl());
		ps.save();
	}
}

前置通知
人员增加
后置通知
最终通知

JDK的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用JDK代理,这就要使用cglib动态代理了。 

 

 

2 CGLIB动态代理

JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承所以不能对final修饰的类进行代理。 

public interface PersonService
{
	public void save();
}

public class PersonServiceImpl implements PersonService
{
	public void save()
	{
		System.out.println("人员增加");
	}
}

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 使用CGLIB动态代理
 */
public class PersonProxy implements MethodInterceptor
{
	private Object target;

	// 返回一个代理类对象
	public Object createProxyInstance(Object target)
	{
		this.target = target;
		Enhancer enhancer = new Enhancer();

		// 设置目标类为父类,会覆盖目标类的非final方法
		enhancer.setSuperclass(this.target.getClass());

		// 回调方法
		enhancer.setCallback(this);

		// 创建代理对象
		return enhancer.create();
	}

	// 上述代码中调用的this就是当前代理对象,会自动调用该方法
	// 方法一
	@Override
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable
	{
		Object result = null;
		System.out.println("前置通知");
		try
		{
			result = method.invoke(target, args);
			System.out.println("后置通知");
		}
		catch (Exception e)
		{
			System.out.println("例外通知");
		}
		finally
		{
			System.out.println("最终通知");
		}
		return result;
	}
}

public class PersonProxy2 implements MethodInterceptor
{
	private Object target;

	// 返回一个代理类对象
	public Object createProxyInstance(Object target)
	{
		this.target = target;
		Enhancer enhancer = new Enhancer();

		// 设置目标类为父类,会覆盖目标类的非final方法
		enhancer.setSuperclass(this.target.getClass());

		// 回调方法
		enhancer.setCallback(this);

		// 创建代理对象
		return enhancer.create();
	}

	// 方法二
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable
	{
		Object result = null;
		System.out.println("前置通知");
		try
		{
			result = proxy.invokeSuper(obj, args);
			System.out.println("后置通知");
		}
		catch (Exception e)
		{
			System.out.println("例外通知");
		}
		finally
		{
			System.out.println("最终通知");
		}
		return result;
	}
}

public class TestProxy
{
	public static void main(String[] args)
	{
		PersonProxy cglib = new PersonProxy();
		PersonServiceClass ps1 = (PersonServiceClass) cglib.createProxyInstance(new PersonServiceClass());
		ps1.save();
		System.out.println("--------------------");
		PersonProxy2 cglib2 = new PersonProxy2();
		PersonServiceClass ps2 = (PersonServiceClass) cglib2.createProxyInstance(new PersonServiceClass());
		ps2.save();
	}
}

 

前置通知
增加人员
后置通知
最终通知
--------------------
前置通知
增加人员
后置通知
最终通知

参考并演绎自地址:http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html

 

时间: 2012-11-21
Tags: java, void, class, spring

Java动态代理学习2——静态代理和动态代理并对照spring的通知的相关文章

Java动态代理学习1——静态代理

  一.代理模式 代理模式是常用的java设计模式,特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等. 代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务.    按照代理的创建时期,代理类可以分为两种: 静态代理:由程序员创建或特定工具自动生成源代码再对其编译.在程序运行前代理类的.class文件就已经存在了. 动

C++静态库与动态库

C++静态库与动态库 这次分享的宗旨是--让大家学会创建与使用静态库.动态库,知道静态库与动态库的区别,知道使用的时候如何选择.这里不深入介绍静态库.动态库的底层格式,内存布局等,有兴趣的同学,推荐一本书<程序员的自我修养--链接.装载与库>. 什么是库 库是写好的现有的,成熟的,可以复用的代码.现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常. 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行.库有两种:静态库(.a..lib

一步一步学习SignalR进行实时通信_7_非代理

原文:一步一步学习SignalR进行实时通信_7_非代理 一步一步学习SignalR进行实时通信\_7_非代理 SignalR 一步一步学习SignalR进行实时通信_7_非代理 前言 代理与非代理 建立连接 调用方法 维持状态 从服务器接收 结束语 参考文献 前言 距离上次写博客已经一个多月了,一直想抽空写,却一直没时间写.现在紧接着要开始毕业设计了,工作的同时还要抽时间完成毕业设计,心也是有些累.由于自己是个.NET迷,自己的工作也是.net开发,老师给的毕业设计百分之八九十都是安卓java

java gif 帧数-java如何获取gif帧或者区分静态gif和动态gif

问题描述 java如何获取gif帧或者区分静态gif和动态gif java语音怎么获取gif图的帧数,或者有什么第三方jar可以做到? 另外程序如何区分静态的gif和动态的gif

java.lang包下的类能不能被代理,为什么??

问题描述 java.lang包下的类能不能被代理,为什么?? 今天碰到一个问题,说是java.lang下的类不能被代理? 想知道原因是什么? 解决方案 绝对不能.要是你能随便代理,那JVM的安全何在? java.lang这个包是java的核心包.这个包下的类是有顶级的BootStrap 引导类加载器加载.java的类加载器是双亲委派机制,意思就是上层加载器加载过了,就不再加载. 举个例子,你自己定义一个类加载器.自己写个java.lang.String类,然后尝试着用自己的类加载器去加载,绝对加

JSP-讲解(生成java类、静态导入与动态导入)

一.JSP技术简介 JSP是Java Server Page的缩写,它是Servlet的扩展,它的作用是简化网站的创建和维护. JSP是HTML代码与Java代码的混合体. JSP文件通常以JSP或JSPX的扩展名. JSP拥有自己的语法. JSP形式上像HTML,但本质上是Servlet. JSP的出现,使得将Web开发中的HTML与业务逻辑代码有效分离成为可能.通常JSP只负责生成动态的HTML文档,而业务逻辑由其他Java组件如JavaBean来实现.JSP可以通过Scriptlet来访问

Java架构师学习之路

Java架构师,首先得是一个高级java攻城狮,熟练使用各种框架,并知道它们实现的原理.jvm虚拟机原理.调优,懂得jvm能让你写出性能更好的代码;池技术:什么对象池,连接池,线程池-- Java反射技术,写框架必备的技术,但是有严重的性能问题,替代方案java字节码技术;nio,没什么好说的,值得注意的是"直接内存"的特点,使用场景;java多线程同步异步;java各种集合对象的实现原理,了解这些可以让你在解决问题时选择合适的数据结构,高效的解决问题,比如hashmap的实现原理,好

Tomcat安装+Nginx反向代理Tomcat+Apache使用mod_jk+mod_proxy反向代理和负载均衡【图解】

一.Tomcat简介 Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目由Apache.Sun 和其他一些公司及个人共同开发而成.由于有了Sun 的参与和支持最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现Tomcat 5 支持最新的Servlet 2.4 和JSP 2.0 规范.因为Tomcat 技术先进.性能稳定而且免费因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可成为目前比较流行

android: 静态XML和动态加载XML混合使用,以及重写Layout控件

近期对android里面控件修改做了很多实验,由于公司需求很多,不得不重写很多控件.程序目标无非是:高效.轻巧.清晰.标准化   完成动态加载Layout有两种方法,依据个人喜好进行选择:   方法1:静态主Layout动态加载静态子Layout   首先构建子Layout:main2 [xhtml] view plaincopy <?xml version="1.0" encoding="utf-8"?>   <!--布局可以任意定义,此处拿线性