写在前面:本来是想写Spring AOP原理的,分析下AOP就是代理模式的实现,这儿就先从代理模式讲起。
什么是代理模式
给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用,防止直接访问目标对象给系统带来的不必要复杂性。
代理对象:起到中介作用,连接客户端和目标对象
Java有3种代理模式
静态代理
静态代理即在编译期创建代理对象,特点:代理类与目标类需要实现相同的接口
代码示例:
接口类
public interface BuyTicketIface {
/**
* 买电影票
*/
void buyMovieTicket();
}
目标类
public class BuyTicketIml implements BuyTicketIface {
@Override
public void buyMovieTicket() {
System.out.println("xx买电影票");
}
}
代理类
public class BuyTicketProxy implements BuyTicketIface {
private BuyTicketIface buyTicket;
public BuyTicketProxy() {
}
public BuyTicketProxy(BuyTicketIface buyTicket) {
this.buyTicket = buyTicket;
}
@Override
public void buyMovieTicket() {
System.out.println("给钱");
buyTicket.buyMovieTicket();
System.out.println("买到票");
}
}
测试类
public class BuyTicketTest {
public static void main(String[] args) {
//创建目标对象
BuyTicketIface buyTicket = new BuyTicketIml();
//创建代理对象
BuyTicketProxy proxy = new BuyTicketProxy(buyTicket);
//购票
proxy.buyMovieTicket();
}
}
输出结果
给钱
xx买电影票
买到票
动态代理(Jdk代理)
动态代理,即创建代理对象的方式为在jvm运行时通过反射动态创建,与静态代理在编译期就创建代理对象相比减少了内存的占用,同时减少了对业务接口的依赖,降低了耦合度。
动态代理特点:
- 目标对象实现类接口
- 代理对象通过JDk API动态的在内存中创建
Jdk创建代理对象的类java.lang.reflect.Proxy,创建方法newProxyInstance
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
参数说明:
loader - 定义代理类的类加载器
interfaces - 代理类要实现的接口列表(即目标对象实现的接口列表)
h - 指派方法调用的调用处理程序
示例:
接口
/**
* @Description 接口
* @date 2018/8/7 16:50
*/
public interface FlyIface {
void fly();
}
目标类
public class Dog implements FlyIface {
private String name;
public Dog() {
}
public Dog(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void fly() {
System.out.println("....dog fly...."+name);
}
}
public class Dream implements FlyIface {
@Override
public void fly() {
System.out.println(".....dream fly....");
}
}
代理类
需要实现接口InvocationHandler
public class FlyProxy implements InvocationHandler {
private Object target;
public Object bind(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy class:"+proxy.getClass().getName()+"\n method:"+method.getName());
System.out.println("目标类执行前处理");
Object result = method.invoke(target,args);
System.out.println("目标类执行结束处理");
return result;
}
}
测试类
public class DynamicProxyTest {
public static void main(String[] args) {
FlyProxy proxy = new FlyProxy();
FlyIface fly = (FlyIface)proxy.bind(new Dog("dd"));
fly.fly();
}
}
输出结果
proxy class:com.sun.proxy.$Proxy0
method:fly
目标类执行前处理
....dog fly....huahua
目标类执行结束处理
Cglib代理
Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口。它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)。Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类。
有或者没有实现接口的类都可以通过Cglib代理加载。使用Cglib代理需要注意的事项:
1.需要引入cglib的jar文件,由于Spring的核心包中已经包括了Cglib功能,所以也可以直接引入spring-core-${version}.jar
2.目标类不能为final,否则报错
3.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法
示例:
目标类
public class Duck {
public void yell() {
System.out.println("Duck quack");
}
}
代理类
Cglib子类代理工厂
public class CglibProxy implements MethodInterceptor {
public Object bind(Object target) {
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(target.getClass());
//设置回调
enhancer.setCallback(this);
return enhancer.create();//创建代理对象
}
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable{
System.out.println("目标类方法执行前处理");
proxy.invokeSuper(object,args);
System.out.println("目标类方法执行结束处理");
return null;
}
}
测试类
public class CglibTest {
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
Duck duck = (Duck) cglibProxy.bind(new Duck());
duck.yell();
}
}
输出结果
目标类方法执行前处理
Duck quack
目标类方法执行结束处理
输出结果表明,在目标类Duck.yell()方法执行前后,代理进行了拦截。