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

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

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

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

一、JDK这种方式动态代理

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

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

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

[java] view plaincopyprint?

  1. package cn.xym.empmis.web.filter;
  2. import java.io.IOException;
  3. import java.lang.reflect.InvocationHandler;
  4. import java.lang.reflect.Method;
  5. import java.lang.reflect.Proxy;
  6. import javax.servlet.Filter;
  7. import javax.servlet.FilterChain;
  8. import javax.servlet.FilterConfig;
  9. import javax.servlet.ServletException;
  10. import javax.servlet.ServletRequest;
  11. import javax.servlet.ServletResponse;
  12. import javax.servlet.http.HttpServletRequest;
  13. import javax.servlet.http.HttpServletResponse;
  14. /**
  15. * 解决全站乱码问题
  16. * @author Administrator
  17. *
  18. */
  19. public class CharacterEncodingFilter
    implements Filter{
  20. @Override
  21. public void init(FilterConfig filterconfig)
    throws ServletException {
  22. // TODO Auto-generated method stub
  23. }
  24. @Override
  25. public void doFilter(ServletRequest servletrequest,
  26. ServletResponse servletresponse, FilterChain filterchain)
  27. throws IOException, ServletException {
  28. final HttpServletRequest request = (HttpServletRequest) servletrequest;
  29. HttpServletResponse response = (HttpServletResponse) servletresponse;
  30. request.setCharacterEncoding("UTF-8"); 
    //只能解决Post方式提交的乱码问题,无法解决get提交的乱码
  31. //可以用包装设计模式,也可以用动态代理技术来解决get请求的乱码问题
  32. filterchain.doFilter((ServletRequest) Proxy.newProxyInstance(CharacterEncodingFilter.class.getClassLoader()
  33. , request.getClass().getInterfaces()
  34. , new InvocationHandler() {
  35. @Override
  36. public Object invoke(Object proxy, Method method, Object[] args)
  37. throws Throwable {
  38. //proxy表示:动态代理对象
  39. //method表示:需要代理的方法
  40. //args表示需要代理方法的参数
  41. if (!method.getName().equals("getParameter")){
  42. return method.invoke(request, args);
  43. }
  44. if (!request.getMethod().equalsIgnoreCase("get")){
  45. return method.invoke(request, args);
  46. }
  47. //满足要拦截处理的条件了
  48. String value = (String) method.invoke(request,args);
  49. if (value ==
    null){
  50. return
    null;
  51. }
  52. return
    new String(value.getBytes("iso8859-1"),"UTF-8");
  53. }
  54. }), response);
  55. }
  56. @Override
  57. public void destroy() {
  58. // TODO Auto-generated method stub
  59. }
  60. }
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动态代理功能

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

[html] view plaincopyprint?

  1. <?xml
    version="1.0"
    encoding="UTF-8"?>
  2. <beans
    xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:tx="http://www.springframework.org/schema/tx"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
  7. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
  8. http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
  9. <!-- 准备要做为目标对象(被代理对象) -->
  10. <bean
    id="userServiceImpl"
    class="cn.cubic.aop.service.impl.UserServiceImpl"
    />
  11. <!-- 配置通知对象 -->
  12. <!-- 前置通知 -->
  13. <bean
    id="myMethodBeforeAdvice"
    class="cn.cubic.aop.MyMethodBeforeAdvice"/>
  14. <!-- 后置通知 -->
  15. <bean
    id="myAfterReturningAdvice"
    class="cn.cubic.aop.MyAfterReturningAdvice"
    />
  16. <!-- 环绕通知 -->
  17. <bean
    id="myMethodInterceptor"
    class="cn.cubic.aop.MyMethodInterceptor"
    />
  18. <!-- 异常通知 -->
  19. <bean
    id="myThrowsAdvice"
    class="cn.cubic.aop.MyThrowsAdvice"/>
  20. <!-- 引入通知 ,自定义切入点,了解即可-->
  21. <bean
    id="myMethodBeforeAdviceFilter"
    class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
  22. <property
    name="advice"
    ref="myMethodBeforeAdvice"/>
  23. <property
    name="mappedNames">
  24. <list>
  25. <value>sayHello</value>
  26. </list>
  27. </property>
  28. </bean>
  29. <!-- 配置代理对象 -->
  30. <bean
    id="proxyFactoryBean"
    class="org.springframework.aop.framework.ProxyFactoryBean">
  31. <!-- 指定你希望代理的目标对象 -->
  32. <property
    name="target"
    ref="userServiceImpl"
    />
  33. <!-- 指定“代理接口”的列表 -->
  34. <property
    name="proxyInterfaces">
  35. <list>
  36. <value>cn.cubic.aop.service.IAbstractService</value>
  37. <value>cn.cubic.aop.service.IAbstractService2</value>
  38. </list>
  39. </property>
  40. <!-- 把“通知”织入代理对象 -->
  41. <property
    name="interceptorNames">
  42. <list>
  43. <value>myMethodBeforeAdviceFilter</value>
  44. <value>myAfterReturningAdvice</value>
  45. <value>myMethodInterceptor</value>
  46. <value>myThrowsAdvice</value>
  47. </list>
  48. </property>
  49. </bean>
  50. </beans>
