代理模式原理
为了更好满足单一职责的原则,而又非入侵式的,还可以保证可以完整的实现这一整个过程,我们使用代理模式
- 例子(买火车票)
我要去湖北了,需要买一张火车票
火车票流程图 去火车站——> 买票——>回来
1.去火车站
2.买票
3.回来
现在可以在手机买票软件上购票 ,那么只需要步骤二就可以 步骤一和步骤三被代理掉了
事实上我只需要而且只想自己买票,这就是我的职责(买票就是核心代码)
现在买票我一个人全包,去买票和回来我要就给别人做(非核心代码啊)
购票接口
//这里是购票接口(核心代码)
interface Buy{
void buy();
}
核心逻辑实现
//这是我,只需要专心于买票
class I implements Buy{
public void buy() {
System.out.println(" 买票" );
}
}
实现代理和main 方法
//这里是代理类 类似手机购票软件
//我的实现也写在里面
public class BuyProxy implements Buy{
public static void main(String[] args) {
//第一步先实现核心逻辑
I i = new I();
//第二步声明代理
BuyProxy proxy = new BuyProxy();
//第三步选择代理对象
proxy.getByAgent(i);
//执行代理方法
proxy.buy();
}
//这里使用多态 ,, 实现了可插拔功能(低耦合)
//这个就是被代理对象
private Buy buy;
//如果是动态代理这里入参是Object 静态无所谓
public void getByAgent(Buy buy){
this.buy = buy;
}
/**
* 这里是代理类实现购票接口的功能
*/
public void buy() {
System.out.println("前置增强-》去火车站");
buy.buy(); //实现购票功能
System.out.println("后置增强-》回家");
}
}
这样就实现了了一个静态代理 这就是 aop(切面编程) 的原理
使用代理模式的好处
1.在程序中通过代理,可以详细控制访问某个或者某类对象的方法,在调用这个方法前做前置处理,调用这个方法后做后置处理。
譬如: 记录方法执行耗时 判断是否满足进入方法的条件 ,开启/关闭一些资源 ,日志,
对核心业务和非核心业务解耦
动态代理
java 现在有两种动态代理解决方案
1.cglib 需要导入cglib 的jar 包
2. jdk动态代理 使用java 原生的动态代理
Jdk动态代理:利用拦截器(必须实现InvocationHandler)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理
cglib动态代理:总结就是通过加载字节码,然后修改他的字节码,然后生成子类(代理类)
JDK动态代理和cglib字节码生成的区别?
1、jdk动态代理只能对实现了接口的类生成代理,而不能针对类
2、cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法的增强,可以对接口和类生成代理,但是因为采用的是继承,所以该类或方法最好不要生成final,对于final类或方法,是无法继承的
Spring如何选择是用JDK还是cglib?
1、当bean实现接口时,会用JDK代理模式
2、当bean没有实现接口,用cglib实现
3、可以强制使用cglib(在spring配置中加入<aop:aspectj-autoproxy proxyt-target-class=”true”/>)
两种动态代理的对比参考出处
:https://blog.csdn.net/qq_43842093/article/details/121709970
cglib动态代理
上面实现了简单的静态代理,还讲了cglib 的原理,现在我们在实现 cglib 的动态代理
spring-core jar包默认导入了cglib 包 如果有spring-core jar包 就不需要重新导入cglib jar包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
代理类 继承MethodInterceptor 实现 interceptor方法
public class MyProxy implements MethodInterceptor {
private Object target ;
public Object getProxy(Object clazz){
try {
//生成一个实例对象
this.target = clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
//声明一个类增强器
Enhancer en = new Enhancer();
//设置他要增强的类
en.setSuperclass(clazz);
//设置拦截类(实现了MethodInterceptor的类,当前类就实现了)
en.setCallback(this);
//返回动态生成的代理类
return en.create();
}
/**
*对方法的增强或重写
* 对接口的实现()
*
* @param o 动态生成的代理类对象
* @param method 调用的方法
* @param objects 参数集合
* @param methodProxy 通过调用的方法生成的代理方法
* @return 执行 method.invoke()的返回值
* @throws Throwable
*/
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println( o.getClass().getName());
System.out.println("method = "+method);
System.out.println("objects = " + Arrays.toString(objects));
System.out.println("methodProxy = " + methodProxy.getClass().getName());
Object invoke = methodProxy.invokeSuper(o, objects);
Object invoke1 = methodProxy.invoke(this.target, objects);
return invoke;
}
}
被代理的对象
public abstract class PieMapper {
public List<Pie> getList(){
System.out.println("1314 = " + 1314);
return null;
};
public abstract void del();
}
测试代码
public class MyProxyMain {
public static void main(String[] args) {
//代理类实现
MyProxy myProxy = new MyProxy();
//真实的核心逻辑实现被代理 PieMapper
PieMapper proxy = (PieMapper) myProxy.getProxy(PieMapper.class);
//执行代理方法
proxy.del();
}
}
代码执行结果
可以看他打印的地址:
注意:代理生成的是 PieMapper的子类,类似多态
,可以看他打印的地址:
出现内存溢出异常
** 注意对于methodProxy.invoke(),如果第一个参数是o,也就是代理对像,那么就会出现OOM内存溢出现象,这是由于当在程序中,当生成了代理类去调用具体类(被代理类)的方法时,会先去走拦截器,然后跑代理类增强后的方法,此时,在增强方法中,利用代理对象又去调用具体类的方法,又会再次去走拦截器跑代理类增强后的方法,这样就会出现无限循环的情况导致OOM;**
**对于对于methodProxy.invoke(),如果第一个参数是this.object,那么当程序中生成代理类时,去调用方法时,依旧先去走增强逻辑,但是此时,方法内部调用被代理对象的方法,内部方法就不会执行增强逻辑(这和AOP的行为相似,只增强直接调用的方法,不会增强调用方法内部继续调用的其他方法)**
错误的例子
总结
invokeSuper调用的是被代理类的方法, 但只有代理类才存在基类, 必须使用代理类作为obj参数调用
invoke调用的是增强方法, 必须使用被代理类的对象调用, 使用代理类会造成OOM
jdk动态代理
直接上例子
代理类 继承 InvocatinoHandler 实现代理方法invoke
/**
* 实现InvocationHandler 用到的都在java.lang.reflect包下
*/
public class MyProxy implements InvocationHandler {
private Object target;
public MyProxy(Class clazz){
try {
this.target = clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前置增强");
//使用反射机制执行方法
Object invoke = method.invoke(target,args);
System.out.println("执行方法int = "+(Integer)invoke);
//实现功能增强
System.out.println("后增强");
return invoke;
}
}
被代理类
因为和cglib 差不太多 就写在一个类里
public class MyproxyMain {
public static void main(String[] args) {
//这是接口的匿名内部类 实现了核心代码
DepartmentMapper mapper = new DepartmentMapper() {
@Override
public Integer getList() {
return 4;
}
};
//把匿名内部类放入代理中
MyProxy myProxy = new MyProxy(mapper.getClass());
//创建目标类对象的代理类对象
//newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h)
//loader - 类加载器来定义代理类
//interfaces - 代理类实现的接口列表
//h - 实现了InvocationHandler 的代理类
Object o = Proxy.newProxyInstance(mapper.getClass().getClassLoader(), mapper.getClass().getInterfaces(), myProxy);
DepartmentMapper o1 = (DepartmentMapper) o;
//输出
o1.getList();
}
}
输出