静态代理
静态代理角色分析
- 抽象角色 (共有的方法): 一般使用接口或者抽象类来实现
- 真实角色(房东) : 被代理的角色
- 代理角色(中介) : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
- 客户(租客): 使用代理角色来进行一些操作
客户与真实角色之间不会直接接触,都是通过代理角色
还有一种结构图是从房东角度描述的
代码实现
Rent . java 即抽象角色(抽象方法)
public interface Rent {
public void rent();
}
public class Host implements Rent{
public void rent() {
System.out.println("房屋出租");
}
}
public class Proxy implements Rent {// 真实角色
private Host host;
public Proxy() { }
public Proxy(Host host) {
this.host = host;
}
public class Proxy implements Rent { //中介
private Host host;
public Proxy() { }
public Proxy(Host host) {
this.host = host;
//出租房
@Override
public void rent(){
seeHouse();host.rent();fare();}
//看房
public void seeHouse(){System.out.println("带房客看房")}
//收中介费
public void fare(){System.out.println("收中介费");}}
}
//客户类,一般客户都会去找代理!
public class Client {
public static void main(String[] args) {
//房东要租房Host host = new Host();
//中介帮助房东Proxy proxy = new Proxy(host);
//你去找中介!proxy.rent();
}}
分析:在这个过程中,你直接接触的就是中介,就如同现实生活中的样子,你看不到房东,但是你依旧租到了房东的房子通过代理,这就是所谓的代理模式,
静态代理的好处:
- 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
- 公共的业务由代理来完成 . 实现了业务的分工 ,
- 公共业务发生扩展时变得更加集中和方便 .
缺点 :
- 类多了 , 就要相应的增加代理类 , 工作量变大了 . 开发效率降低
解决方法:我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !
静态代理再理解运用
增加一个业务接口UserService
(抽象角色),有增删改查(add,delete,update,query
)4个方法
创建一个真实角色UserServiceImpl
来实现业务接口。
假如我们要增加一个日志功能方法,如果去改业务接口或者业务实现类都会有些麻烦。
这时可以通过静态代理可以实现不改变原有代码就可增加新的功能
public class UserServiceProxy implements UserService {
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService; }
public void add() {
log("add"); userService.add(); }
public void delete() {
log("delete"); userService.delete(); }
public void log(String msg){
System.out.println("执行了"+msg+"方法"); }
}
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);
proxy.add();
客户类测试时增加以下,即可再不改变业务接口及业务类的情况下增加功能
输出:先输出日志:执行了add方法 然后输出add方法
静态代理总结
- 将房东与中介都需要实现的功能提取为接口,房东与中介都实现该接口(因为这是房东和中介一定要实现的最基本也是最重要的功能)
- 中介持有一个房东对象,然后中介创建自己增加的功能方法。
- 每次调用房东的重写方法,方法体内会让房东属性去调用基本功能。然后重写方法再调用中介增加的方法。
- 前三步就讲明白了代理模式。最后测试即是:房东需要出租房,就创建房东对象。房东找中介,就是创建中介对象并传入房东对象。最后中介实现出租功能。
动态代理
JAVA动态代理是通过反射实现的
- 动态代理的角色(即结构)和静态代理的一样 .
- 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
- 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
- 基于接口的动态代理——JDK动态代理
- 基于类的动态代理—cglib
- 现在用的比较多的是通过java字节码的方式实现(javasist)动态代理
但是三种的原理还是差不多的,就语法有所区别
-
JDK动态代理
JDK动态代理和动态表面上最大的区别就是静态代理是通过代理对象的调用实现增强。而动态代理是被代理对象直接调用自己的方法,但是方法不知不觉的在内部被增强了
- 静态代理如果要对整个接口的所有方法都增强,需要对每一个方法都进行重写。而动态代理直接就是一次性对整个类的方法进行增强
- jdk动态代理的核心是
**InvocationHandler **
接口和**Proxy **
类 jdk动态代理只能增强实现了某个接口的类
public class Host implements Rent{ @Override public void rent() { System.out.println("房屋出租"); } }
```java public class ProxyInvocationHandler implements InvocationHandler { private Rent rent;//被代理的接口 public void setRent(Rent rent) {
this.rent = rent;
} public Object getProxy(){
//newProxyInstance第一个参数为类加载器,第二个:被代理类实现的接口,第三:实现了 InvocationHandler 接口的对象 return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(),this);
}
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //method可以获得当前被增强方法的信息
log.(method.getName()) System.out.println("带房客看房"); //前置增强
//invoke(代理类,增强方法名,增强方法参数)
Object result = method.invoke(rent, args); //这里表示原本的方法的执行位置 System.out.println("收中介费"); //后置增强 return result; //返回对象,该对象能被转为被代理对象。
} }
```java
public class Client {
public static void main2(String[] args) {
//真实角色
Host host = new Host();
//代理实例的调用处理程序
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setRent(host); //将真实角色放置进去!
Rent proxy = (Rent)pih.getProxy(); //动态生成对应的代理类!
proxy.rent(); //看起来只会输出原本方法,但是实际输出却被增强 了
}
public static void main(String[] args) { //静态代理
//真实角色
Host host = new Host();
//代理实例的调用处理程序
ProxyInvocationHandler2 pih=new ProxyInvocationHandler2(); //静态代理的中介
pih.setRent(host);
pih.rent();
}
}
Cglib动态代理
与jdk动态代理对比
- jdk动态代理的类必须实现了某个接口,而Cglib是通过继承方式实现代理(创建被代理类的实现类)注意jdk动态代理也可以直接代理接口
- 总结就是jdk动态代理的本质还是静态那一套:代理类实现被代理类的接口得到代理类,并持有个被代理类的对象。然后是
**前-持有对象调原本方法-后**
- 而cglib本质是创建被代理类的子类作为代理类,不再需要持有被代理类对象,直接
**前-super.原本方法-后**
- 如果需要代理的对象的体系较复杂(实现了多个接口和较深的继承层次结构),Spring 使用 JDK 实现动态代理时有可能出现问题
- 总结就是jdk动态代理的本质还是静态那一套:代理类实现被代理类的接口得到代理类,并持有个被代理类的对象。然后是
- 大部分时候都是jdk动态代理更优秀,不过cglib适用的情况更多
cglib不能不能代理声明为 final 类型的类和方法。(因为final方法无法被重写,final类无法被继承)
public interface Animal { void eat(); } //----------------------------- public class Cat implements Animal { public void eat() { System.out.println("猫吃鱼"); } } //------------------ public class CatProxy implements Animal { private Cat target; public CatProxy() { this.target = new Cat(); } @Override public void eat() { System.out.println("执行前调用"); target.eat(); System.out.println("执行后调用"); } } //使用CatProxy的调用eat,就实现了既调用了原本的eat,又进行了增强
```java public class Cat {
public void eat() {
System.out.println("猫吃鱼");
} } public class CatProxy extends Cat {
public CatProxy() { }
@Override public void eat() {
System.out.println("执行前调用"); super.eat(); System.out.println("执行后调用");
}
} //原理与jdk动态代理类似,不过一个是实现并调用原本的方法,一个是重写并调用原有的方法
<a name="cmk2P"></a>
### Cglib具体使用
- 首先需要导入一个依赖:
```xml
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
public class AliSmsService {
public String send(String message) {
System.out.println("send message:" + message);
return message;
}
}
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class DebugMethodInterceptor implements MethodInterceptor {
/**
* @param o 代理对象(增强的对象)
* @param method 被拦截的方法(需要增强的方法)
* @param args 方法入参
* @param methodProxy 用于调用原始方法
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//前置增强
System.out.println("before method " + method.getName());
Object object = methodProxy.invokeSuper(o, args);
//后置增强
System.out.println("after method " + method.getName());
return object;
}
}
import net.sf.cglib.proxy.Enhancer;
public class CglibProxyFactory {
public static Object getProxy(Class<?> clazz) {
// 创建动态代理增强类
Enhancer enhancer = new Enhancer();
// 设置类加载器
enhancer.setClassLoader(clazz.getClassLoader());
// 设置被代理类
enhancer.setSuperclass(clazz);
// 设置方法拦截器
enhancer.setCallback(new DebugMethodInterceptor());
// 创建代理类
return enhancer.create();
}
}
AliSmsService aliSmsService = (AliSmsService) CglibProxyFactory.getProxy(AliSmsService.class);
aliSmsService.send("java");
静态代理和动态代理的对比
- 灵活性 :动态代理更加灵活,不需要必须实现接口,可以直接代理实现类,并且可以不需要针对每个目标类都创建一个代理类。另外,静态代理中,接口一旦新增加方法,目标对象和代理对象都要进行修改,这是非常麻烦的!
- JVM 层面 :静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。而动态代理是在运行时动态生成类字节码,并加载到 JVM 中的