实现动态代理的两种方式介绍+例子demo(JDK、CGlib)

标签: 代理 demo jdk | 发表时间:2014-03-05 04:57 | 作者:u011133213
出处:http://blog.csdn.net

JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢?

这就需要CGLib了。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。

JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。


一、JDK这种方式动态代理

1. 没引入spring配置文件时,怎么实现JDK动态代理


情景介绍:如何解决全站中文乱码问题?

我们会定义一个过滤器:CharacterEncodingFilter

package cn.xym.empmis.web.filter;

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

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 解决全站乱码问题
 * @author Administrator
 *
 */
public class CharacterEncodingFilter implements Filter{

	@Override
	public void init(FilterConfig filterconfig) throws ServletException {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void doFilter(ServletRequest servletrequest,
			ServletResponse servletresponse, FilterChain filterchain)
			throws IOException, ServletException {
		
		
		final HttpServletRequest request = (HttpServletRequest) servletrequest;
		HttpServletResponse response = (HttpServletResponse) servletresponse;
		request.setCharacterEncoding("UTF-8");	//只能解决Post方式提交的乱码问题,无法解决get提交的乱码
		
		//可以用包装设计模式,也可以用动态代理技术来解决get请求的乱码问题
		
		filterchain.doFilter((ServletRequest) Proxy.newProxyInstance(CharacterEncodingFilter.class.getClassLoader()
				, request.getClass().getInterfaces()
				, new InvocationHandler() {
					
					@Override
					public Object invoke(Object proxy, Method method, Object[] args)
							throws Throwable {
						//proxy表示:动态代理对象
						//method表示:需要代理的方法
						//args表示需要代理方法的参数
						if (!method.getName().equals("getParameter")){
							return method.invoke(request, args);
						}
						if (!request.getMethod().equalsIgnoreCase("get")){
							return method.invoke(request, args);
						}
						//满足要拦截处理的条件了
						String value = (String) method.invoke(request,args);
						if (value == null){
							return null;
						}
						return new String(value.getBytes("iso8859-1"),"UTF-8");
					}
				}), response);
	}
	@Override
	public void destroy() {
		// TODO Auto-generated method stub
		
	}
	
}


2.引入spring配置文件时,实现JDK动态代理功能

要设计出几种需要的“通知类型”的类,在配置文件中配置代理对象,指定代理目标(即要被代理的对象),指定所有要代理的接口(列表),最后把需要的“通知类型”织入到代理对象!

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xmlns:context="http://www.springframework.org/schema/context"
		xmlns:tx="http://www.springframework.org/schema/tx"
		xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
				http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
				http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
		
		<!-- 准备要做为目标对象(被代理对象) -->
		<bean id="userServiceImpl" class="cn.cubic.aop.service.impl.UserServiceImpl" />
	
		<!-- 配置通知对象 -->
		<!-- 前置通知 -->
		<bean id="myMethodBeforeAdvice" class="cn.cubic.aop.MyMethodBeforeAdvice"/>
		<!-- 后置通知 -->
		<bean id="myAfterReturningAdvice" class="cn.cubic.aop.MyAfterReturningAdvice" />
		<!-- 环绕通知 -->
		<bean id="myMethodInterceptor" class="cn.cubic.aop.MyMethodInterceptor" />
		<!-- 异常通知 -->
		<bean id="myThrowsAdvice" class="cn.cubic.aop.MyThrowsAdvice"/>
		
		<!-- 引入通知 ,自定义切入点,了解即可-->
		<bean id="myMethodBeforeAdviceFilter" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
			<property name="advice" ref="myMethodBeforeAdvice"/>
			<property name="mappedNames">
				<list>
					<value>sayHello</value>
				</list>
			</property>
		</bean>
		
		
		<!-- 配置代理对象 -->
		<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
			
			<!-- 指定你希望代理的目标对象 -->
			<property name="target" ref="userServiceImpl" />
			
			<!-- 指定“代理接口”的列表 -->
			<property name="proxyInterfaces">
				<list>
					<value>cn.cubic.aop.service.IAbstractService</value>
					<value>cn.cubic.aop.service.IAbstractService2</value>
				</list>
			</property>
			
