代理模式应用场景
SpringAOP、日志记录,性能统计,安全控制,事务处理,异常处理等等。
代理的分类
- 静态代理(静态定义代理类)
- 动态代理(动态生成代理类)
Jdk自带动态代理
Cglib 、javaassist(字节码操作库)
静态代理
什么是静态代理
由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
静态代理代码
public interface IUserDao {
void save();
}
public class UserDao implements IUserDao {
public void save() {
System.out.println("已经保存数据...");
}
}
代理类
public class UserDaoProxy implements IUserDao {
private IUserDao target;
public UserDaoProxy(IUserDao iuserDao) {
this.target = iuserDao;
}
public void save() {
System.out.println("开启事物...");
target.save();
System.out.println("关闭事物...");
}
}
JDK动态代理
概念
在java的动态代理机制中,有两个重要的类或接口,一个是InvocationHandler接口、另一个则是 Proxy类,这个类和接口是实现我们动态代理所必须用到的。
InvocationHandler接口是给动态代理类实现的,负责处理被代理对象的操作的,而Proxy是用来创建动态代理类实例对象的,因为只有得到了这个对象我们才能调用那些需要代理的方法。
JDK动态代理实现的原理
首先Jdk的动态代理实现方法是依赖于接口的,首先使用接口来定义好操作的规范。然后通过Proxy
类产生的代理对象调用被代理对象的操作,而这个操作又被分发给InvocationHandler
接口的 invoke
方法具体执行
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
此方法的参数含义如下
proxy:代表动态代理对象
method:代表正在执行的方法
args:代表当前执行方法传入的实参
返回值:表示当前执行方法的返回值
定义抽象方法
public interface Star {
void sing();
}
抽象方法具体实现
public class RealStar implements Star {
@Override
public void sing() {
System.out.println("真正明星唱歌");
}
}
代理类创建
public class DynamicProxy implements InvocationHandler{
private Star star;
public DynamicProxy(Star star) {
super();
this.star = star;
}
/**代理增强方法**/
protected void sellTicket() {
System.out.println("代理卖票");
}
/**代理增强方法**/
protected void collectMoney() {
System.out.println("代理收钱");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("jdk动态代理");
sellTicket();
method.invoke(star,args);
collectMoney();
return null;
}
public static void main(String[] args){
Star star=new RealStar();
Star proxy= (Star) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Star.class},new DynamicProxy(star));
proxy.sing();
}
}
测试结果
jdk动态代理
代理卖票
真正明星唱歌
代理收钱
CGlib动态代理
由于JDK只能针对实现了接口的类做动态代理,而不能对没有实现接口的类做动态代理,所以cgLib横空出世!CGLib(Code Generation Library)是一个强大、高性能的Code生成类库,它可以在程序运行期间动态扩展类或接口,它的底层是使用java字节码操作框架ASM实现。
1 引入cgLib 库
cglib-nodep-3.2.6.jar:使用nodep包不需要关联asm的jar包,jar包内部包含asm的类.
2 定义业务类,被代理的类没有实现任何接口
public class Frank {
public void submit(String proof) {
System.out.println(String.format("老板欠薪跑路,证据如下:%s",proof));
}
public void defend() {
System.out.println(String.format("铁证如山,%s还Frank血汗钱","马旭"));
}
}
3 定义拦截器,在调用目标方法时,CGLib会回调MethodInterceptor接口方法拦截,来实现你自己的代理逻辑,类似于JDK中的InvocationHandler接口。
public class cgLibDynProxyLawyer implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
if (method.getName().equals("submit"))
System.out.println("案件提交成功,证据如下:"+ Arrays.asList(params));
Object result = methodProxy.invokeSuper(o, params);
return result;
}
}
4定义动态代理工厂,生成动态代理
public class ProxyFactory {
public static Object getGcLibDynProxy(Object target){
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new cgLibDynProxyLawyer());
Object targetProxy= enhancer.create();
return targetProxy;
}
}
5客户端调用
public static void main(String[] args) {
Frank cProxy= (Frank) ProxyFactory.getGcLibDynProxy(new Frank());
cProxy.submit("工资流水在此");
cProxy.defend();
}
案件提交成功,证据如下:[工资流水在此]
老板欠薪跑路,证据如下:工资流水在此
铁证如山,马旭还Frank血汗钱
cgLib的动态代理原理
CGLIB原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。
CGLIB底层:使用字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。
CGLIB缺点:对于final方法,无法进行代理。
静态代理
抽象角色
public interface Star {
void sing();
}
public class RealStar implements Star{
@Override
public void sing() {
System.out.println("真正明星唱歌");
}
}
/**
* 静态代理:
* 被代理类含有接口和具体实现类;
* 代理类实现被代理接口,并含有被代理接口属性、相应属性构造器
*
* Created by Administrator on 2019/2/24 0024.
*/
public class ProxyStar implements Star{
private Star star;
public ProxyStar(Star star) {
this.star = star;
}
@Override
public void sing() {
this.sellTicket();
star.sing();
this.collectMoney();
}
private void sellTicket() {
System.out.println("经纪人卖票");
}
private void collectMoney() {
System.out.println("经纪人收钱");
}
public static void main(String[] args){
ProxyStar proxyStar=new ProxyStar(new RealStar());
proxyStar.sing();
}
}
静态代理和动态代理的区别:
静态:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。
动态:在程序运行时运用反射机制动态创建而成。