<?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类:

[java] view plaincopyprint?

  1. package cn.cubic.aop.cglib;
  2. import java.lang.reflect.Method;
  3. import net.sf.cglib.proxy.Enhancer;
  4. import net.sf.cglib.proxy.MethodInterceptor;
  5. import net.sf.cglib.proxy.MethodProxy;
  6. /**
  7. *
  8. * Simple to Introduction
  9. *
  10. * @ProjectName:  [springAop]
  11. * @Package:      [cn.cubic.aop.cglib]
  12. * @ClassName:    [CGLIBProxy]
  13. * @Description:  [描述该类的功能]
  14. * @Author:       [逍遥梦]
  15. * @CreateDate:   [2014-3-1 下午4:47:22]
  16. * @UpdateUser:   [逍遥梦]
  17. * @UpdateDate:   [2014-3-1 下午4:47:22]
  18. * @UpdateRemark: [说明本次修改内容]
  19. * @Version:      [v1.0]
  20. *
  21. */
  22. public class CGLIBProxy
    implements MethodInterceptor{
  23. private Enhancer enhancer =
    new Enhancer();
  24. public Object getProxy(Class clazz){
  25. //设置父类
  26. enhancer.setSuperclass(clazz);
  27. enhancer.setCallback(this);
  28. //通过字节码技术动态创建子类实例
  29. return enhancer.create();
  30. }
  31. /**
  32. * 所有的方法都会被这个方法所拦截。该类实现了创建子类的方法与代理的方法。getProxy(SuperClass.class)方法通过入参即父类的字节码,通过扩展父类的class来创建代理对象。intercept()方法拦截所有目标类方法的调用,obj表示目标类的实例,method为目标类方法的反射对象,args为方法的动态入参,proxy为代理类实例。
  33. */
  34. @Override
  35. public Object intercept(Object obj, Method method, Object[] args,
  36. MethodProxy methodproxy)
    throws Throwable {
  37. System.out.println("cglib实现的前置代理");
  38. //通过代理类调用父类中的方法
  39. Object result = methodproxy.invokeSuper(obj, args);
  40. System.out.println("cglib实现的后置代理");
  41. return result;
  42. }
  43. }
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类:

[java] view plaincopyprint?

  1. package cn.cubic.aop.service.impl;
  2. public class CGLIBUserServiceImpl {
  3. public void sayHello(){
  4. System.out.println("CGLIBUserServiceImpl的sayHello方法被调用!");
  5. }
  6. public void sayBye(){
  7. System.out.println("CGLIBUserServiceImpl的sayHello方法被调用!");
  8. }
  9. }
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函数:

[java] view plaincopyprint?

  1. package cn.cubic.aop.junit;
  2. import cn.cubic.aop.cglib.CGLIBProxy;
  3. import cn.cubic.aop.service.impl.CGLIBUserServiceImpl;
  4. public class CGLIBTest {
  5. /**
  6. * @param args
  7. */
  8. public static
    void main(String[] args) {
  9. CGLIBProxy proxy = new CGLIBProxy();
  10. //生成子类,创建代理类
  11. CGLIBUserServiceImpl impl = (CGLIBUserServiceImpl)proxy.getProxy(CGLIBUserServiceImpl.class);
  12. impl.sayHello();
  13. }
  14. }
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方法,无法进行代理!