			<!-- 把“通知”织入代理对象 -->
			<property name="interceptorNames">
				<list>
					<value>myMethodBeforeAdviceFilter</value>
					<value>myAfterReturningAdvice</value>
					<value>myMethodInterceptor</value>
					<value>myThrowsAdvice</value>
				</list>
			</property>
		</bean>
		
</beans>



二、CGlib 这种方式实现动态代理

CGLIBProxy类:

package cn.cubic.aop.cglib;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;


/**
 *   
 * Simple to Introduction  
 *
 * @ProjectName:  [springAop] 
 * @Package:      [cn.cubic.aop.cglib]  
 * @ClassName:    [CGLIBProxy]   
 * @Description:  [描述该类的功能]   
 * @Author:       [逍遥梦]   
 * @CreateDate:   [2014-3-1 下午4:47:22]   
 * @UpdateUser:   [逍遥梦]   
 * @UpdateDate:   [2014-3-1 下午4:47:22]   
 * @UpdateRemark: [说明本次修改内容]  
 * @Version:      [v1.0] 
 *
 */
public class CGLIBProxy implements MethodInterceptor{
	
	 private Enhancer enhancer = new Enhancer();
	
	 public Object getProxy(Class clazz){
		  
		  //设置父类
		  enhancer.setSuperclass(clazz);
		  enhancer.setCallback(this);
		  
		  //通过字节码技术动态创建子类实例
		  return enhancer.create();
	 }
	
	/**
	 * 所有的方法都会被这个方法所拦截。该类实现了创建子类的方法与代理的方法。getProxy(SuperClass.class)方法通过入参即父类的字节码,通过扩展父类的class来创建代理对象。intercept()方法拦截所有目标类方法的调用,obj表示目标类的实例,method为目标类方法的反射对象,args为方法的动态入参,proxy为代理类实例。
	 */
	@Override
	public Object intercept(Object obj, Method method, Object[] args,
			MethodProxy methodproxy) throws Throwable {
		
		System.out.println("cglib实现的前置代理");
		
		//通过代理类调用父类中的方法
		Object result = methodproxy.invokeSuper(obj, args);
		
		System.out.println("cglib实现的后置代理");
		return result;
	}

	
}


CGLIBUserServiceImpl类:

package cn.cubic.aop.service.impl;

public class CGLIBUserServiceImpl {
	
	public void sayHello(){
		System.out.println("CGLIBUserServiceImpl的sayHello方法被调用!");
	}
	
	public void sayBye(){
		System.out.println("CGLIBUserServiceImpl的sayHello方法被调用!");
	}
}

Main函数:

package cn.cubic.aop.junit;

import cn.cubic.aop.cglib.CGLIBProxy;
import cn.cubic.aop.service.impl.CGLIBUserServiceImpl;

public class CGLIBTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		CGLIBProxy proxy = new CGLIBProxy();
		
		//生成子类,创建代理类
		CGLIBUserServiceImpl impl = (CGLIBUserServiceImpl)proxy.getProxy(CGLIBUserServiceImpl.class);
		impl.sayHello();
		
	}

}


三、比较两种方式的优缺点

CGLib创建的动态代理对象性能比JDK创建的动态代理对象的性能高不少,但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。同时,由于CGLib由于是采用动态创建子类的方法,对于final方法,无法进行代理!

作者:u011133213 发表于2014-3-4 20:57:28 原文链接
阅读:84 评论:0 查看评论

相关 [代理 demo jdk] 推荐:

实现动态代理的两种方式介绍+例子demo(JDK、CGlib)

- - CSDN博客架构设计推荐文章
JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢. CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑. JDK动态代理与CGLib动态代理均是实现Spring AOP的基础.

JDK动态代理机制

