设计模式之代理模式详解(java)
Posted 小样5411
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式之代理模式详解(java)相关的知识,希望对你有一定的参考价值。
一、代理模式
代理模式很重要,它是SpringAOP的底层实现!!!代理模式分为两类:静态代理和动态代理。其中动态就是用到反射。
参考视频(狂神说Java):代理模式
1.1 静态代理
举一个经典案例说明:房东与租客
描述:房东不想贴小广告费力找租客,带租客看房,租客也不想跟无头苍蝇一样找合适房子,于是就诞生了一个代理中介,房东只要把钥匙交给中介,然后带人看房,签合同的事情都中介搞定,房东不用操心这些,这就是代理。
首先我们先写一个理想情况,租客可以很容易的找到房东并租到房
//租房
public interface Rent {
void rent();
}
//租客
public class Customer {
public static void main(String[] args) {
//租客找房东租房
Landlord landlord = new Landlord();
landlord.rent();
System.out.println("租客找到房东,租了房子");
}
}
//房东
public class Landlord implements Rent{
@Override
public void rent() {
System.out.println("房东要出租房子");
}
}
但是理想是理想,租客往往都需要找中介,因为中介房源多,可以挑选的多,所以就出现代理(Proxy类)
package com.yx.proxy;
package com.yx.proxy;
public class Proxy {
private Landlord landlord;
public Proxy(){
}
public Proxy(Landlord landlord) {
this.landlord = landlord;
}
//中介拿着不同房东的房子钥匙,带租客看房
public void rent(){
seeHouse();
landlord.rent();
contract();
}
//中介带租客看多套房
public void seeHouse(){
System.out.println("中介带着租客看房");
}
//中介代理房东签合同
public void contract(){
System.out.println("中介代理房东签合同");
}
}
//租客
public class Customer {
public static void main(String[] args) {
Landlord landlord = new Landlord();
//代理房东租房
Proxy proxy = new Proxy(landlord);
proxy.rent();
}
}
总结:何为代理模式?
代理模式就是你看不见房东,通过代理中介就能租到房。实际开发也是,直接通过代理对象就能获取到结果,而不用去接触真实对象,也就是说,真实对象的操作更加纯粹,繁琐操作交给代理。你会发现被代理的真实角色房东类里面一直就只有rent方法没有变过,不会改变原有代码。比如这里房东只想把房子出租出去收钱,那么带人看房,签合同这些麻烦事就给代理去做,自己就做最基本的收租工作。
再举一个例子,用户常做的操作就是增删改查,但是此时我们想在每个增删改查前都加一个打印日志功能,这时候就可以用代理,加入新功能,不改变原有功能,这就像Spring AOP中的切面,切入新功能,Spring AOP底层就是用代理模式。
//用户
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);
proxy.add();
}
}
//用户接口
public interface UserService {
void add();
void delete();
void update();
void query();
}
//用户接口
public interface UserService {
void add();
void delete();
void update();
void query();
}
public class UserServiceProxy implements UserService{
private UserServiceImpl userService;
//Spring中赋值推荐用set
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
@Override
public void add() {
log("add");
userService.add();
}
@Override
public void delete() {
log("delete");
userService.delete();
}
@Override
public void update() {
log("update");
userService.update();
}
@Override
public void query() {
log("query");
userService.query();
}
public void log(String msg){
System.out.println("[Debug]使用了"+msg+"方法");
}
}
运行Client
这样代理就没有改变原来的基本增删改查操作,而且增加了新操作(扩展操作),改动原有代码是大忌,因为可能导致公司原本正常执行的代码被你一改,大家都运行出错了,开发要符合开闭原则,对修改关闭,对新增开放。
上面还是自己手动写log方法,开发中肯定不能这么死板(写死了),要动态灵活,动态自动生成,所以下面讲动态代理
1.2 动态代理
先说明,动态代理底层都是反射、反射、反射!!!
动态代理分两类:
1、基于JDK的动态代理(接口)
2、基于Cglib的动态代理(类)
java.lang.reflect下有个代理类,叫Proxy,动态自动生成代理类还需要一个叫InvocationHandler接口,用该接口中唯一的invoke方法执行代理实例,并返回结果,这里有点蒙?举个例子就懂了
同样租房子案例,这次我们不写静态代理,写会自己创建代理的动态代理
//房东
public class Landlord implements Rent {
@Override
public void rent() {
System.out.println("房东要出租房子");
}
}
//租房
public interface Rent {
void rent();
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
private Rent rent;//被代理的类
public void setRent(Rent rent) {
this.rent = rent;
}
//生成得到代理类
public Object getProxy(){
//创建代理实例(通过反射获取参数):类加载器、被代理的接口、InvocationHandler接口
Object obj = Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
return obj;
}
//执行代理实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
Object res = method.invoke(rent, args);//invoke就是执行对应接口方法,所以这里讲rent放进去,类似AOP中的切点
contract();
return res;
}
public void seeHouse() {
System.out.println("中介带看房子");
}
public void contract(){
System.out.println("签合同");
}
}
注意:这里还是先写了Rent接口,作为代理接口,如果要改成万能代理,只要把private Rent rent
改成private Object object
即可
public class Client {
public static void main(String[] args) {
Landlord landlord = new Landlord();//真实角色:房东
//代理角色:还没有,可通过ProxyInvocationHandler创建出来,以前自己写代理,现在可以通过这个类动态创建
ProxyInvocationHandler handler = new ProxyInvocationHandler();
handler.setRent(landlord);//设置代理的真实角色
Rent proxy =(Rent) handler.getProxy();//getProxy动态生成代理类
proxy.rent();//就是执行invoke
}
}
运行结果如下
抽象出万能代理类,也就是代码的封装复用
//自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;//被代理的类
public void setObject(Object target) {
this.target = target;
}
//生成得到代理类
public Object getProxy(){
//创建代理实例(通过反射获取参数):类加载器、被代理的接口、InvocationHandler接口
Object obj = Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
return obj;
}
//执行代理实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res = method.invoke(target, args);
return res;
}
}
运行后
换成其他真是角色,比如之前的UserServiceImpl
运行后
这只是证明可以用动态代理调用基本操作,如果需要新增扩展操作呢?比如加一个日志
运行Client
如果换成调用proxy.delete()
这样就做到万能通用,且不会改动之前写好的代码,都是新增的
总结动态代理:
动态代理就是通过重写InvocationHandler接口中invoke方法,并且用Proxy创建代理实例来创建动态代理的,这两个点是最重要的,一个创建一个执行
看完上面什么是代理,非常推荐没有阅读过Spring AOP四种实现方式的阅读一下这个文章
以上是关于设计模式之代理模式详解(java)的主要内容,如果未能解决你的问题,请参考以下文章