1.静态代理
角色分析:
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
- 客户:访问代理对象的人!
代码步骤:
- 接口
//租房
public interface Rent {
public void rent();
}
2.真实角色
//房东
public class Host implements Rent{
public void rent() {
System.out.println("房东出租房子!");
}
}
3.代理角色
public class Proxy implements Rent{
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
public void rent() {
host.rent();
seeHouse();
sign();
fee();
}
//看房
public void seeHouse(){
System.out.println("中介带着看房子!");
}
//签合同
public void sign(){
System.out.println("和中介签署租赁合同!");
}
//收费用
public void fee(){
System.out.println("中介收取费用!");
}
}
4.客户端访问代理角色
public class Client {
public static void main(String[] args) {
//房东要出租房子
Host host = new Host();
// host.rent();
//代理,中介帮房东出租房子,并且代理角色一般会有一些附属操作!
Proxy proxy = new Proxy(host);
//不用面对房东,直接找中介租房即可!
proxy.rent();
}
}
代理模式的好处:
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
- 公共角色就交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
缺点:
- 一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率会变低~
2.静态代理加深理解
代码步骤:
- 接口
接口描述抽象事务。例子:一件事务有四种行为(操作)
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
2.真实角色
真实角色实现接口所描述的事务。例子:一个人有权利去实现这个事务的四种行为(操作)
//真实角色
public class UserServiceImpl implements UserService{
public void add() {
System.out.println("增加了一个用户!");
}
public void delete() {
System.out.println("删除了一个用户!");
}
public void update() {
System.out.println("修改了一个用户!");
}
public void query() {
System.out.println("查询了一个用户!");
}
}
3.代理角色
代理角色没有直接权利去实现接口描述的事务,但是可以帮真实角色去完成真实角色所要完成的四种行为(操作),好比操控真实角色去完成他该做的事,事实上完成四种行为的,还是真实角色。代理角色若想控制真实角色,首先他自己也要对这四种行为有所了解,所以要创建四种行为的代理方法(不需要真实完成,只需要在方法里通过真实角色对象调用事务行为)
(我也好想被代理啊!!)
public class UserServiceProxy implements UserService{
//代理角色获取真实角色的信息
private UserServiceImpl userService;
//set方法用于核对客户端上的真实角色信息,是否与代理角色所获取的真实角色信息是否一致
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
public void add() {
log("add");
userService.add();
}
public void delete() {
log("delete");
userService.delete();
}
public void update() {
log("update");
userService.update();
}
public void query() {
log("query");
userService.query();
}
public void log(String msg){
System.out.println("[Debug] 使用了一个"+msg+"方法");
}
}
4.客户端访问代理角色
客户端,相当于一个平台。真实角色将自己的操控权放到这个平台上,然后代理角色会来获得这个操控权,进而控制真实角色去完成四种行为。
(所以代理角色需调用set方法来核对客户端上的真实角色信息是否与自己所拥有的真实角色信息是否一致)
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);
proxy.delete();
}
}
聊聊AOP
3. 动态代理
动态代理和静态代理角色一样
动态代理的代理类是动态生成的,不是我们直接写好的!
动态代理分为两大类:基于接口的动态代理,基于类的动态代理
基于接口 — JDK动态代理【我们在这里使用】
基于类:cglib
java字节码实现:javassist
需要了解两个类:Proxy:代理;InvocationHandler:调用处理程序。
代码步骤:
- 接口
public interface Rent {
public void rent();
}
2.真实角色
public class Host implements Rent{
public void rent() {
System.out.println("房东要出租房子!");
}
}
3.ProxyInvocationHandler类
只是一个处理程序,用于生成代理类
//我们会用这个类,自动生成代理类!
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//生成得到代理类
public Object getProxy(){//死代码,使用时只需要更改rent就可以
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
rent.getClass().getInterfaces(),this);
}
//处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质,就是使用反射机制实现!
Object result = method.invoke(rent, args);
seeHose();
fee();
return result;
}
public void seeHose(){
System.out.println("中介带着看房子!");
}
public void fee(){
System.out.println("中介收取费用!");
}
}
4.测试
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理角色:现在没有
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//通过调用程序处理角色来处理我们要调用的接口对象!
pih.setRent(host);
Rent proxy = (Rent) pih.getProxy(); //这里的proxy就是动态生成的,我们并没有写
proxy.rent();
}
}
在此,我们可以提炼出ProxyInvocationHandler作为工具类
//用这个类自动生成代理类!
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
}
//处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
Object result = method.invoke(target, args);
return result;
}
public void log(String msg){
System.out.println("[Debug] 使用了一个"+msg+"方法");
}
}
4.InvocationHandler接口和Proxy类详解
InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序;在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法。
看下官方文档对InvocationHandler接口的描述:
{@code InvocationHandler} is the interface implemented by
the <i>invocation handler</i> of a proxy instance.
<p>Each proxy instance has an associated invocation handler.
When a method is invoked on a proxy instance, the method
invocation is encoded and dispatched to the {@code invoke}
method of its invocation handler.
当我们通过动态代理对象调用一个方法时候,这个方法的调用就会被转发到实现InvocationHandler接口类的invoke方法来调用,看如下invoke方法:
/**
* proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0
* method:我们所要调用某个对象真实的方法的Method对象
* args:指代代理对象方法传递的参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
Proxy类就是用来创建一个代理对象的类,它提供了很多方法,但是我们最常用的是newProxyInstance方法。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
这个方法的作用就是创建一个代理类对象,它接收三个参数,我们来看下几个参数的含义:
loader:一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载
interfaces:一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。
h:一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。
动态代理的好处:
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
- 公共角色就交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务
- 一个动态代理类可以代理多个类,只要是实现了同一个接口即可!