- - CSDN博客编程语言推荐文章
    代理模式有两种,一种是静态代理,这种方式需要为每一个被代理类写一个代理类,显示比较麻烦. 还一种是动态代理,动态代理实现方式一般有两种,JDK动态代理与CGLIB动态代理,这里说一下对JDK动态代理的理解.     JDK动态代理最核心的就类就是java.lang.reflect.Proxy,可调用Proxy.newInstance(..)生成动态代理.

JAVA之JDK动态代理

- - Java - 编程语言 - ITeye博客
在Java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和接口,可以生成JDK动态代理类或动态代理对象. Proxy提供了用于创建动态代理类和代理对象的静态方法,它也是所有动态代理类的父类,如果在程序中为一个或多个接口动态的生成实现类,就可以使用Proxy来创建动态代理类,.

Spring AOP 代理机制 JDK&CGLIB

- - 开源软件 - ITeye博客
Spring AOP使用JDK动态代理或者CGLIB来为目标对象创建代理. (建议优先使用JDK的动态代理). 如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理. 所有该目标类型实现的接口都将被代理. 若该目标对象没有实现任何接口,则创建一个CGLIB代理. 如果你希望强制使用CGLIB代理,(例如:希望代理目标对象的所有方法,而不只是实现自接口的方法) 那也可以.

动态代理双剑客--JDK Proxy与CGLIB

- - CSDN博客推荐文章
研究过设计模式的同胞们都知道代理模式可以有两种实现方案:. 1.接口实现(或继承抽象类). 都可以通过Proxy控制对Target的访问. “接口实现”的方式更加灵活,代理类可以代理所有实现了ISubject接口的类;. “继承父类”的方式代理类只能代理它的父类,因为java中只支持单继承. 如果Target有直接接口,那么这两种方式都可以;.

Sun JDK 1.6内存管理

- 小丑鱼 - 淘宝JAVA中间件团队博客
分为使用篇、调优篇和实现篇三个部分,使用篇为填鸭式,调优篇为pattern式,实现篇为启发式,三个PPT的目标为:. 1.掌握Sun JDK的内存区域的划分;. 2.掌握Sun JDK垃圾收集器的使用方法和触发时机;. 4.掌握一些基本的GC调优的方法;. 5.了解自动内存管理的常见实现方法,以及Sun JDK所做的优化.

JDK自带监控工具

- - ITeye博客
         系统在生产运行过程中最重要的工作莫过于监控与问题的处理,监控是预防问题产生很重要的手段. 在监控过程中可以发现那些模块或进程出现了问题,出现问题后会及时通知问题负责人.         实现监控的手段非常多,有系统级别监控系统,也有监控小工具等等. Java 就已经自带了一些监控工具,可以不借助外部软件的情况下简单、快速查看相应监控信息.

Activiti工作流demo

- - CSDN博客综合推荐文章
继上篇《 Activiti工作流的环境配置》.        前几篇对Activiti工作流进行了介绍,并讲解了其环境配置. 本篇将会用一个demo来展示Activiti工作流具体的体现,直接上干货.        以HelloWorld程序为例.       首先说一下业务流程,员工张三提交了一个申请,然后由部门经理李四审核,审核通过后再由总经理王五审核,通过则张三申请成功.

Y Combinator 举办 Demo Day

- Radar - 丕子
世界上最大的创业公司孵化器 Y Combinator 今天举办他们的 Demo Day,这次一共有 63 家创业公司参加演示,其中有 31 家愿意向媒体和投资人曝光自己,下面是这些创业公司的名字以及一句话描述:. Aisle50: 杂货版 Groupon. Interstate: 项目管理软件,可以跟客户分享开发路线图.

地形模拟演示Demo

- kongshanzhanglao - 博客园-首页原创精华区
地形渲染的首先是创建一个三角网络平面,然后调整平面顶点的y高度值,模拟地面的山丘和山谷,最后再绘制贴图效果. 本文首先介绍如何生成三角网络平面. 然后介绍如何通过高度图调整平面高度. 以及使用BlendMap和3种材质绘制贴图效果的方法. 最后演示如何调整摄像机位置和移动速度,在地面上行走. 一个m*n个顶点的平面由2*(m-1)*(n-1)个三角形组成.