学习背景在学习设计模式的时候,研究数据访问对象的spring的自动注入,代理名词多次出现,通过资料搜索进行研究,课程推荐(不到三个小时讲的很形象建议看看,不打广告,拒绝扫雪视频,有好的希望各位能够可以下面评论和点赞一起努力呀) 使用工具及环境:
1.idea2018
2.jdk1.8
3.maven项目
Java动态代理和静态代理的学习(jdk和cglib)
一、代理简介
1.1 小定义
代理模式,就是给一个对象提供一个代理对象,控制原对象的访问。在这个模式中,通过创建代理对象作为替身替代了原有对象,从而达到我们对控制对象的访问的目的。(在不修改原对象的基础上,对原对象的功能进行修改或者增强)
通俗讲:相当于生活中的,中介、外卖小哥、经纪人。。。。(了解就好)。
1.2 代理的性质
代理具有解耦性质,从其本质定义上看,对于原对象进行扩展功能,不会影响到原对象的正常执行。
二、三种代理模式
2.1 概念
目标类:原对象,我们需要通过代理对象控制其访问,扩展功能。
代理类:代理模式产生的对象,是原对象的替身,已经在原有的基础上修改了逻辑。
具体有以下两种方式实现:
1、继承方式
2、接口方式
2.2 静态代理
需求:希望通过代理方式,模拟实现外卖小哥代理顾客取餐送饭过程。(体会类继承代理的作用)
1、对继承方式的实现:
Customer
package com.itheima.proxy.staticdemo;
public class Customer{
@Override
public String order(String foodName){
return "已经下单点了"+foodName;
}
}
DeliveryClerk
package com.itheima.proxy.staticdemo;
public class DeliveryClerk extends Customer {
@Override
public String order(String foodName) {
String result = super.order(foodName);
System.out.println("已经接受到订单,正前往取餐途中。。。");
System.out.println("已经取餐,正在派送。。。");
return result+",已经搅拌均匀";
}
}
StaticTest
package com.itheima.proxy.staticdemo;
public class StaticTest {
public static void main(String[] args){
//创建一个顾客对象
Customer customer = new DeliveryClerk();
String result = customer.order("麻婆豆腐");
System.out.println(result);
}
}
2、对接口方式的实现:
OrderInterface
package com.itheima.proxy.staticdemo;
public interface OrderInterface {
public String order(String foodName);
}
Customer
package com.itheima.proxy.staticdemo;
public class Customer implements OrderInterface{
@Override
public String order(String foodName){
return "已经下单点了"+foodName;
}
}
DelieveryClerk2
package com.itheima.proxy.staticdemo;
public class DelieveryClerk2 implements OrderInterface {
//把原来对象传入并保存
private final OrderInterface source;
public DelieveryClerk2(OrderInterface source) {
this.source = source;
}
@Override
public String order(String foodName) {
String result = source.order(foodName);
System.out.println("已经接受到订单,正前往取餐途中。。。");
System.out.println("已经取餐,正在派送。。。");
return result+",已经搅拌均匀";
}
}
StaticTest
package com.itheima.proxy.staticdemo;
public class StaticTest {
public static void main(String[] args){
//创建顾客对象
Customer customer1 = new Customer();
//创建代理对象
OrderInterface delieveryClerk = new DelieveryClerk2(customer1);
// 调用代理对象的方法,可以看到增强之后的效果
String result1 = delieveryClerk.order("红烧肉");
System.out.println(result1);
}
}
从上面不难看出通过两种方式对顾客这一目标类的扩展以及代理实现方法。
2.3 动态代理jdk
本代理用到jdk自带的模式化代理
Proxy.newProxyInstance()
方法
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)变量声明: ClassLoader loader:固定写法,指定目标类对象的类加载器即可,用于加载目标类及其接口的字节码文件,调用字节码的getClassLoader()方法。 Class<?>[] interfaces:固定写法,目标类的接口,调用字节码的getInterfaces()方法
InvocationHandler h 该方法是对目标类的方法的扩展,通过匿名内部类,这个会使得不管代理类用什么方法都会调用里面的invoke方法。
对于handler接口需要实现的方法public Object invoke(Object proxy, Method method, Object[] args)参数详解
proxy:代理对象的一个引用,就是Proxy.newProxyInstance()的返回值,此引用了解就行
method:对应的是触发invoke方法执行的方法对象,通俗讲调用xxx方法,那么xxx方法触发> > invoke方法,那么method就是xxx方法对应的反射对象(Method对象)
以外卖小哥代理需求为例写:
OrderInterface
package com.itheima.proxy.dynamicdemo.jdk;
public interface OrderInterface {
public String order(String foodName);
public void test();
public void test2();
}
Customer
package com.itheima.proxy.dynamicdemo.jdk;
public class Customer implements OrderInterface{
@Override
public String order(String foodName){
return "已经下单点了"+foodName;
}
@Override
public void test() {
System.out.println("我是test");
}
@Override
public void test2() {
System.out.println("我是test2");
}
}
DynamicTest
package com.itheima.proxy.dynamicdemo.jdk;
import com.itheima.proxy.dynamicdemo.jdk.explain.DeliveryClerk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.logging.Handler;
public class DynamicTest {
public static void main(String[] args) {
//准备一个目标类对象
Customer customer = new Customer();
// 使用jdk的Api,动态生成一个代理对象
OrderInterface deliveryClerk = (OrderInterface) Proxy.newProxyInstance(
customer.getClass().getClassLoader(),
customer.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// customer.order((String) args[0]);
if ("order".equals(method.getName())){
Object result = method.invoke(customer, args);
System.out.println("已经接受到订单,正前往取餐途中。。。");
System.out.println("已经取餐,正在派送。。。");
return result+",已经搅拌均匀";
}else
{
return method.invoke(customer, args);//使用反射,在原对象中执行该方法,并不修改其逻辑
}
}
}
);
//调用代理对象执行对应方法
String result = deliveryClerk.order("麻婆豆腐");
System.out.println(result);
deliveryClerk.test();
deliveryClerk.test2();
}
}
jdk动态最主要的是利用
Proxy.newProxyInstance
这个方法实现代理,里面的原理相当于创建了一个代理类实现接口,并且每个方法对实现InvocationHandler的invoke方法的统一调用,实现代理模式。
对于invoke方法中实现的方法中的执行语句不管是执行代理类中的哪个方法都会去执行,所以可以通过对于各个方法名字的判断来区别具体实现代理类中的方法,来扩展相应的目标类方法。
下面为以上的解释做个实例来简单实现底层jdk代理
1、底层实现的最主要的代理类(外卖小哥类)
DeliveryClerk
package com.itheima.proxy.dynamicdemo.jdk.explain;
import com.itheima.proxy.dynamicdemo.jdk.OrderInterface;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 此处模拟Proxy.newProxyInstance()方法中做的事情,从底层看看jdk如何完成动态的代理
*/
public class DeliveryClerk implements OrderInterface {
// 接收外部传送过来的InvocationHandler对象
private final InvocationHandler handler;
public DeliveryClerk(InvocationHandler handler) {
this.handler = handler;
}
@Override
public String order(String foodName) {
// 每一个方法的实现, 实际上并没有其他的事情,而是直接调用Invocation Handler中的invoke方法
try {
// 调用的事order方法,则反射获取order对应的method对象,传入invoke中
Method method = OrderInterface.class.getMethod("order", String.class);
// 调用Invocation Handler中的invoke方法
Object result = handler.invoke(this, method, new Object[]{foodName});
// 返回结果
return (String) result;
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return null;
}
@Override
public void test() {
// 每一个方法的实现, 实际上并没有其他的事情,而是直接调用Invocation Handler中的invoke方法
try {
// 调用的事test方法,则反射获取order对应的method对象,传入invoke中
Method method = OrderInterface.class.getMethod("test");
// 调用Invocation Handler中的invoke方法
Object result = handler.invoke(this, method, new Object[]{});
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
@Override
public void test2() {
// 每一个方法的实现, 实际上并没有其他的事情,而是直接调用Invocation Handler中的invoke方法
try {
// 调用的事test方法,则反射获取order对应的method对象,传入invoke中
Method method = OrderInterface.class.getMethod("test2");
// 调用Invocation Handler中的invoke方法
Object result = handler.invoke(this, method, new Object[]{});
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}
DynamicTest
package com.itheima.proxy.dynamicdemo.jdk;
import com.itheima.proxy.dynamicdemo.jdk.explain.DeliveryClerk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.logging.Handler;
public class DynamicTest {
public static void main(String[] args) {
//准备一个目标类对象
Customer customer = new Customer();
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("order".equals(method.getName())){
Object result = method.invoke(customer, args);
System.out.println("已经接受到订单,正前往取餐途中。。。");
System.out.println("已经取餐,正在派送。。。");
return result+",已经搅拌均匀";
}else
{
return method.invoke(customer, args);//使用反射,在原对象中执行该方法,并不修改其逻辑
}
}
};
OrderInterface deliveryClerk = new DeliveryClerk(handler);
//调用代理对象执行对应方法
String result = deliveryClerk.order("麻婆豆腐");
System.out.println(result);
deliveryClerk.test();
deliveryClerk.test2();
}
}
2.4 动态代理cglib
本代理其实原理与jdk代理一样,不同的是它需要外部导包,我利用maven直接引入外部包。
Enhancer.create(Class type, Callback callback)方法,参数讲解:
Class type:类的字节码,例如customer.getClass()
Callback callback:回调函数只是个接口一般要自己定义写成其另一种子接口MethodInterceptor的实现来扩展目标类
cglib引入
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
Customer
package com.itheima.proxy.dynamicdemo.cglib;
public class Customer{
public String order(String foodName){
return "已经下单点了"+foodName;
}
public void test() {
System.out.println("我是test");
}
public void test2() {
System.out.println("我是test2");
}
}
DynamicTest
package com.itheima.proxy.dynamicdemo.cglib;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class DynamicTest {
public static void main(String[] args) {
//创建一个目标类对象,也就是顾客对象
final Customer customer = new Customer();
//使用cglib创建代理对象
Customer deliveryClerk = (Customer) Enhancer.create(customer.getClass(), new MethodInterceptor() {
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 判断
if ("order".equals(method.getName())){
Object result = method.invoke(customer, args);
System.out.println("已获得订单,去取餐路上。。。");
System.out.println("已取得,正在送餐。。。。");
return result = "额外添加美味佐料;口水一下";
}else {
return method.invoke(customer, args);// 原封不动调用原来的调用,反射调用
}
}
});
//调用代理类方法
String result = deliveryClerk.order("鱼香肉丝");
System.out.println(result);
}
}
本实例的底层实现只是用了类的继承实现起来与接口实现基本一致,创建了一个类并为每个方法实现统一调用的MethodInterceptor的intercept方法。
DeliveryClerk
package com.itheima.proxy.dynamicdemo.cglib.explain;
import com.itheima.proxy.dynamicdemo.cglib.Customer;
import net.sf.cglib.proxy.MethodInterceptor;
import java.lang.reflect.Method;
/**
* 模拟在内存中调用cglib的Enhancer.create(Class type, Callback callback)方法后,内部写了什么样的代理类
*/
public class DeliveryClerk extends Customer {
private final MethodInterceptor methodInterceptor;
public DeliveryClerk(MethodInterceptor methodInterceptor) {
this.methodInterceptor = methodInterceptor;
}
@Override
public String order(String foodName) {
try {
Method method = Customer.class.getMethod("order", String.class);
Object result = methodInterceptor.intercept(this, method, new Object[]{foodName}, null);
return (String)result;
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return super.order(foodName);
}
@Override
public void test() {
try {
Method method = Customer.class.getMethod("test");
Object result = methodInterceptor.intercept(this, method, new Object[]{}, null);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
@Override
public void test2() {
try {
Method method = Customer.class.getMethod("test2");
Object result = methodInterceptor.intercept(this, method, new Object[]{}, null);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}
DynamicTest
package com.itheima.proxy.dynamicdemo.cglib;
import com.itheima.proxy.dynamicdemo.cglib.explain.DeliveryClerk;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class DynamicTest {
public static void main(String[] args) {
//直接使用代理类方法
DeliveryClerk deliveryClerk1 = new DeliveryClerk(new MethodInterceptor() {
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 判断
if ("order".equals(method.getName())){
Object result = method.invoke(customer, args);
System.out.println("已获得订单,去取餐路上。。。");
System.out.println("已取得,正在送餐。。。。");
return result = "额外添加美味佐料;口水一下";
}else {
return method.invoke(customer, args);// 原封不动调用原来的调用,反射调用
}
}
});
//调用代理类方法
String result = deliveryClerk1.order("鱼香肉丝");
System.out.println(result);
}
}
到这其实对于代理这个词旧不应该会有什么困惑了这种思想使得代码能够更加具有解耦的性质,里面也包含了很多有关Java的基础知识,比如最多的反射、多态。