一、静态代理
代理
当客户对象(被代理对象)由于某些情况 不能直接访问 目标对象(例如 想请巴菲特吃饭 你应该跟它的秘书预约 而不能够直接打电话给巴菲特) 这时候可以提供一个中间类来访问目标对象。
代理的作用
保护了目标对象
避免了 直接对目标对象进行访问 在代理中可以对客户对象进行检索?
代理增强目标类
拓展目标对象的能力(巴菲特只会跟你吃饭 但是它的秘书可能会一些其它的 例如给他排时间表订机票)
代理模式可以减少耦合
避免了客户对象和目标对象的直接接触 提高了可扩展性 要加什么功能直接加给代理
增加代码量
在客户对象和目标对象之间增加了一个代理类
降低了程序速度
间接访问消耗一定的系统资源
增加了系统的复杂度
多了一个代理中间会多一个过程
代理实现
①目标对象与代理完成相同的事情(通常用实现同一个接口来完成)
public interface SellGoods {
void sellGoods();
}
②客户对象
//被代理角色 (国内某地的买家 因为不相信国内商品的质量而选择海外代购)
public class Buyer {
Proxy proxy=new Proxy(new SellerInForeign());
public void byGoodGoods()//发起买商品动作并且付钱
{
proxy.sellGoods();
}
}
③代理
//代理角色 (海外代购商) 完成交关税 确认商品真伪等其它工作
public class Proxy implements SellGoods{
private SellerInForeign seller=new SellerInForeign();//使用组合完成 卖商品给国内的客户这件事情
public Proxy() {
}
public Proxy(SellerInForeign seller) {
this.seller = seller;
}
@Override
public void sellGoods() {
payTax();
sure();
seller.sellGoods();
}//卖出商品
public void payTax()
{
System.out.println("the proxy pay taxes");
}
public void sure()
{
System.out.println(" the proxy makeSure goods is good");
}
}
④目标对象
public class SellerInForeign implements SellGoods{
//目标角色 (一个海外的商家 它只要完成卖商品就行了)
@Override
public void sellGoods() {
System.out.println("the seller sold goods");
}
}
⑤测试
public static void main(String[] args) {
Buyer buyer=new Buyer();
buyer.byGoodGoods();
}
初识AOP
AOP
面向切面的编程 通过代理模式的底层实现
现有一个程序已经测试通过上线(按照传统开发)
①写好Dao层接口
//传统开发 写好增删改查的接口去实现
public interface UserService {
void add();
void del();
void mod();
void que();
}
②实现类
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("had add a User");
}
@Override
public void del() {
System.out.println("had del a User");
}
@Override
public void mod() {
System.out.println("had mod a User");
}
@Override
public void que() {
System.out.println("had que a User");
}
}
③控制层
public class UserControl {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
userService.add();//测试通过
//当功能昨晚程序上线后 需要添加一个日志功能
}
}
当项目发布并且已经在市场上运行一段时间之后 , 项目经理下午吃饭的时候认为你为这个项目加一个日志功能 但是这个项目很大 你怕改了 实现类 会跑不来了 这个时候你在网上看了代理模式 你发现可以 增加一个代理 在 让代理去实现 日志 功能 基本的增删改查还是实现类去实现 并且不改动是实现类就不用苦苦的Debug 只要埋头写代理类就好了
④代理类
//添加一个代理 拓展UserService的功能 不改变原来的代码
public class UserServiceProxy implements UserService {
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}//使用Set方法方便Spring容器注入
@Override
public void add() {
log("add");
userService.add();
}
@Override
public void del() {
log("del");
userService.del();
}
@Override
public void mod() {
log("mod");
userService.mod();
}
@Override
public void que() {
log("que");
userService.que();
}
public void log(String msg) {
System.out.println("调用了" + msg + new Timestamp(System.currentTimeMillis()));
}
}
二、动态代理
动态代理和静态代理
它们的对象(角色)一样
代理不再是我们事先写好的(利用反射再运行时动态生成)
实现方式分类
①基于接口 JDK动态代理
通过reflect类库的 InvocationHandle 、Proxy 两个类来实现在运行时动态生成代理 和处理代理的方法调用
②基于类 cglib
③java字节码 javasist
基于接口 JDK动态代理
①使用Proxy的静态方法newProxyInstance()在运行时创建代理类
public static Object newProxyInstance (ClassLoader loader,Class < ?>[]interfaces,InvocationHandler h)
//loder 类加载器 使用null 表示使用默认的类加载器
//interfaces 泛型接口数组 每个元素都是需要被代理实现的接口
//h 调用处理器 指定代理对象能做哪些事情
拿到代理
public class DynamicGetProxy {
private Class<?>[] interfaces;
//一个接口数组 要代理什么
public void setTarget(Class<?>[] targets) {
this.interfaces=targets;
}
public Object getProxy(InvocationHandler handler) {
return Proxy.newProxyInstance(DynamicGetProxy.class.getClassLoader(),interfaces,handler);
//返回代理
}
}
调用处理程序(像静态代理那样 代理对象间接访问目标对象)
因为我们没有写一个实际的代理类(但我们的确有拥用(反射在运行时生成的一个代理类))所以我们需要一个调用处理方法 来管理代理类的所有方法
public class RentProxyInvocationHandle implements InvocationHandler {
Object target;//目标对象
public void setTarget(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(target,args);
//使用反射 间接调用目标对象上的方法(提供代理)
//我们可以在这里横向扩展
return null;
}
//Invoke 方法在代理对象每次执行方法时会被唤醒执行 然后method 会以那个方法为值 args 中的每个参数是代理调用方法时传入的参数(使用了reflect)
}
真实对象
public class Host implements Rent {
@Override
public void rent() {
System.out.println("房东租房");
}
}
测试程序
public static void main(String[] args) {
DynamicGetProxy dynamicGetProxy = new DynamicGetProxy();
Host host=new Host();//实例一个真实对象
dynamicGetProxy.setTarget(host.getClass().getInterfaces());//拿到这个真实对象实现的所有接口
RentProxyInvocationHandle proxyInvocationHandle=new RentProxyInvocationHandle();//调用处理程序
proxyInvocationHandle.setTarget(host);//传过去 让代理调用目标
Rent rent= (Rent) dynamicGetProxy.getProxy(proxyInvocationHandle);//让虚拟机生成一个代理类 拿到这个代理类
rent.rent();
//调用测试方法
}
基于cglib(Code Generation Library)
真实对象
public class Target {
public void login()
{
System.out.println("用户登录");
}
}
方法拦截器和动态生成代理对象
package com.lxc.proxy;
import com.lxc.aspect.AspectCglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
//Interceptor
//拦截
public class CglibProxy implements MethodInterceptor {
public Object createProxy(Object target) {
Enhancer enhancer = new Enhancer();
//增强器
enhancer.setSuperclass(target.getClass());
// 指定要增强的类
enhancer.setCallback(this);
// 设置回调
//类似方法监听
// 当target对象开始执行某个方法 方法拦截器会对它拦截 然后对方法进行增强(通过执行拦截器的intercept)
return enhancer.create();
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//MethodProxy 看不懂代理方法
AspectCglib aspectCglib=new AspectCglib();
aspectCglib.permissionCheck();
Object o = methodProxy.invokeSuper(proxy, args);
aspectCglib.log();
return o;
}
}
测试类
@Test
public void jdkCglib()
{
TargetJdk targetJdk=new TargetJdk.TargetJdkImp();
JdkProxy jdkProxy=new JdkProxy();
TargetJdk targetJdk1 = (TargetJdk) jdkProxy.createProxy(targetJdk);
targetJdk1.login();
}
jdk和cglib区别
-
jdk使用反射在运行时动态生成真实对象的代理拦截真实对象的方法执行在指定位置执行增强
-
cglib 使用ASM(assembly)开源包 加载真实对象.class(字节码文件) 然后修改生成子类(覆盖真实对象中的方法,在覆盖时增强)因为是子类不能覆写final 关键字修饰的方法
-
jdk 不能代理 没有实现接口的真实对象
-
spring会根据是否实现接口自动在两者之间切换动态代理的实现