静态代理和动态代理区别(是否有实现类)
文章目录
- 一、简介
- 1、代理分类:
- 2、代码区别:
- 二、静态代理
- 1、接口定义要做的事情
- 2、具体用户实现类
- 3、代理类(业务增强类)
- 4、测试类
- 三、动态代理
- 1、JDK动态代理类
- 1.1、有接口有委托类的情况
- 1.2、仅有接口的情况
- 2、CGLIB动态代理
- 三、总结
一、简介
Proxy代理模式是一种结构型设计模式,主要解决的问题是:在直接访问对象时带来的问题,
代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。
Java中使用代理技术主要用于扩展原功能又不侵入(修改)源代码。
主要应用: 比如想在某个类的某个方法执行之前打印日志或者记录下开始时间,但是又不好将打印日志和时间的逻辑写入原来的方法里。这时就可以创建一个代理类实现和原方法相同的方法,通过让代理类持有真实对象,然后代码调用的时候直接调用代理类的方法,来达到增强业务逻辑的目的。
1、代理分类:
静态代理: 由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。
Java中的静态代理要求代理类(ProxySubject)和委托类(RealSubject)都实现同一个接口(Subject)。静态代理中代理类在编译期就已经确定,而动态代理则是JVM运行时动态生成,静态代理的效率相对动态代理来说相对高一些,但是静态代理代码冗余大,一旦需要修改接口,代理类和委托类都需要修改。
动态代理: 在程序运行时运用反射机制动态创建而成。
Java中的动态代理依靠反射来实现,代理类和委托类不需要实现同一个接口。委托类需要实现接口,否则无法创建动态代理。代理类在JVM运行时动态生成,而不是编译期就能确定。
Java动态代理主要涉及到两个类:java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler。代理类需要实现InvocationHandler接口或者创建匿名内部类,而Proxy用于创建动态动态。
2、代码区别:
静态代理:
动态代理:(少用户自己实现类)
二、静态代理
1、接口定义要做的事情
public interface IBuyHouse { // 定义要做的事情 public void buyHouse(); }2、具体用户实现类
/** * 用户实现类(也就是自己做,需要干的事情) */ public class HouseDelegation implements IBuyHouse{ @Override public void buyHouse() { System.out.println("去选房,购房."); } }3、代理类(业务增强类)
/** * 代理类(代替你完成工作,可以附加功能) */ public class HouseAgent implements IBuyHouse{ private IBuyHouse iBuyHouse; public HouseAgent(IBuyHouse iBuyHouse) { this.iBuyHouse = iBuyHouse; } @Override public void buyHouse() { // 1、代替你完成工作 iBuyHouse.buyHouse(); // 2、代理类新增的功能 System.out.println("倒卖你的信息"); } }4、测试类
/** * 测试类 */ public class Test { public static void main(String[] args) { HouseDelegation delegation = new HouseDelegation(); // 1、将你要做的事情,传递给代理对象 HouseAgent agent = new HouseAgent(delegation); // 2、执行代理类方法 agent.buyHouse(); } }优点:
代理使客户端不需要知道实现类是什么,怎么做的,而客户端只需知道代理即可(解耦合),对于如上的客户端代码,newUserManagerImpl()可以应用工厂将它隐藏,如上只是举个例子而已。
缺点:
1)代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
2)代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。如上的代码是只为UserManager类的访问提供了代理,但是如果还要为其他类如Department类提供代理的话,就需要我们再次添加代理Department的代理类。
通俗点的解释:
优点:
1、实现解耦,不需要知道怎么做,只知道代理即可。
缺点:
1、大量的重复代码,改代码,需要维护的量大。
2、每个类都写代码,程序太大时,无法实现。
三、动态代理
使用动态代理,我们最大的改变就是不需要定义一个个的代理类了。最重要的是获取到代理对象,有了代理对象,我们就可以直接调用代理对象了。
1、JDK动态代理类
JDK动态代理不仅可以代理有接口有实现类的情况,也可以代理只有接口没有实现类的情况。
使用JDK动态代理无需引入任何外部的jar包,JDK已经给我们提供了一种获取代理对象的API,只需要我们传入相关信息,它就可以返回我们需要的代理对象。
java.lang.reflect.Proxy类的定义如下:
java.lang.reflect.InvocationHandler接口的定义如下:
//Object proxy:被代理的对象 //Method method:要调用的方法 //Object[] args:方法调用时所需要参数 public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }1.1、有接口有委托类的情况
1、接口定义要做的事情
public interface IBuyHouse { // 定义要做的事情 public void buyHouse(); }2、具体用户实现类
/** * 用户实现类(也就是自己做,需要干的事情) */ public class HouseDelegation implements IBuyHouse{ @Override public void buyHouse() { System.out.println("去选房,购房."); } }3、代理类(业务增强类)
package com.lydms.testPro; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * 功能增强类 */ public class MyProxyPlus implements InvocationHandler { // 把委托对象传递进来进行增强 private Object object; public MyProxyPlus(Object object) { this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 1、执行原来的业务逻辑 Object result = method.invoke(object, args); // 2、执行增强逻辑 System.out.println("对原有的功能进行增强"); return result; // 如果原有业务逻辑有返回值别忘了返回 } }4、测试类
import java.lang.reflect.Proxy; /** * 测试类 */ public class test { public static void main(String[] args) { // 获取IBuyHouse的代理对象 HouseDelegation houseDelegation = new HouseDelegation(); // 1、将你要做的事情,传递给代理对象(并做功能增强MyProxyPlus) IBuyHouse iBuyHouse = (IBuyHouse) Proxy.newProxyInstance(houseDelegation.getClass().getClassLoader(), houseDelegation.getClass().getInterfaces(), new MyProxyPlus(houseDelegation)); // 2、执行代理类方法 iBuyHouse.buyHouse(); } }总结:
同样进行了增强,是不是代理类不见了呢!!!!这就是动态代理的好处,不需要你定义代理类了,你只需要能拿到代理对象就可以
1.2、仅有接口的情况
假如说上面我们只定义了IBuyCar接口和IBuyHouse接口,没有委托类(实现类),也是可以玩的。定义一个InvocationHandler接口的实现,用于写业务逻辑,你把所有的业务逻辑写在invoke方法中就行了
1、接口定义要做的事情
public interface IBuyHouse { // 定义要做的事情 public void buyHouse(); }2、代理类(业务增强类)
/** * 功能增强类 */ public class MyProxyPlus implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("对原有的功能进行增强"); return null; } }3、测试类
import java.lang.reflect.Proxy; /** * 测试类 */ public class Test { public static void main(String[] args) { // 1、将你要做的事情,传递给代理对象(并做功能增强MyProxyPlus) IBuyHouse iBuyHouse = (IBuyHouse) Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[]{IBuyHouse.class}, new MyProxyPlus()); // 2、执行代理类方法 iBuyHouse.buyHouse(); } }总结:
业务逻辑从无到有不也是一种增强嘛!是不是代理类不见了而且连实现类都不需要了呢!!!!这就是我们Mapper动态代理的底层原理(只要定义接口,不需要写实现类)
2、CGLIB动态代理
Java中的动态代理包括JDK动态代理和CGLIB动态代理。使用这两种代理方式我们都可以不用定义代理类,区别在于使用JDK动态代理必须有一个接口类,使用CGLIB动态代理不需要接口类。
所以如果你要对一个实现了接口的类进行业务增强就用JDK动态代理,如果就对一个普通类进行业务增强就用CGLIB动态代理。如下
1、cglib是第三方jar,因此需要引入jar包
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2.2</version> </dependency>2、具体用户实现类
public class Book { public void addBook() { System.out.println("新增图书..."); } }3、测试类
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class Test { public static void main(String[] args) { Book book = new Book(); // 1、获取book对象的代理对象, Book bookProxy = (Book) Enhancer.create(book.getClass(), new MethodInterceptor() { Object obj = null; // 2、都是对业务逻辑的增强 @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("日志开始........................"); obj = method.invoke(book, objects); System.out.println("日志结束........................"); return obj; } }); // 3、执行方法 bookProxy.addBook(); } }结果:
总结:
cglib动态代理其实就是把原有对象传进去进行方法拦截,拦截到之后进行逻辑增强
三、总结