- 静态代理
- 基于JDK的动态代理
静态代理
角色分析
抽象角色:一般使用接口或抽象类来解决
真实角色:被代理的角色
代理角色:代理真实角色,然后做一些附加操作
客户:访问代理对象的人
现在有一个类,封装了真实角色行为
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("代码原有增加逻辑");
}
@Override
public void del() {
System.out.println("代码原有删除逻辑");
}
@Override
public void update() {
System.out.println("代码原有更新逻辑");
}
@Override
public void query() {
System.out.println("代码原有查询逻辑");
}
}现在要增加打印日志的功能,如果直接在每个方法中修改,则违反了开闭原则。
需要一个代理类来实现
代理类在构造或者setter方法中接收被代理的对象,实现被代理对象的方法,在此基础上,又添加了自己的功能。
public class Proxy implements UserService {
private UserServiceImpl userService;
public Proxy() {
}
public Proxy(UserServiceImpl userService) {
this.userService = userService;
}
@Override
public void add() {
log("add");
userService.add();
}
@Override
public void del() {
log("del");
userService.del();
}
@Override
public void update() {
log("update");
userService.update();
}
@Override
public void query() {
log("query");
userService.query();
}
private void log(String msg) {
System.err.println("用户使用了"+msg+"方法");
}
}客户调用代理
public class Client {
public static void main(String[] args) {
UserServiceImpl arg = new UserServiceImpl();
Proxy proxy = new Proxy(arg);
proxy.add();
proxy.del();
}
}静态代理的优点是符合开闭原则,不更改原有代码,弊端是每次增加一个被代理类,必然要多加一个代理类,导致代码量增加,开发效率变低。动态代理解决了这个弊端
基于JDK的动态代理(需要接口)
同样的,有userService 的接口和实现
public interface UserService {
void add();
void del();
void update();
void query();
}
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("代码原有增加逻辑");
}
@Override
public void del() {
System.out.println("代码原有删除逻辑");
}
@Override
public void update() {
System.out.println("代码原有更新逻辑");
}
@Override
public void query() {
System.out.println("代码原有查询逻辑");
}
}新建一个代理类
//动态代理类需要实现InvocationHandler接口 ,重写invoke方法
public class JdkProxy<T> implements InvocationHandler {
//持有一个被代理的对象
private T target;
//在构造方法中传入被代理对象
public JdkProxy(T target) {
this.target = target;
}
@SuppressWarnings("unchecked")
//不写泛型用object也没有问题
public T getProxy() {
//返回一个代理对象,代理target类,参数含义见如下下方代码块
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before(method);
//target为当前被代理对象,args为对象调用方法的方法参数(反射)
method.invoke(target, args);
after(method);
return null;
}
public void after(Method method) {
System.out.println("调用" + method.getName() + "方法后");
}
public void before(Method method) {
System.out.println("调用" + method.getName() + "方法前");
}
}
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader, //当前对象类加载器
Class<?>[] interfaces, //当前对象接口
InvocationHandler h) //代理类本身调用代理类
public class Client {
public static void main(String[] args) {
JdkProxy<UserService> jdkProxy = new JdkProxy<UserService>(new UserServiceImpl());
//如果没有泛型制约,此处不会要求强转,需要自己有意识的转成被代理类,才能调用接口方法
UserService proxy = (UserService) jdkProxy.getProxy();
proxy.add();
proxy.query();
}
}如果现在我有一个admin类要实现加入日志功能,只需要在构造中传入该类就可以,无需再针对admin类建立新的代理类