代理模式优势及意义:1.安全上,能够屏蔽客户端访问真是对象。2.可以在目标对象实现的基础上,增强额外的功能操作,即功能扩展(相当于明星和经纪人)。3.系统性能上,可以对真是对象进行延时加载,从而达到按需分配的设计思想。(@lazy对真正需要加载数据时才加载。这样节省资源分配;ioc管理bean对象时,都是通过代理模式来管理对象)
定义:给指定的目标对象提供了一种通过代理对象访问的方式。
代理模式是一个结构化的设计模式。
结构图:
静态代理实例:
1.
package propxy;
//接口
public interface ByCar {
void buyCar();
}
2.
package propxy;
//目标对象
public class Person implements ByCar{
@Override
@ask(askTr="i have 15000$")
public void buyCar() {
System.out.println("i want by care;");
}
}
3.
package propxy;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
//对目标对象增强 采用注解方式
public @interface ask {
String askTr();
}
4.
package propxy;
//代理对象
public class ProxyBy implements ByCar {
// 目标对象的引用
private ByCar bc;
public ProxyBy(ByCar bc) {
super();
this.bc = bc;
}
@Override
public void buyCar() {
// 对目标对象增强
String askTr = null;
try {
askTr=bc.getClass().getMethod("buyCar")
.getAnnotatio(ask.class).askTr();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(askTr);
bc.buyCar();
}
}
5.
package propxy;
public class test {
public static void main(String[] args) {
ByCar byCaryCar=new ProxyBy(new Person());
byCaryCar.buyCar();
}
}
静态代理常用环境:项目中的三层架构就是静态代理;controller层调用service层;service层调用dao层;就是典型的静态代理;
- JDK动态代理 2.cglib代理
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
动态代理:通过字节码重组的方式动态的创建proxy类实例和proxy实例;生成的class文件就是所谓的字节码;如xx.getclass();
Jdk动态代理:
- 声明subject接口,且realsubject必须实现该接口
- 必须实现invocationhandler接口,且覆盖invoke方法
- 可以通过jdk提供的proxy.newproxyinstance静态来构造代理对象
实例:
package propxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//实现invocationhandler 重写invoke()方法
public class DynamicProxy implements InvocationHandler{
private ByCar bc;
@SuppressWarnings("unchecked")
public <T> T getProxy(ByCar bc) {
this.bc=bc;
//通过jdk自带静态的方法 proxy.newproxyinstance() 来生成代理对象
//通过字节码重组的方式动态的生成一个class字节码,在通过class字节码反射的方式 创建一个对象
return (T) Proxy.newProxyInstance(DynamicProxy.class.getClassLoader(), bc.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//目标对象增强
String askTr = bc.getClass().getMethod("buyCar").getAnnotation(ask.class).askTr();
System.out.println(askTr);
//反射机制
return method.invoke(bc, args);
}
}
调用:
public static void main(String[] args) {
ByCar byCar =new DynamicProxy().getProxy(new Person());
byCar.buyCar();
}
Cglib动态代理:通过jvm(java虚拟机)中,通过动态的组装当前类的一个子类来实现的动态代理;
需引入cglib的依赖插件;
三大步骤:
- 引入cglib插件;
- 实现methodinterceptor 用于拦截 重写intercept方法
- 通过Enhancer enhancer=new Enhancer(); enhancer.create() 创建代理对象
- 实例:
1.
package propxy;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class DynamicCglib implements MethodInterceptor{
@SuppressWarnings("unchecked")
public <T> T GetInstances(Class<?> clazz) {
//通过传入的字节码文件(clazz) 从新生成一个字节码文件继承于它
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return (T) enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] objs, MethodProxy methodProxy) throws Throwable {
System.out.println("方法前--------------操作");
Object invokeSuper = methodProxy.invokeSuper(obj, objs);
System.out.println("方法后--------------操作");
return invokeSuper;
}
}
2.
package propxy;
public class User {
public void eat() {
System.out.println("user:eat............");
}
}
调用:
package propxy;
public class test {
public static void main(String[] args) {
//静态
ByCar byCaryCar=new ProxyBy(new Person());
byCaryCar.buyCar();
//动态jdk
ByCar byCar =new DynamicProxy().getProxy(new Person());
byCar.buyCar();
//动态cglib
User obj = new DynamicCglib().GetInstances(User.class);
obj.eat();
}
}
Jdk动态代理与cglib的区别:1.如果bean实例没有实现某一接口 就采用cglib代理模式;如果bean实例实现了某一接口,spring就会采用jdk动态代理模式;
2.cglib采用的是继承于原来的一个类;调用目标对象的方法,不是采用的反射机制,而是采用的缓存fast机制;而jdk代理是采用实现接口的方式,调用目标对象的方法是采用的是反射机制;
Jdk动态代理与cglib的优劣:1.cglib的动态代理不需要目标对象实现任何接口,但是如果目标对象里的方法是final时,就不能实现动态代理;jdk动态代理需要目标对象继承某一接口,没有方法final的限制;
2.cglib动态代理采用的缓存fast机制,它为每个方法体编了一个记号,像用索引的方式去快速调用,而jdk采用的反射机制的调用该方法体;所以cglib动态代理在jvm编译生成class文件的过程中较于jdk动态代理要慢一些;但是调用方法体要快一些;jdk采用反射机制的去调用方法体 是比较耗费资源的;
在项目中:service接口对象往往采用的是cglib的动态代理 而mybatis里的mapper接口对象是采用的jdk动态代理的方式;
Spring的aop采用面向切面编程,代理模式在其中表现的淋淋尽致,比如我们放在service层方法上的注解如@transition 我们都知道是事物注解;那spring是怎么来处理它的呢? 在我们代理模式中为目标对象增强其功能,我们是不是会定义一个注解,放在目标对象方法上;在我们通过代理模式调用目标对象方法时通过方法上定义的注解 我们可以在方法前后做一些操作 就是对目标对象方法的一些增强;
比如 @transition,我们都是放在service层的方法上,而service层采用的是cglib动态代理的方式。我们在调用目标对象的方法时通过拦截器transitioninterceptor捕获到方法上有@transition的注解(transitioninterceptor肯定实现了methodinterceptor方法拦截器),那么,spring就会在目标方法前做事物开启操作,然后动态代理的方式调用目标对象方法(方法里肯定就是些mapper里相关的事物操作啦 我这里用的是mybatis框架 注意:mapper采用的是jdk的动态代理方式),方法后
如果有异常就做事物回滚操作;这些都是通过代理模式来实现的;
service层调用采用的cglib代理方式图解:代理名字都含有cglib 通过上面实例 debug模式可验证
mybatis里mapper代理模式图解:代理名字都是$proxy开头 通过上面实例 debug模式可验证