动态代理:是指为其他对象提供一种代理,以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用,代理模式属于结构型设计模式。代理分为静态代理和动态代理
1.静态代理
public interface Iperson {
//找对象
void findlove();
//void findwork(String args);
}
public class Zhangsan implements Iperson {
//找对象,提要求
@Override
public void findlove() {
System.out.println("是个美女就行");
}
}
//父亲帮儿子相亲,实现
public class ZhangLaosan {
private Zhangsan zhangsan;
public ZhangLaosan(Zhangsan zhangsan) {
this.zhangsan = zhangsan;
}
public void findlove() {
System.out.println("开始帮儿子看女孩子");
zhangsan.findlove();
System.out.println("开始交往");
}
}
public class Test {
public static void main(String[] args) {
Zhangsan iperson = new Zhangsan();
//只能帮儿子找对象
ZhangLaosan zhangLaosan = new ZhangLaosan(iperson);
zhangLaosan.findlove();
}
}
以上是静态代理,父亲帮儿子找对象
下面通过jdk动态代理实现
public class JdkMmeiPo implements InvocationHandler{
private Iperson target;
public Iperson getInstace(Iperson target){
this.target = target;
Class<?> aClass = target.getClass();
return (Iperson) Proxy.newProxyInstance(aClass.getClassLoader(),aClass.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object invoke = method.invoke(this.target, args);
after();
return invoke;
}
private void before() {
System.out.println("开始----");
}
private void after() {
System.out.println("结束-----");
}
}
public class JdkProxyTest {
public static void main(String[] args) {
JdkMmeiPo jdk = new JdkMmeiPo();
Iperson zhansan = jdk.getInstace(new Zhangsan());
zhansan.findlove();
}
}
如果再想干一件事情,直接加方法即可,JdkMmeiPo类不需要任何改动
public interface Iperson {
//找对象
void findlove();
void findwork();
}
public class Zhangsan implements Iperson {
//找对象,提要求
@Override
public void findlove() {
System.out.println("是个美女就行");
}
@Override
public void findwork() {
System.out.println("张三找工作");
}
}
public final class $Proxy0 extends Proxy
implements Iperson
{
private static Method m1;
private static Method m2;
private static Method m4;
private static Method m0;
private static Method m3;
public $Proxy0(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
public final boolean equals(Object obj)
{
try
{
return ((Boolean)super.h.invoke(this, m1, new Object[] {
obj
})).booleanValue();
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString()
{
try
{
return (String)super.h.invoke(this, m2, null);
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final void findwork()
{
try
{
super.h.invoke(this, m4, null);
return;
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode()
{
try
{
return ((Integer)super.h.invoke(this, m0, null)).intValue();
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final void findlove()
{
try
{
super.h.invoke(this, m3, null);
return;
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
Class.forName("java.lang.Object")
});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m4 = Class.forName("com.example.test301.jdkproxy.Iperson").getMethod("findwork", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m3 = Class.forName("com.example.test301.jdkproxy.Iperson").getMethod("findlove", new Class[0]);
}
catch (NoSuchMethodException nosuchmethodexception)
{
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
}
catch (ClassNotFoundException classnotfoundexception)
{
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}
通过反编译查看源码
JDK动态代理的实现原理
获取被代理对象的引用,并且获取它的所有接口(反射获取)。
JDK Proxy类重新生成一个新的类,实现了被代理类所有接口的方法。
动态生成Java代码,把增强逻辑加入到新生成代码中。
编译生成新的Java代码的class文件。
加载并重新运行新的class,得到类就是全新类。
静态代理,张三父亲只能给张三找媳妇,而动态代理媒婆可以给张三找媳妇,也能帮李四找媳妇
下面看一下另一种cglib代理
public class Zhangsan{
//找对象,提要求
public void findlove() {
System.out.println("是个美女就行");
}
}
public class CglibMmeiPo implements MethodInterceptor {
public Object getInstace(Class<?> clazz){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
private void before() {
System.out.println("开始----");
}
private void after() {
System.out.println("结束-----");
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object o1 = methodProxy.invokeSuper(o, objects);
after();
return o1;
}
}
public class CglibTest {
public static void main(String[] args) {
CglibMmeiPo cglib = new CglibMmeiPo();
Zhangsan instace = (Zhangsan)cglib.getInstace(Zhangsan.class);
instace.findlove();
}
}
JDK是采用读取接口的信息
CGLib覆盖父类方法
目的:都是生成一个新的类,去实现增强代码逻辑的功能
JDK Proxy 对于用户而言,必须要有一个接口实现,目标类相对来说复杂
CGLib 可以代理任意一个普通的类,没有任何要求
CGLib 生成代理逻辑更复杂,效率,调用效率更高,生成一个包含了所有的逻辑的FastClass,不再需要反射调用
JDK Proxy生成代理的逻辑简单,执行效率相对要低,每次都要反射动态调用
CGLib 有个坑,CGLib不能代理final的方法
代理模式的优缺点
代理模式具有以下优点:
(1)代理模式能将代理对象与真实被调用目标对象分离。
(2)在一定程度上降低了系统的耦合性,扩展性好。
(3)可以起到保护目标对象的作用。
(4)可以增强目标对象的功能。
缺点:
(1)代理模式会造成系统设计中类的数量增加。
(2)在客户端和目标对象中增加一个代理对象,会导致请求处理速度变慢。
(3)增加了系统的复杂度。
Spring中的代理选择原则
(1)当Bean有实现接口时,Spring就会用JDK动态代理。
(2)当Bean没有实现接口时,Spring会选择CGLib代理。
(3)Spring可以通过配置强制使用CGLib代理,只需在Spring的配置文件中加入如下代码
<aop:aspectj-autoproxy proxy-target-class="true"/>