JDK动态代理机制

    代理模式有两种,一种是静态代理,这种方式需要为每一个被代理类写一个代理类,显示比较麻烦。还一种是动态代理,动态代理实现方式一般有两种,JDK动态代理与CGLIB动态代理,这里说一下对JDK动态代理的理解。

    JDK动态代理最核心的就类就是java.lang.reflect.Proxy,可调用Proxy.newInstance(..)生成动态代理。

如果有一个UserService接口(JDK动态代理必须有接口),一个UserService接口实现类UserServiceBean,现在要生成一个UserServiceBean的动态代理类代码如下:

final UserService userService = new UserServiceBean();
Object proxy = Proxy.newProxyInstance(UserService.class.getClassLoader(), new Class[]{UserService.class}, new InvocationHandler() {
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object obj = method.invoke(userService, args);
		return obj;
	}
});
UserService us = (UserService) proxy;
newInstance方法的第一个参数指定一个ClassLoader(类装载器),第二个参数是一个接口数组,生成的代理类会实现数组中的所有接口,第三个参数为一个InvocationHandler对象,InvocationHandler是一个接口,所以这里用了匿名内部实现值。


    一个动态类在运行时被创建,并在创建的时候实现了一系统接口,就是在接口数组中指定的接口。一个代理接口是被代理类实现的接口。一个代理实例是代理类实例,每一个代理实现都关联了一个InvocationHandler对象,该对象实现了InvocationHandler接口,当通过代理接口调用一个代理实例的一个方法时会被转发到该代理实例关系的InvocationHandler对象的invoke方法,一个java.lang.reflect.Method对象表示了被调用的方法,而Object[]包含了该方法的参数,把invoke的返回值为作代理对象方法的返回值。


   一个代理类有如下属性:

@@ 一个代理是public,final(即不能被继承),非abstract的

@@ 该代理类的名称为"$Proxyn",其中n为一个数字

@@ 所有的代理类都继承自java.lang.reflect.Proxy

@@ 当一个代理类创建的时候,按照在接口数组中写的顺序实现了所有接口

@@ 如果一个代理类实现了一个非public接口,那么该代理类被定义在与该非public接口相同的包中,因为一个非public接口    不可能是private与protected,那么就只是能default的,即不加任何访问修饰符的,而这种只有在同一个包中的类才能    访问,这就决定了该代理类也必须位于该包中。如果代理类实现的是一public接口,这个代理类的包没有具体指定就是在缺省    包中,即没有包名。

@@ Proxy.isProxyClass方法用于判断一个是不是一个动态代理类

@@ 每一个代理类都有一个带一个参数的公开构造方法,该参数是一个实现了InvocationHandler接口的对象,把该对象关联到    一个代理实例上。

    当调用代理实例的从Object类继承而来的hashCode,equals,toString方法时,也会转发到InvocationHandler的invoke方法,就像接口中声明的方法一样。而其它从Object类继承而来的public方法是不会被代理实现覆盖的,如:getClass等,其实这些方法也不可能被覆盖,因为这些方法都是final的。


下面举一个JDK动态代理应用例子。

在项目中如果使用了Spring的话,事务都是交由Spring来管理的,但是如果有些项目没有使用Spring的话,这时事务的自动开启,提交,回滚操作就可以用JDK动态代理实现,假DAO用的是Hibernate

package cn.it.shopping.service.system.impl;

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

import org.hibernate.Session;

import cn.it.shopping.misc.SessionFactoryHelper;
import cn.it.shopping.pojo.system.Admin;
import cn.it.shopping.service.base.DaoSupport;
import cn.it.shopping.service.system.AdminService;
import cn.it.shopping.utils.MD5;

/**
 * 自己实现的动态代理AdminServiceBean,在方法开始前打开事务,在方法执行打开事务,提交或回滚后关闭session
 * 出现异常自动回滚
 * @author zj
 *
 */
public class ProxyAdminServiceBean extends DaoSupport<Admin> implements AdminService {
	private static ProxyAdminServiceBean instance = new ProxyAdminServiceBean();
	private ProxyAdminServiceBean(){} //保证单例
	
	/**
	 * 真正返回的是instance的一个代理对象
	 */
	public static AdminService getInstance() {
		return (AdminService) Proxy.newProxyInstance(instance.getClass().getClassLoader(), new Class[]{AdminService.class}, new InvocationHandler() {
			
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				//SessionFactoryHelper为获取SessionFactory的一个工具类
				//这样获取的Session会在事务提交或回滚时自动提交,因为返回的本身就已经是个代理对象
				Session session = SessionFactoryHelper.getSessionFactory().getCurrentSession();
				session.getTransaction().begin();//开始前开启事务
				Object result;
				try {
					result = method.invoke(instance, args);//执行业务逻辑
					session.getTransaction().commit();//业务逻辑执行完成后提交事务
					return result;
				} catch (Exception e) {
					session.getTransaction().rollback(); //如果业务逻辑出现例外则事务回滚
					throw new RuntimeException(e);
				} finally {
					if(null!=session && session.isOpen()) {
						session.close();//这里是不会执行的,因为Session会在事务提交或回滚时自动提交,如果调用的是openSession获取Session这里就会执行
					}
				}
			}
		});
	}

	public void save(Admin entity) {
		String pass = MD5.MD5Encode(entity.getPass());
		entity.setPass(pass);
		super.save(entity);
	}
}



作者:xtayfjpk 发表于2013-10-10 16:39:06 原文链接
阅读:41 评论:0 查看评论

你可能感兴趣的:(jdk,代理)