代理模式
代理模式的概念
代理模式在不改变原有类的基础之上,可以对类中的方法进行扩展。其中的核心业务流程还是通过委托类进行完成,但是在业务处理之前可以增加 一个请求过滤,请求校验,业务处理之后的一些后续处理方法,==侧重于对业务流程的一个控制。==而且代理模式的代理关系是确定的,用户不需要额外的指定实际的委托类。
利用代理模式可以保证程序高内聚的特点,比如在Spring AOP的面向切面编程当中,通过代理机制对目标类的方法扩展,实现代码织入。
相关类图
静态代理和动态代理
静态代理的代理类在编译时就已经存在于代理类的字节码中,而动态代理的代理类是在程序的运行过程当中,通过反射机制动态生成的。
静态代理
优点:实现简单,理解简单
缺点:需要代理所有方法,不想代理的方法也需要代理
程序实现:
- 先定义一个统一接口
package Proxy;
/**
* 打印机功能
*/
public interface Printer {
void print();
void input();
}
2、定义具体的核心业务处理类(委托类)
package Proxy;
public class PrinterImpl implements Printer{
@Override
public void print() {
System.out.println("打印机正在打印输出");
}
@Override
public void input() {
System.out.println("打印机正在接受输入");
}
}
3、创建静态代理类
package Proxy;
/**
* 静态代理和委托者需要实现同样的接口
*/
public class StaticProxy implements Printer{
Printer printer = null;
public StaticProxy(Printer printer){
this.printer = printer;
}
@Override
public void print() {
System.out.println("先检查上一个任务是否完成");
printer.print();
}
@Override
public void input() {
//这个方法其实不需要代理,但是静态代理都需要代理
printer.input();
}
}
4、定义静态工厂,代理关系已经确定了,不需要用户传入不同的委托类
package Proxy;
/**
* 静态代理工程
*/
public class StaticProxyFactory {
public static Printer getInstance(){
return new StaticProxy(new PrinterImpl());
}
}
5、客户端调用
package Proxy;
public class Client {
public static void main(String[] args) {
Printer instance = StaticProxyFactory.getInstance();
instance.print();
instance.input();
}
}
java动态代理
java提供远程的动态代理类Proxy,动态代理可以只代理需要代理的方法,不需要代理的方法可以不管,简化了编码
java是基于接口实现的动态代理,如果委托类没有实现接口,动态运行时会报错。
相关API
Proxy.newInstance(classLoader,interfaces,invocationHandler)
项目 | Value |
---|---|
classLoader | 指定动态代理类使用的类加载器 |
interfaces | 委托类实现的接口数组 |
invocationHandler | 调用目标方法,扩展功能代码 |
程序实现
1、定义接口
package Proxy.javaActive;
/**
* 打印机功能
*/
public interface Printer {
void print();
void input();
}
package Proxy.javaActive;
/**
* 委托类的另外一个接口
*/
public interface Email {
void send();
void receive();
}
2、定义readSubject
package Proxy.javaActive;
public class PrinterImpl implements Printer,Email{
@Override
public void print() {
System.out.println("打印机正在打印输出");
}
@Override
public void input() {
System.out.println("打印机正在接受输入");
}
@Override
public void send() {
System.out.println("通过打印机发送邮件");
}
@Override
public void receive() {
System.out.println("通过打印机接收邮件");
}
}
3、定义动态代理
package Proxy.javaActive;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 定义java动态代理
*/
public class ActiveProxy {
//定义实际干活者
Printer printer = null;
public ActiveProxy(Printer printer) {
this.printer = printer;
}
public Object createProxy(){
Object o = Proxy.newProxyInstance(ActiveProxy.class.getClassLoader(), printer.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equals("input")){
Object invoke = method.invoke(printer, args);
return invoke;
}else {
System.out.println("除了input方法外,其他方法都需要前置处理");
Object invoke = method.invoke(printer, args);
//通过反射调用对象的方法,参数是对象的实例,以及传入方法的参数
//还可以调用proxy代理里面的一些方法,这样就不用重复写业务逻辑了
return invoke;
}
}
});
return o;
}
}
4、动态代理工厂
package Proxy.javaActive;
public class ActiveFactory {
public static Object getInstance(){
return new ActiveProxy(new PrinterImpl()).createProxy();
}
}
5、客户端
package Proxy.javaActive;
public class Client {
public static void main(String[] args) {
//获取Printer的动态代理
Printer instance = (Printer) ActiveFactory.getInstance();
instance.print();
instance.input();
//获取Email的动态dialing
Email email = (Email) ActiveFactory.getInstance();
email.send();
email.receive();
}
}
CGLIB动态代理
cglib动态代理基于继承实现,无论被代理者是否实现了接口,其中的方法都可以代理
spring AOP的代理可以使用java动态代理,也可以使用CGLIB动态代理
package cn.ruanwenfu.proxy;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CGLibProxy {
private static FBB fbb = new FBB();
private static Star getProxy(){
//获取增强器
Enhancer enhancer = new Enhancer();
//可以设置代理的接口,也可以不设置,反正不是基于接口实现
enhancer.setInterfaces(FBB.class.getInterfaces());
//设定父类为被代理者
enhancer.setSuperclass(FBB.class);
//设置回调函数,也就是方法作为参数传入,代理对象的所有方法都会走这个回调
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if("eat".equals(method.getName())){
System.out.println("方法停用");
return null;
}else{
System.out.println("权限检查");
Object invoke = method.invoke(fbb, args);
return invoke;
}
}
});
return (Star)enhancer.create();
}
public static void main(String[] args) {
Star proxy = CGLibProxy.getProxy();
proxy.sing();
proxy.eat();
}
}