静态代理
代理对象的两个概念:
1、代理对象存在的价值主要用于拦截对真实业务对象的访问。
2、代理对象应该具有和目标对象(真实业务对象)相同的方法。
/**
* @author acoffee
* @create 2021-11-16 9:49
*/
//接口
interface Cookie{
void cooking();
}
//实现类
class CookieImpl implements Cookie{
@Override
public void cooking() {
System.out.println("煮饭!");
}
}
//代理类
class StaticProxy{
private Cookie cookie;
public StaticProxy(Cookie cookie){
this.cookie = cookie;
}
public void cooking(){
System.out.println("洗菜!");
cookie.cooking();
System.out.println("洗碗!");
}
}
//测试类
public class StaticProxyTest {
public static void main(String[] args) {
StaticProxy staticProxy = new StaticProxy(new CookieImpl());
staticProxy.cooking();
}
}
执行结果:
上述代码
静态代理的缺点:
一个代理类只能为一个接口服务,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。 如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。原因就是代理类和目标对象的类都是在编译期间就已经确定下来了,不利于程序的扩展。
最好就是让一个代理类完成所有的代理,所以就有了我们的动态代理。
动态代理
动态代理分为两种:
① JDK动态代理
② CGLIB 动态代理
Spring Aop底层就是用的这两种代理
① JDK动态代理
JDK动态代理: 利用 反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
JDK动态代理主要是依赖java.lang.reflect的Proxy
类和InvocationHandler
接口,
Proxy类中我们主要是调用Proxy类中的newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
方法可以创建一个动态代理对象,但是这个方法需要3个参数,前两个参数是固定的,但第三个参数比较麻烦,需要我们创建一个类MyInvocationHandler
来实现InvocationHandler
接口,这个类里面要重写invoke()方法,我们这里为了方便是使用的匿名内部类。
/**
* @author acoffee
* @create 2021-11-16 13:56
*/
//接口类
interface CookieService{
void cooking();
}
//实现类
class CookieServiceImpl implements CookieService{
@Override
public void cooking() {
System.out.println("煮饭!");
}
}
//JDK代理类
class JdkDynamicProxy{
public Object getProxyObject(Object targetClass) {
Object proxyInstance = Proxy.newProxyInstance(targetClass.getClass().getClassLoader(), targetClass.getClass().getInterfaces(),
//InvocationHandler:我们这里使用lambda表达式,他是一个函数式接口
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("洗菜!");
Object invoke = method.invoke(targetClass, args);
System.out.println("洗碗!");
return invoke;
}
});
/**InvocationHandler:我们这里使用lambda表达式,他是一个函数式接口
(Object proxy, Method method, Object[] args) -> {
System.out.println("洗菜!");
Object invoke = method.invoke(targetClass, args);
System.out.println("洗碗!");
return invoke;
});*/
return proxyInstance;
}
}
//测试类
public class JdkDynamicTest {
@Test
public void testJdk(){
CookieService cookieService = new CookieServiceImpl();
JdkDynamicProxy jdkDynamicProxy = new JdkDynamicProxy();
CookieService proxyObject = (CookieService) jdkDynamicProxy.getProxyObject(cookieService);
proxyObject.cooking();
}
}
执行结果:
② CGLIB 动态代理
CGlib动态代理: 利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
/**
* @author acoffee
* @create 2021-11-21 18:17
*/
//被代理类
class CookieServiceImpl {
public void cooking() {
System.out.println("煮饭!");
}
}
//代理类
class CglibDynamic {
public Object getProxyObject(Object targetClass) {
//这里是不能使用Lambda表达式,因为这里第二个参数需要的是Callback接口,因为Callback不是函数式接口
//我们这里可以写InvocationHandler接口的原因是因为public interface InvocationHandler extends Callback
//InvocationHandler 接口继承了Callback 接口
Object obj = Enhancer.create(targetClass.getClass(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("洗菜!");
Object invoke = method.invoke(targetClass, args);
System.out.println("洗碗!");
return invoke;
}
});
return obj;
}
}
//测试类
public class CglibDynamicTest {
@Test
public void testCglib() {
CglibDynamic cglibDynamic = new CglibDynamic();
CookieServiceImpl proxyObject = (CookieServiceImpl) cglibDynamic.getProxyObject(new CookieServiceImpl());
proxyObject.cooking();
}
}
JDK动态代理 VS Cglib动态代理
JDK动态代理和cglib动态代理有什么区别?
JDK动态代理智能对实现了接口的类生成代理对象;
Cglib可以对任意类生成代理对象,它的原理是对目标对象进行继承代理,如果目标对象被final
修饰,那么该类无法被Cglib代理。
JDK动态代理
1、因为利用JDKProxy
生成的代理类实现了接口,所以目标类中所有的方法在代理类中都有。
2、生成的代理类的所有的方法都拦截了目标类的所有的方法。而拦截器中invoke方法的内容正好就是代理类的各个方法的组成体。
3、利用JDKProxy
方式必须有接口的存在。
4、invoke
方法中的三个参数可以访问目标类的被调用方法的API、被调用方法的参数、被调用方法的返回类型。
cglib动态代理
1、 Cglib
是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
2、 用Cglib
生成代理类是目标类的子类。
3、 用Cglib
生成 代理类不需要接口
4、 用Cglib
生成的代理类重写了父类的各个方法。
5、 拦截器中的intercept
方法内容正好就是代理类中的方法体