最近在看一些技术源码的时候,发现很多地方都是动态代理, 真可谓是一切皆代理啊,所以我们需要弄明白代理模式这样在看源码的时候会好受很多。
代理(Proxy)模式提供了间接访问目标对象的方式,即通过代理对象访问目标对象,这样做的好处是:可以在目标对象的功能上,增加额外的功能补充,即扩展目标对象的功能。
这就符合了设计模式低开闭原则,即在对既有代码不改动的情况下进行功能扩展。
举个我们平常非常常见的例子, 明星和经纪人就是被代理和代理的关系,明细出演活动的时候,明细就是一个目标对象,他只负责活动中的节目,而其他的琐碎的事情就交给他的代理人(经纪人)来解决。这就是代理思想 中的一个例子。
静态代理的特点:代理类和目标类必须实现 同一个接口或者继承相同的父类。所以我们需要 定义一个接口。下面来看具体实现demo。
接口类:
/**
* @author zhenghao
* @description:
* @date 2020/12/1410:28
*/
public interface Istart {
void sing();
}
目标类:
/**
* @author zhenghao
* @description:
* @date 2020/12/1416:07
*/
public class LDHStar implements Istart {
@Override
public void sing() {
System.out.println("华仔唱歌");
}
}
代理类:
/**
* @author zhenghao
* @description:
* @date 2020/12/1416:08
*/
public class StaticPorxyManager implements Istart {
private Istart target;
public StaticPorxyManager(Istart target) {
this.target = target;
}
@Override
public void sing() {
System.out.println("演唱会之前。。。。");
this.target.sing();
System.out.println("演唱会之后。。。。");
}
}
测试类:
/**
* @author zhenghao
* @description:
* @date 2020/12/1410:46
*/
public class StaticTeasMain {
public static void main(String[] args) {
Istart ldhStar = new LDHStar();
StaticPorxyManager staticPorxyManager = new StaticPorxyManager(ldhStar);
staticPorxyManager.sing();
}
}
静态代理类优缺点:
有点:可以在不修改目标代码的情况下,扩折额外 功能
缺点:因为代理类和目标类必须实现相同的接口,所以会有很多代理类,类太多, 同时,一旦接口增加方法,目标对象很代理对象都需要维护。而动态代理可以解决上面的问题。
动态代理的主要特点就是能够在程序运行时JVM才为目标对象生成代理对象。
常说的动态代理也叫做JDK 代理也是一种接口代理,之所以叫做接口代理,是因为被代理的对象也就是目标对象必须实现至少一个接口,没有实现接口不能使用这种方式生成代理对象,JDK中生成代理对象的代理类就是Proxy,所在包是java.lang.reflect.
接口:
/**
* @author zhenghao
* @description:
* @date 2020/12/1410:28
*/
public interface Istart {
void sing();
}
目标类:
/**
* @author zhenghao
* @description:
* @date 2020/12/1410:29
*/
public class WangFengStar implements Istart {
@Override
public void sing() {
System.out.println("汪峰唱歌。。。");
}
}
InvocationHandler类:
import java.lang.annotation.Target;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @author zhenghao
* @description:
* @date 2020/12/1410:32
*/
public class StarInvocationHandler implements InvocationHandler {
private Object target;
public StarInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("演唱会前工作");
//调用目标对象的目标方法
method.invoke(target,args);
System.out.println("演唱会后工作");
return null;
}
}
代理工厂:
import java.lang.reflect.Proxy;
/**
* @author zhenghao
* @description:
* @date 2020/12/1410:30
*/
public class ProxyFactory {
private Object target;
private StarInvocationHandler starInvocationHandler;
public ProxyFactory( Object target) {
this.target = target;
this.starInvocationHandler = new StarInvocationHandler(target);
}
public Object getProxyObject(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),starInvocationHandler);
}
}
测试方法:
public class TeasMain {
public static void main(String[] args) {
Istart wangfengStar = new WangFengStar();
Istart proxyObject = (Istart)new ProxyFactory(wangfengStar).getProxyObject();
proxyObject.sing();
}
}
JDK代理方式不需要代理对象实现接口,但是目标对象一定要实现接口,但是我们在项目中有很多需要代理的类并没有实现接口,所以这也算是这种代理方式的一种缺陷。
如果我们 需要给没有是实现任何接口的目标类生成代理对象,JDK方式是做不到的。这是就可以使用继承目标类以目标对象子类的方式实现代理,这种方法就叫做Cglib代理,也叫做子类代理,他是在内存中构建一个子类对象从而 实现对目标对象功能的扩展。
Cglib是一个强大的高性能代码生成包,他可以在运行期扩展java类和扩展java接口。它广泛的被许多AOP框架使用,例如Sring AOP和synaop,为他们提供方法的intercepion(拦截)
Cglib包的底层是通过使用一个小而快的字节码处理框架ASM来转换字节码并生成新的类。
Cglib子类代理实现方法:
1.需要引入cglib的jar文件 cglib-2.2.2.jar asm-3.3.1.jar,但是Spring的核心包中已经包括了Cglib功能,所以直接引入spring-core-3.2.5.jar
即可.
2.引入功能包后,就可以在内存中动态构建子类
3.代理的类不能为final,否则报错
4.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
目标类:
/**
* @author zhenghao
* @description:
* @date 2020/12/1410:57
*/
public class JieLunStar {
public void sing(){
System.out.println("杰伦唱歌");
}
}
代理工厂:
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author zhenghao
* @description:
* @date 2020/12/1410:58
*/
public class CglibProxyFactory implements MethodInterceptor {
//维护目标对象
private Object target;
public CglibProxyFactory(Object target) {
this.target = target;
}
/**
* @Description:获得目标类的代理对象
* @author: zhenghao
* @date: 2020/12/14 11:02
*/
public Object getProxyObject(){
//1、工具类
Enhancer enhancer = new Enhancer();
//2、设置父类
enhancer.setSuperclass(target.getClass());
//3、设置回调
enhancer.setCallback(this);
//4、创建代理类
Object proxyObject = enhancer.create();
return proxyObject;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("演唱会前工作");
//执行目标对象的目标方法
method.invoke(target,objects);
System.out.println("演唱后前工作");
return null;
}
测试类:
/**
* @author zhenghao
* @description:
* @date 2020/12/1410:46
*/
public class CglibTeasMain {
public static void main(String[] args) {
JieLunStar jieLunStar = new JieLunStar();
JieLunStar proxyObject = (JieLunStar)new CglibProxyFactory(jieLunStar).getProxyObject();
proxyObject.sing();
}
}
到这三种代理方式我们都介绍完了,下面总结一下:
1、如果目标对象实现了接口,我们就采用JDK方式实现动态代理
2、如果目标对象没有实现接口,我们就需要采用cglib方式实现动态代理;
注解类:
/**
* @author zhenghao
* @description:
* @date 2020/12/1411:33
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Select {
String value() default "";
}
目标类:
/**
* @author zhenghao
* @description:
* @date 2020/12/1411:32
*/
public interface IUserDao {
@Select("select * from user")
String getUser();
}
InvocationHandler类:
/**
* @author zhenghao
* @description:
* @date 2020/12/1410:32
*/
public class UserInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//创建类的是一个具体的实现类
if (Object.class.equals(method.getDeclaringClass())){
return method.invoke(this,args);
}else {
//得到所有注解
String value = method.getAnnotation(Select.class).value();
System.out.println("接口上面的注解的内容:" + value);
//实现具体的业务逻辑 如远程http调用实现等
return "user info";
}
}
}
代理工厂:
import java.lang.reflect.Proxy;
/**
* @author zhenghao
* @description:
* @date 2020/12/1411:47
*/
public class InterfaceProxyFactory {
private Class> target;
private UserInvocationHandler userInvocationHandler;
public InterfaceProxyFactory( Class> target) {
this.target = target;
this.userInvocationHandler = new UserInvocationHandler();
}
public Object getProxyObject(){
return Proxy.newProxyInstance(target.getClassLoader(),new Class[]{target},userInvocationHandler);
}
}
测试类:
/**
* @author zhenghao
* @description:
* @date 2020/12/1410:46
*/
public class InterfaceProxyTeasMain {
public static void main(String[] args) {
IUserDao proxyObject = (IUserDao) new InterfaceProxyFactory(IUserDao.class).getProxyObject();
System.out.println(proxyObject.getUser());
}
}