1、静态代理
1.1 代码实现
// 接口
interface Hello{
String sayHello(String str);
}
// 实现
class HelloImp implements Hello{
@Override
public String sayHello(String str) {
return "HelloImp: " + str;
}
}
// 静态代理方式
class StaticProxiedHello implements Hello{
private Hello hello = new HelloImp();
@Override
public String sayHello(String str) {
logger.info("You said: " + str);
return hello.sayHello(str);
}
}
上例中静态代理类StaticProxiedHello作为HelloImp的代理,实现了相同的Hello接口。
1.2 总结
由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
2、动态代理
2.1 代理
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* jdk代理方式
* @Title: DynamicProxy.java
* @Package com.spring.pro.jdk.proxy
* @Description:
* @author ybwei
* @date 2018年11月8日 下午6:10:57
* @version V1.0
*/
public class DynamicProxy implements InvocationHandler {
private Object target;
public DynamicProxy(Object target) {
this.target = target;
}
@SuppressWarnings("unchecked")
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
sayBefore();
Object result = method.invoke(target, args);
sayAfter();
return result;
}
private void sayBefore() {
System.out.println("before...");
}
private void sayAfter() {
System.out.println("after...");
}
}
2.2 测试
import com.spring.pro.jdk.proxy.DynamicProxy;
import com.spring.pro.service.Hello;
import com.spring.pro.service.HelloImpl;
/**
* @Title: Test.java
* @Package com.spring.pro.jdk.test
* @Description:
* @author ybwei
* @date 2018年11月8日 下午6:12:10
* @version V1.0
*/
public class Test {
public static void main(String[] args) {
DynamicProxy dynamicProxy = new DynamicProxy(new HelloImpl());
Hello hello = dynamicProxy.getProxy();
hello.sayHello("JDK");
}
}
结果:
before...
Hello by JDK
after...
2.3 总结
在程序运行时,运用反射机制动态创建而成。 动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。
缺点:
只能对实现了接口的类进行,没有实现接口的类不能使用JDK动态代理。
如果对象没有实现接口我们该如何代理呢?答案是CGLIB。
3、CGLIB
3.1 代码实现
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
/**
* cglib代理方式
* @Title: CGLibProxy.java
* @Package com.spring.pro.proxy
* @Description:
* @author ybwei
* @date 2018年11月8日 下午5:43:24
* @version V1.0
*/
public class CGLibProxy implements MethodInterceptor {
// 单例模式
private static CGLibProxy instance = new CGLibProxy();
private CGLibProxy() {
}
public static CGLibProxy getInstance() {
return instance;
}
public <T> T getProxy(Class<T> cls) {
return (T) Enhancer.create(cls, this);
}
public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
sayBefore();
Object result = methodProxy.invokeSuper(obj, objects);
sayAfter();
return result;
}
private void sayBefore() {
System.out.println("before...");
}
private void sayAfter() {
System.out.println("after...");
}
}
3.2 测试
import com.spring.pro.cglib.proxy.CGLibProxy;
import com.spring.pro.service.Hello;
import com.spring.pro.service.HelloImpl;
/**
* @Title: Test.java
* @Package com.spring.pro.proxy.test
* @Description:
* @author ybwei
* @date 2018年11月8日 下午5:45:11
* @version V1.0
*/
public class Test {
public static void main(String[] args) {
Hello helloCGLib = CGLibProxy.getInstance().getProxy(HelloImpl.class);
helloCGLib.sayHello("CGLib");
}
}
3.3 总结
JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
4、spring aop
SpringAOP动态代理策略
- 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP 。
- 如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换。
- 如果目标对象实现了接口,可以强制使用CGLIB实现AOP。