一、静态代理:
静态代理实现 通过代理对象与被代理对象实现同一个接口,被代理对象中实现对代理对象中的方法进行增强 弊端:必须继承或实现同一个接口
定义一个接口ProposalService
public interface ProposalService { public void save(); }
目标对象(也就是被代理对象)实现ProposalService接口
/** * 目标对象 * @author Administrator * */ public class ProposalServiceImpl implements ProposalService { /** * 目标对象要被增强的方法 */ @Override public void save() { // TODO Auto-generated method stub System.out.println("当前对象执行的save方法"); } }
代理对象 实现ProposalService接口
/** * 代理对象实现类 * @author Administrator * */ public class ProxyProposal implements ProposalService{ private ProposalService target; public ProxyProposal (ProposalService target){ this.target=target; } /** * 代理对象实现proposalService接口对象方法增强 */ @Override public void save() { // TODO Auto-generated method stub System.out.println("开始事务"); target.save(); System.out.println("提交事务"); } }
测试类实现:
public class Test { public static void main(String[] args) { //目标对象 ProposalService target = new ProposalServiceImpl(); //代理对象 ProxyProposal proxyProposal = new ProxyProposal(target); //调用增强的方法 proxyProposal.save(); } }
为了解决代理对象与被代理对象必须实现同一接口的弊端采用动态代理方式。
二、动态代理
定义一个接口
package cn.ac.bcc.dynamic.proxy.service; /** * 动态代理方式 java api接口 * @author Administrator * */ public interface ProposalService { public void save(); }
定义接口实现类 即要增强的目标对象类
package cn.ac.bcc.dynamic.proxy.service.impl; import cn.ac.bcc.dynamic.proxy.service.ProposalService; /** * 目标对象 * @author Administrator * */ public class ProposalServiceImpl implements ProposalService { /** * 目标方法 事项对该方法增强 */ @Override public void save() { // TODO Auto-generated method stub System.out.println("我是目标方法"); } }
代理实例类
package cn.ac.bcc.dynamic.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 代理对象工厂类 用来创建代理对象 * @author Administrator * */ public class ProxyFactroy { //用来接收目标对象 private Object target; public ProxyFactroy(Object target){ this.target=target; } /** * 获取代理对象 * newProxyInstance(ClassLoader loader,Interfaces interface,InvocationHandler invocationHandle) * 参数一:目标对象类加载器对象 通过加载器获取当前目标全类 * 参数二:目标对象实现的接口类型 数组 class<>[] * 参数三: * @return */ public Object getNewInstanceProxy(){ return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { /** * invoke(Object proxy, Method method, Object[] args) * 参数一:代理实例 并不是目标对象 * 参数二:目标对象反射类型方法类对象 * 参数三:目标方法中参数数组 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("对目标方法开始增强代码。。。。"); //采用反射方式 调用目标方法 返回目标对象 Object invoke = method.invoke(target, args); System.out.println("对目标方法执行完之后增强的代码。。。。"); return invoke; } }); } }
测试类:
package cn.ac.bcc.dynamic.proxy.test; import cn.ac.bcc.dynamic.proxy.ProxyFactroy; import cn.ac.bcc.dynamic.proxy.service.ProposalService; import cn.ac.bcc.dynamic.proxy.service.impl.ProposalServiceImpl; /** * java api方式实现动态代理测试类 * @author Administrator * */ public class DynProxyTest { public static void main(String[] args) { //创建目标对象 ProposalService proposalService= new ProposalServiceImpl(); //创建代理工厂对象传入目标对象获取代理实例 ProposalService ps = (ProposalService) new ProxyFactroy(proposalService).getNewInstanceProxy(); //代理对象调用save方法 出发增强方法 ps.save(); } }
问题总结:开始在invoke(Object proxy,method method,Object args[]) Object proxy认为是目标对象 在method.invoke(目标对象,args) 以为是invoke传入的参数出现异常
原因就是invoke(Object proxy) proxy 并不是目标对象 而是在其上调用方法的代理实例
详解推荐博客地址:https://blog.csdn.net/truong/article/details/34857951
JDK动态代理具有很大的局限性,目标对象(也就是委托类)必须实现一个接口,并不是所有的类都实现了接口,如果该类没有实现接口就没有办法通过JDK动态代理方式对方法进行增强。
Cglib实现动态代理
Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
- JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.
- Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
- Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.
使用cglib实现动态代理
1)需要引入cglib.jar 我这里采用了 cglib2.2.2.jar
2)同时需要引入asm.jar 采用类asm.3.1.jar
3) 代理类不是final 否则会报错
4)目标对象的方法如果为final / static 方法不会被拦截即不会执行目标对象额外的业务方法
代码实现:
package cn.ac.bcc.dynamic.cglib; /** * 定义一个目标类对象 目标类对象没有实现接口 * @author Administrator * */ public class UserDao { public void save(){ System.out.println("需要增强的方法"); } }
代理类实现:
package cn.ac.bcc.dynamic.cglib; import java.lang.reflect.Method; import java.util.concurrent.Exchanger; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * 定义一个代理类对象实现方法拦截器 * @author Administrator * */ public class ProxyFactory implements MethodInterceptor { //目标对象 private Object target; public ProxyFactory(Object target){ this.target=target; } /** * 获取代理实例 给目标对象创建一个代理对象 * @return */ public Object getProxyInstance(){ //工具类 Enhancer enhancer = new Enhancer(); //设置父类 enhancer.setSuperclass(target.getClass()); //设置回调函数 enhancer.setCallback(this); //创建子类 return enhancer.create(); } /** * 重写methodInterceptor类拦截方法 对需要增强方法进行拦截 */ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("开始增强代码"); Object invoke = method.invoke(target, args); System.out.println("增强后代码"); return invoke; } }
测试类:
package cn.ac.bcc.dynamic.cglib; public class CglibTest { public static void main(String[] args) { //创建目标对象(需要增类对象) UserDao userDao = new UserDao(); //创建获取增强代理类对象 ProxyFactory proxyFactory = new ProxyFactory(userDao); //获取代理对象 UserDao ud = (UserDao)proxyFactory.getProxyInstance(); ud.save(); } }
异常产生:
原因没有引入asm.jar出现异常
当引入asm.4.1.jar出现异常
解决方案:引入asm.3.1.jar
使用CGLIB即使代理类没有实现任何接口也可以实现动态代理功能。CGLIB具有简单易用,它的运行速度要远远快于JDK的
CGLIB的核心类:
net.sf.cglib.proxy.Enhancer – 主要的增强类
net.sf.cglib.proxy.MethodInterceptor – 主要的方法拦截类,它是Callback接口的子接口,需要用户实现
net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用,
参考博客:
https://www.cnblogs.com/gonjan-blog/p/6685611.html
https://blog.csdn.net/yangsnow_rain_wind/article/details/79291256
https://blog.csdn.net/fighterandknight/article/details/51200470
https://blog.csdn.net/danchu/article/details/70238002