代理
代理模式是程序设计中常用的一种模式,通常用于生成一个代理类,间接执行委托类的方法。这样可以很好的保证委托类只实现基本的功能,而不需要和具体的业务产生关联,从而实现代码的高可用性、低耦合性和无代码侵入。为了实现代理类和委托类的行为一致性,他们通常会实现相同的接口。
就像租房的中介一样,房东和中介都有出租房屋的接口。房东的接口中只负责把房子出租出去,然后收房租。不同的中介则会进行不同的代理,有的中介只收取中介费,然后将房子出租;有的中介则将房子稍作装修出租,并且提供了相应的家政服务,不但可以收取中介费还可以收取家政服务费。这个例子生动但是好恶心,对黑中介深恶痛绝,哈哈。
按照代理的创建时期,可以将代理类分为:静态代理和动态代理。
静态代理与动态代理
概念:
静态代理:静态指的是代理类代码是由程序员手动创建或者通过其他工具生成,在程序运行之前通过编译代理类的java文件,就已经生成了.class文件。
动态代理:动态是相对于静态而言,代理类的.class文件在运行前是不存在的,在程序运行时,根据需要动态生成代理类的.class文件。始终没有代理类的java文件。
两者的根本区别:是否拥有代理类的java文件。
代码:
静态代理:
//提供出租的服务接口
public interface RendService {
void rend();
}
//房东出租房屋的服务实现
public class RendServiceImpl implements RendService {
@Override
public void rend() {
System.out.println("出租房屋,收取房租");
}
}
//中介提供的出租房屋的中介服务
public class ProxyRendServiceImpl implements RendService {
private RendService rendService;
public ProxyRendServiceImpl(RendService rendService){
this.rendService = rendService;
}
@Override
public void rend() {
System.out.println("装修房屋");
rendService.rend();
System.out.println("收取家政服务费");
}
}
//客户端调用
public class TestStaticProxy {
public static void main(String[] args) {
RendService rendService = new RendServiceImpl();
ProxyRendServiceImpl proxy = new ProxyRendServiceImpl(rendService);
proxy.rend();
}
}
运行结果:
优点:简单明了。
缺点:1、当有多个实现时,会出现大量需要代理的接口,编写大量代理类便成了繁重的工作。2、当出现业务调整,增加或者删除接口时,需要对所有代理类进行修改,造成了维护成本高。
动态代理(jdk代理实现):
//动态代理类
public class DynamicProxyRendServiceImpl implements InvocationHandler {
private Object targetObject;
public Object newInstance(Object targetObject){
this.targetObject = targetObject;
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("装修房屋");
Object invoke = method.invoke(targetObject, args);
System.out.println("收取家政服务费");
return invoke;
}
}
//客户端调用
public class TestDynamicProxy {
public static void main(String[] args) {
RendService rendService = new RendServiceImpl();
DynamicProxyRendServiceImpl proxyRendService = new DynamicProxyRendServiceImpl();
RendService proxy = (RendService)proxyRendService.newInstance(rendService);
proxy.rend();
}
}
运行结果:
优点:1、对于某一类功能的代理,可以使用同一个动态代理实现,能够减少开发工作量。2、后期维护简单,只需要维护动态代理类。
缺点:动态代理是通过java反射机制发挥作用的,所以理解比较困难。
常用于实现日志系统、事务、拦截器、权限控制业务。
jdk动态代理和cglib动态代理
Jdk动态代理:jdk代理要实现InvocationHandler接口,使用Proxy.newInstance()方法创建委托类的代理对象。
Cglib动态代理:cglib代理要实现MethodInterceptor接口,使用Enhancer对象的create()方法创建委托类的代理对象。
两者区别:使用jdk代理的类必须实现接口或者本身就是接口,比如@Service的应用、mybatis的mapper文件是;使用cglib代理的类必须是非final字段修饰的类。因为jdk代理是通过委托类实现的接口生成代理类,而cglib是通过继承委托类产生子类生成代理类。Jdk和cglib分别使用byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces)和byte[] bs = DefaultGeneratorStrategy.INSTANCE.generate(enhancer)生成.class文件,然后加载到jvm中,通过java反射机制使用class的构造方法产生代理类实例。
Cglib动态代理代码:
cglib 需要的maven配置:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.6</version>
</dependency>
//cglib代理类
public class CglibProxyServiceImpl implements MethodInterceptor {
private Object targetObject;
public CglibProxyServiceImpl(Object targetObject){
this.targetObject = targetObject;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib 代理");
return method.invoke(targetObject, objects);
}
}
//cglib客户端调用
public class TestCglibProxy {
public static void main(String[] args) {
RendService rendService = new RendServiceImpl();
Callback cglib = new CglibProxyServiceImpl(rendService);
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RendService.class);
enhancer.setCallback(cglib);
RendService proxy = (RendService)enhancer.create();
proxy.rend();
}
}
运行结果:
更多问题可以加公众号:代码小栈,期待为您解决更多问题