Java的代理就是客户类不再直接和委托类打交道,而是通过一个中间层来访问,这个中间层就是代理。
使用代理的优势
- 可以隐藏委托类的实现
可以实现客户类与委托类的解耦,在不修改委托类代码的情况下能够做一些额外的处理。
现实案例
工厂:委托类,生产玩具
商店:代理类,出售玩具,额外操作:礼盒包装,价格标签等
客户:客户类,购买玩具静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同的父类。 ```java public interface HelloService {
public void sayHello();
}
// 委托类 public class HelloServiceImpl implements HelloService {
public void sayHello() {
System.out.println("Hello");
}
}
// 代理类 public class HelloServiceImplProxy implements HelloService {
private HelloService target;
public HelloServiceImplProxy(HelloService target) {
this.target = target;
}
public void sayHello() {
System.out.println("代理类做了一些事...");
target.sayHello(); // 执行委托类的方法
System.out.println("代理类完成了一些事...");
}
}
public class Application { public static void main(String[] args) { // 目标对象 HelloService target = new HelloServiceImpl(); // 代理对象,把目标对象传给代理对象,建立代理关系 HelloServiceImplProxy proxy = new HelloServiceImplProxy(target); proxy.sayHello(); // 执行代理方法 } }
<a name="htDzk"></a>
#### 静态代理的特点
1、可以做到不修改目标对象的功能前提下,对目标功能扩展<br />2、缺点:因为代理对象需要与目标对象实现同一个接口,所以会有很多的代理类。同时,一旦接口增加方法,委托对象与代理对象都要维护。 (代码耦合度较高)。
<a name="eAyIh"></a>
### JDK动态代理
<a name="a0IWH"></a>
#### 动态代理特点
1、代理对象不需要实现接口。<br />2、代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(反射)。
<a name="Kl76q"></a>
#### JDK中生成代理对象的API
代理类所在包: java.lang.reflect.Proxy<br />JDK实现代理只需要使用newProxyInstance方法
```java
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvacationHandler handler);
## classLoader : 指定当前目标对象使用类加载器,获取类加载器的方法是固定的
## Class<?>[] interfaces: 目标对象实现的接口类型,使用泛型方法确认类型
## InvacationHandler: 事件处理,执行目标对象方法时,会触发时间处理器的方法,会把当前执行目标对象的方法
作为参数传入。
代码示例
public class ProxyFactory implements InvacationHandler {
// 目标对象
private Object target;
public class ProxyFactory(Object target) {
this.target = target;
}
// 给目标对象动态生成代理对象
public Object newProxyInstance() {
return Proxy.newProxyInstance(
//指定代理对象的类加载器
target.getClass().getClassLoader(),
// 代理对象需要实现的接口,可以同时指定多个接口
target.getClass().getInterfaces(),
// 方法调用的实际执行者,代理对象的方法调用都会转发到这里
this
);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("invoke before");
Object result = method.invoke(obj, args);
System.out.println("invoke after");
return result;
}
}
// 测试动态代理类
public class Application {
public static void main(String[] args) {
ProxyFactory proxy = new ProxyFactory(new HelloServiceImpl());
HelloService helloService = (HelloService) proxy.newProxyInstance();
helloService.sayHello();
}
}
## 该实例,调用ProxyFactory类的newProxyInstance方法来获取一个代理类实例。这个代理类实现了我们指定
的接口并且会把方法调用分发到指定的调用处理器。
## 首先通过newProxyInstance方法获取代理类的实例,之后可以通过这个代理类的实例调用代理类的方法,对代理
类的方法调用都会调用中间类(实现invocationHandler的类)的invoke方法,在invoke方法中抵用委托类(目标对象)
的对应方法,然后加上自己的处理。
Java动态代理最大特点就是动态生成的代理类和委托类实现了同一个接口。java动态代理其实内部是通过反射机制实现的,也就是已知的一个对象,在运行的时候动态调用它的方法,并且调用的时候还可以加上一些自己的逻辑在里面。
JDK动态代理有个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现。
Cglib动态代理
maven引入cglib包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>
cglib代理是针对类来实现代理的,原理是对指定的委托类生成一个子类并重写其中业务方法来实现代理。代理类对象是由Enhancer创建的。
cglib创建动态代理类模式是:
- 查找目标类上的所有非final的public类型的方法(final的不能被重写)
- 将这些方法的定义转成字节码。
- 将组成的字节码转换成相应的代理的Class对象然后通过反射获得代理类的实例对象。
实现MethodInterceptor接口,用来处理对代理类上所有方法的请求
代码示例
```java // 委托类,是一个简单类 public class CglibHelloClass { /**
- 方法1
- @param userName
@return */ public String sayHello(){ System.out.println(“目标对象的方法执行了”); return “sayHello”; }
public String sayByeBye(){ System.out.println(“目标对象的方法执行了”); return “sayByeBye”; } }
public class ProxyFactory implements MethodInterceptor {
// 维护目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
// 给目标对象创建一个代理对象
public Object getProxyInstance() {
// 工具类
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(target.getClass());
// 设置回调函数
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
throw Throwable {
System.out.println("方法调用之前");
Object o = proxy.invokeSuper(obj, args);
System.out.println("方法调用之后");
return o;
}
//测试类 public class MainCglibProxy { public static void main(String[] args) { ProxyFactory proxy = new ProxyFactory(new CglibHelloClass()); CglibHelloClass cglibHelloClass = (CglibHelloClass) proxy.newProxyInstance(); cglibHelloClass.sayHello(); } } ```
总结
静态代理:委托类和代理类实现同一个接口,然后在代理类真正实现类,并且静态代理的关系在编译期间就确定了。适合代理类较少且确定的情况。
动态代理:委托类和代理类的关系是在运行期间确定的。提供更大的灵活性。
- JDK动态代理所用到的代理类在程序调用到代理类对象时才由JVM真正创建,JVM根据传进来的业务实现类对象以及方法名,动态创建了一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用。
JDK动态代理和Cglib动态代理区别
- JDK动态代理基于Java反射机制实现,必须要实现了接口的业务类才能用这个方法生成代理对象。
- Cglib动态代理基于ASM框架通过生成业务类的子类来实现的。
描述代理的几种方式,分别说出优缺点?
- 静态代理:委托类和代理类实现同一个接口,在代理对象中指向目标实际对象的实例,这样对外暴露的是代理对象而不是目标实际对象。关系在编译期间已确定。
- 优点:可以很好的保护实际对象的业务逻辑被暴露,增强安全性。
- 缺点:不同的接口要有不同的代理类实现,会很冗余。
- JDK动态代理:实现InvacationHandler接口,重写invoke方法边可以完成代理的实现。
jdk动态代理是利用反射生成代理类Proxyxx.class代理类字节码,并生成对象。
- 优点:解决了静态代理中冗余的代理实现类的问题。
- 缺点:JDK动态代理是基于接口设计实现的,如果没有接口,会抛出异常。
- CGLIB代理:主要针对没有接口的类实现代理。采用非常底层的字节码技术,其原理是通过字节码为一个类创建子类,并在子类用方法拦截的结束拦截所有父类方法的调用,顺势植入横切逻辑,来完成动态代理的实现。
实现方式是实现MethodInterceptor接口,重写intercept方法,通过Enhancer类的回调方法来实现。
- 优点:没有接口也能实现动态原理,采用字节码增强技术,性能也不错。
- 缺点:技术实现相对难理解些。
对于单例的对象,因为无需频繁创建对象,用cglib合适(因为cglib在创建代理对象时花费的时间比JDK多得多。),反之,使用JDK方式要更为合适。
cglib由于是采用动态创建子类的方法,对于final方法,无法进行代理。