定义:
代理模式是指,为其他对象提供一种代理,以控制对这个对象的访问。(保护和增强目标对象)
分为动态代理和静态代理。
一、静态代理:显示声明被代理对象
package com.gupaoedu.vip.pattern.proxy;
/**
* Created by Tom on 2019/3/10.
*/
public interface Person {
void findLove();
}
package com.gupaoedu.vip.pattern.proxy.staticproxy;
import com.gupaoedu.vip.pattern.proxy.Person;
/**
* Created by Tom on 2019/3/10.
*/
public class Son implements Person{
public void findLove(){
System.out.println("儿子要求:肤白貌美大长腿");
}
public void findJob(){
}
public void eat(){
}
}
package com.gupaoedu.vip.pattern.proxy.staticproxy;
import com.gupaoedu.vip.pattern.proxy.Person;
/**
* Created by Tom on 2019/3/10.
*/
public class Father implements Person {
private Son person;
public Father(Son person){
this.person = person;
}
public void findLove(){
System.out.println("父亲物色对象");
this.person.findLove();
System.out.println("双方父母同意,确立关系");
}
public void findJob(){
}
}
package com.gupaoedu.vip.pattern.proxy.staticproxy;
/**
* Created by Tom on 2019/3/10.
*/
public class FatherProxyTest {
public static void main(String[] args) {
Father father = new Father(new Son());
father.findLove();
//农村,媒婆
//婚介所
//大家每天都在用的一种静态代理的形式
//对数据库进行分库分表
//ThreadLocal
//进行数据源动态切换
}
}
二、动态代理(JDK,CGLib,Gupao)
package com.gupaoedu.vip.pattern.proxy.dynamicproxy.jdkproxy;
import com.gupaoedu.vip.pattern.proxy.Person;
/**
* Created by Tom on 2019/3/10.
*/
public class Girl implements Person {
public void findLove() {
System.out.println("高富帅");
System.out.println("身高180cm");
System.out.println("有6块腹肌");
}
}
package com.gupaoedu.vip.pattern.proxy.dynamicproxy.jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Created by Tom on 2019/3/10.
*/
public class JDKMeipo implements InvocationHandler {
private Object target;
public Object getInstance(Object target) throws Exception{
this.target = target;
Class<?> clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object obj = method.invoke(this.target,args);
after();
return obj;
}
private void before(){
System.out.println("我是媒婆,我要给你找对象,现在已经确认你的需求");
System.out.println("开始物色");
}
private void after(){
System.out.println("OK的话,准备办事");
}
}
package com.gupaoedu.vip.pattern.proxy.dynamicproxy.jdkproxy;
import com.gupaoedu.vip.pattern.proxy.Person;
import sun.misc.ProxyGenerator;
import java.io.FileOutputStream;
import java.lang.reflect.Method;
/**
* Created by Tom on 2019/3/10.
*/
public class JDKProxyTest {
public static void main(String[] args) {
try {
Object obj = new JDKMeipo().getInstance(new Girl());
Method method = obj.getClass().getMethod("findLove",null);
method.invoke(obj);
//obj.findLove();
//$Proxy0
// byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{Person.class});
// FileOutputStream os = new FileOutputStream("E://$Proxy0.class");
// os.write(bytes);
// os.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
原理:通过字节码重组,生成了新的类,新的方法(由静转动,实现增强,不能修改本来的类,只能生成新的类)
JDK动态代理的实现原理:
1,拿到被代理类的引用,并且获取他的所有接口(反射获取)
2,JDK Proxy类重新生成一个新的类,实现了被代理类所有接口的方法
3,动态生成Java代码,把增强的代码放到新生成的代码中
4,编译
5,加载运行
附加:自己动手写JDK动态代理接口(略)
静态代理和动态代理的区别:
静态代理:对目标对象有一定的限制,不符合开闭原则
动态代理:对目标对象的类型没有限制,但是目标对象要实现一个接口(生成代理对象的时候要扫描对象接口的方法),否则无法调用
实战演习:
分别使用静态代理和动态代理实现数据源的动态修改
SpringAOP使用代理模式实现。(切面增强)
CGLib:
动态生成一个类去继承目标类,来实现动态代理。(目标类不需要实现接口,也没有什么其他的限制)
package com.gupaoedu.vip.pattern.proxy.dynamicproxy.cglibproxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* Created by Tom on 2019/3/11.
*/
public class CGlibMeipo implements MethodInterceptor {
public Object getInstance(Class<?> clazz) throws Exception{
//相当于Proxy,代理的工具类
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object obj = methodProxy.invokeSuper(o,objects);
after();
return obj;
}
private void before(){
System.out.println("我是媒婆,我要给你找对象,现在已经确认你的需求");
System.out.println("开始物色");
}
private void after(){
System.out.println("OK的话,准备办事");
}
}
package com.gupaoedu.vip.pattern.proxy.dynamicproxy.cglibproxy;
/**
* Created by Tom on 2019/3/11.
*/
public class Customer {
public void findLove(){
System.out.println("儿子要求:肤白貌美大长腿");
}
}
package com.gupaoedu.vip.pattern.proxy.dynamicproxy.cglibproxy;
import net.sf.cglib.core.DebuggingClassWriter;
/**
* Created by Tom on 2019/3/11.
*/
public class CglibTest {
public static void main(String[] args) {
try {
//JDK是采用读取接口的信息
//CGLib覆盖父类方法
//目的:都是生成一个新的类,去实现增强代码逻辑的功能
//JDK Proxy 对于用户而言,必须要有一个接口实现,目标类相对来说复杂
//CGLib 可以代理任意一个普通的类,没有任何要求
//CGLib 生成代理逻辑更复杂,效率,调用效率更高,生成一个包含了所有的逻辑的FastClass,不再需要反射调用
//JDK Proxy生成代理的逻辑简单,执行效率相对要低,每次都要反射动态调用
//CGLib 有个坑,CGLib不能代理final的方法
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"E://cglib_proxy_classes");
Customer obj = (Customer) new CGlibMeipo().getInstance(Customer.class);
System.out.println(obj);
obj.findLove();
} catch (Exception e) {
e.printStackTrace();
}
}
}
底层原理:生成3个类:
其一,生成代理类的一个子类,重写了父类的方法,在父类的方法中,先调用了intercept()方法,又调用了父类的原方法
其二,略。。。(晦涩难懂)
其三,略。。。(晦涩难懂)
如果动态代理,需要代理多个方法,可以结合策略模式实现,以后再讲。。
CGLib和JDK Proxy的区别:
1,JDK动态代理是实现了被代理对象的接口,CGLib是继承了被代理对象
2,JDK和CGLib都是在运行时期生成字节码,JDK是直接写Class字节码,CGLib使用ASM框架写class字节码,CGlib实现代理更复杂,生成代理类比JDK效率低
3,JDK调用代理方法是通过反射机制调用,CGLIb是通过FastClass机制直接调用方法,CGlib执行效率更高
SpringAOP使用代理的原则:
1,当bean有实现接口时,Spring会使用JDK动态代理
2,当bean没有实现接口时,Spring选择CGlib
3,spring通过配置可以强制使用CGLib,只需要在spring的配置中添加如下配置:
<aop:aspectj-autoproxy proxy-target-class="true"/>