时间: 2024-10-29 19:09:15

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

java的动态代理的两种方式和spring的aop面向切面编程的对比

java动态代理的两种方式 使用动态代理的好处:可以进行类的功能的加强,同时减少耦合和代码的冗余,耦合的意思是不用吧加强的部分写到各个实现类里面,冗余的意思是如果对每个实现类加强的部分是一样的,通过一个代理类即可实现 基于jdk的动态代理 通过jdk中自带的Proxy类进行动态的代理,Proxy创建动态代理对象的时候要传入三个参数,第一个是classloader,第二个是interfaces,第三个是代理类实现的方法 要求:代理的是一个接口,必须至少有一个实现类 创建接口的代码: /** * a

java中设置代理的两种方式

1 前言 有时候我们的程序中要提供可以使用代理访问网络,代理的方式包括http.https.ftp.socks代理.比如在IE浏览器设置代理. 那我们在我们的java程序中使用代理呢,有如下两种方式.直接上代码. 2 采用设置系统属性 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 import jav

动态代理及其两种实现方式(JDK、CGLIB)

什么是代理模式 为某对象提供一个代理,从而通过代理来访问这个对象. 代理模式的角色组成 代理模式有三种角色组成: 抽象角色:通过接口或抽象类声明真实角色实现的业务方法. 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作. 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用. 我的总结: 抽象角色就是一个接口或抽象类,定义一些方法: 真实角色就是对抽象角色以及其中的方法进行实现: 代理角色也要实现抽象角色,并且注入真实角色

Java动态代理的两种实现方式:

方式一:传统的代理 package cn.hc.domain; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 代理对象创建的工厂类 * @author hc * */ public class JDKProxyFactory implements InvocationHandler{ //被代理对象 private

利用Selenium实现图片文件上传的两种方式介绍

在实现UI自动化测试过程中,有一类需求是实现图片上传,这种需求根据开发的实现方式,UI的实现方式也会不同. 一.直接利用Selenium实现 这种方式是最简单的一种实现方式,但是依赖于开发的实现. 当开发直接使用file类型的input实现图片文件的上传时,实例:<input type="file" name=''filename"> 我们可以直接利用Selenium提供的方法实现文件上传,但是因为依赖开发的实现,而且目前实现基本都会利用框架,所以这种实现方式有很

SpringBoot中使用Spring Data Jpa 实现简单的动态查询的两种方法

首先谢谢大佬的简书文章:http://www.jianshu.com/p/45ad65690e33# 这篇文章中讲的是spring中使用spring data jpa,使用了xml配置文件.我现在使用的是spring boot ,没有了xml文件配置就方便多了.我同样尝试了两种方式,也都是简单的查询,需要更复杂的查询,还需要我研究研究.往下看,需要先配置springboot的开发环境,需要大致了解springboot,这里可以看下面两篇文章: springboot 项目新建 springboot

MyBatis开发Dao层的两种方式(Mapper动态代理方式)

MyBatis开发原始Dao层请阅读我的上一篇博客:MyBatis开发Dao层的两种方式(原始Dao层开发) 接上一篇博客继续介绍MyBatis开发Dao层的第二种方式:Mapper动态代理方式 Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上一篇博客中Dao接口实现类方法. Mapper接口开发需要遵循以下规范: (1)Mapper.xml文件中的namespace与mapper接口的类路

动态加载dex的两种方式

DexClassLoader 加载的类是没有组件生命周期的,也就是说即使DexClassLoader通过对dex的动态加载完成了对组件的加载,当系统启动该组件时,还会出现加载类失败的异常.有两种方式可以解决上面出现的问题: 方法一:http://blog.csdn.net/androidsecurity/article/details/8809542,更改系统的classloader使其为自定义的加载器. 特点:两个dex具有明显的分割线,第一个dex只起启动作用,后面不会出现第一个dex的类加

Python 实现接口类的两种方式+邮件提醒+动态导入模块+反射(参考Django中间件源码)

实现接口类的两种方式 方式一 from abc import ABCMeta from abc import abstractmethod class BaseMessage(metaclass=ABCMeta): @abstractmethod def send(self,subject,body,to,name): pass 方式二 class BaseMessage(object): def send(self, subject, body, to, name): raise NotImp