10_springAOP基础_动态代理
0x01_代理模式的概念
代理模式是一种设计模式,是通过代理对象访问目标对象,这样可以在目标对象基础上增强额外的功能,如添加权限,访问控制和审计等功能。
一张经典的UML图:
代理模式分为静态代理和动态代理。(个人觉得代理模式可能是23种设计模式中比较难的设计模式)。
0x02_静态代理
静态代理中代理类与被代理类都需要实现同一个接口,这就说明一个静态代理类只能代理一个类,并且还要事先知道我们要代理哪个类才能写代理类,如果我们有其他类还想使用代理那就必须再写一个代理类。然而在实际开发中我们是可能是有非常多的类是需要被代理的,并且事先我们可能并不知道我们要代理哪个类。所以如果继续使用静态代理反而会增加许多的工作量,并且效率低下,代码复用率也不好。
下面来看一个例子理解:
package com.bones.test1;
public class Test1_Static_Proxy_Pattern {
public static void main(String[] args) {
SellHouse o1 = new Owner("小明");
o1.sell();
}
}
//接口
interface SellHouse{//接口:卖房
void sell();
}
//被代理的类
class Owner implements SellHouse{
private String name;
public Owner(String name) {
this.name = name;
}
@Override
public void sell() {
System.out.println("我是"+this.name+",周末我有空,出来聊聊价格?");
}
}
分析:上面的有一个接口,是
SellHouse
,有一个方法sell
,代表卖房的业务有业主
Owner
类,业主有name属性,同时业主要实现SellHouse
接口里面的sell
方法,但是实现有限,因为实际情况下业主很忙,也有别的事要做,而且有些情况下,不能随意修改源代码,那么就考虑加个代理类。
增加代理类之后的代码:
package com.bones.test1;
public class Test1_Static_Proxy_Pattern {
public static void main(String[] args) {
Owner o1 = new Owner("小明");
SellHouse agency = new Agency(o1);
agency.sell();
}
}
//接口
interface SellHouse{//接口:卖房
void sell();
}
//被代理的类
class Owner implements SellHouse{
private String name;
public Owner(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public void sell() {
System.out.println("我是"+this.name+",周末我有空,出来聊聊价格?");
}
}
//代理类
class Agency implements SellHouse{
private Owner owner;
public Agency(Owner owner) {
this.owner = owner;
}
@Override
public void sell() {
System.out.println("我是"+owner.getName()+"委托的中介,有啥事找我聊聊也行");
System.out.println("业主"+owner.getName()+"说过:");
owner.sell();
}
}
运行结果:
0x03_动态代理
动态代理可以针对于一些不特定的类或者一些不特定的方法进行代理,我们可以在程序运行时动态的变化代理的规则,代理类在程序运行时才创建的代理模式称为动态代理。这种情况下,代理类并不是在Java代码中定义好的,而是在程序运行时根据我们的在Java代码中的“指示”动态生成的
下面介绍2个:
- Proxy 动态代理 JDK动态代理 面向接口 ----》不需要导入第三方依赖
- cglib 动态代理 第三方动态代理 面向父类 —〉需要导入第三方依赖
Proxy_JDK动态代理
说明:
jdk的动态代理,实际上是通过字节码技术,动态的编写代理类,然后生成该代理类的实例。该实例作为
Proxy.newProxyInstance
方法返回给当前创建代理的对象。
首先有一个接口:有2个功能,sell
和money
interface SellHouse{
void sell();
void money();
}
其次有被代理类:业主Person
,实现了接口SellHouse
,有自己的属性name
和一个带有这个属性的构造器.然后实现了接口的2个方法
class Person implements SellHouse{
private String name;
public Person(String name) {
this.name = name;
}
@Override
public void sell() {
System.out.println("用户"+this.name+"说:和中介聊吧,我太忙了");
}
@Override
public void money() {
System.out.println("收钱真高兴啊");
}
}
因为业主很忙,所以需要中介,并且中介可以完善并且丰富卖房的功能(增强)。
class Agency extends Person{
public Agency(String name) {
//这里可以做增强
super(name);
//这里可以做增强
}
@Override
public void sell() {
super.sell();
}
}
但是如果代理类的功能需要发生变化时,每一次都需要去修改代理类的代码,不方便,扩展性也不好,所以考虑用JDK动态代理:(这时候就不再需要Agency
这个类了)
@Test
public void test(){
SellHouse p = new Person("蔡徐坤");
//newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
//ClassLoader loader
ClassLoader classLoader = p.getClass().getClassLoader();
//Class<?>[] interfaces
Class<?>[] interfaces = p.getClass().getInterfaces();
//InvocationHandler h
InvocationHandler myInvocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res = null;//如果有返回值
//Object proxy 代理对象
//Method method 被代理的方法
//Object[] args 被代理方法运行时的实参
if (method.getName().equals("sell")){
System.out.println("你好,我是中介");
res = method.invoke(p,args);
System.out.println("联系电话:12345");
}else {//如果是其他方法,直接执行即可
res = method.invoke(p,args);
}
return res;
}
};
SellHouse proxyInstance = (SellHouse) Proxy.newProxyInstance(classLoader, interfaces, myInvocationHandler);
proxyInstance.sell();
}
说明:
利用Java的反射技术(
Reflection
),在运行时创建一个实现某些给定接口的新类(也称“动态代理类”)及其实例(对象),代理的是接口(Interfaces
),不是类(Class
),也不是抽象类。在运行时才知道具体的实现,spring aop
就是此原理。
Proxy.newProxyInstance
需要三个参数:
ClassLoader loader
: 用哪个类加载器去加载代理对象可以用被代理类的字节码文件创建类构造器:
ClassLoader classLoader = p.getClass().getClassLoader();
Class<?>[] interfaces
:动态代理类需要实现的接口,被代理对象所实现的所有接口Class<?>[] interfaces = p.getClass().getInterfaces();
InvocationHandler h
:动态代理方法在执行时,会调用h里面的invoke方法去执行,执行处理器对象,专门用于定义增强的规则当让代理对象调用任何方法时,都会触发
invoke
方法的执行。invoke方法3个参数:
- Object proxy 代理对象
- Method method 被代理的方法
- Object[] args 被代理方法运行时的实参