AOP底层分析-代理模式
代理模式分类
- 静态代理
- 动态代理
一、代理的基本构成:
代理模式上,基本上有Subject角色,RealSubject角色,Proxy角色。其中:Subject角色负责定义RealSubject和Proxy角色应该实现的接口;RealSubject角色用来真正完成业务服务功能;Proxy角色负责将自身的Request请求,调用realsubject 对应的request功能来实现业务功能,自己不真正做业务。
- 抽象角色Subject:一般使用接口或抽象类解决
- 真实角色RealSubject:被代理角色
- 代理角色Proxy:代理真实角色,一般会增加一些附属操作
- 客户Client:访问代理角色的人
二、静态代理
举例:房东租房
抽象角色Subject
public interface Rent {
public void Rent();
}
真实角色RealSubject
public class Host implements Rent{
public void Rent() {
System.out.println ("房东要出租房子");
}
}
代理角色Proxy
public class Proxy implements Rent{
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
//签租赁合同-公共业务
public void Contract()
{
System.out.println ("签租赁合同");
}
//收中介费-公共业务
public void fee(){
System.out.println ("收中介费");
}
//看房-公共业务
public void SeeHouse(){
System.out.println ("中介带你看房");
}
public void Rent() {
host.Rent ();
SeeHouse ();
fee ();
Contract ();
}
}
客户Client
public class Client {
public static void main(String[] args) {
Host host = new Host (); //房东要租房子
Proxy proxy = new Proxy (host); //代理 中介帮房东租房子(***代理角色一般会有一些附属操作)
//不用面对房东直接找中介租房即可
proxy.Rent ();
}
}
举例:用户增删改查
抽象角色Subject
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
真实角色RealSubject
public class UserServiceImpl implements UserService{
public void add() {
System.out.println ("增加了一个用户");
}
public void delete() {
System.out.println ("删除了一个用户");
}
public void update() {
System.out.println ("修改了一个用户");
}
public void query() {
System.out.println ("查询了一个用户");
}
}
/*改动原有逻辑代码是大忌
* */
代理角色Proxy
public class UserServiceProxy implements UserService{
UserService userService;
public UserServiceProxy() {
}
public UserServiceProxy(UserService userService) {
this.userService = userService;
}
public void add() {
log ("add");
userService.add ();
}
public void delete() {
log ("delete");
userService.delete ();
}
public void update() {
log("update");
userService.update ();
}
public void query() {
log("query");
userService.query ();
}
public void log(String message){
System.out.println ("[DEBUG]:使用了"+message+"方法");
}
}
客户Client
public class Client {
public static void main(String[] args) {
UserService userService=new UserServiceProxy (new UserServiceImpl ());
userService.add ();
}
}
静态代理实现步骤
- 编写接口
- 编写实现接口的真实角色
- 编写实现接口的代理角色,需要有一个属性可以调用真实角色
- 客户端访问代理角色,传入真实角色参数
静态代理优点:
- 可以使真实角色的操作更加纯粹,不用去关注一些公共业务
- 公共业务实现交给代理角色,实现业务的分工
- 公共业务发生扩展时,方便集中管理
静态代理缺点:
一个真实角色就会产生一个代理角色,代码量翻倍,开发效率低
解决办法->动态代理
三、Srping框架的AOP技术底层也是采用的动态代理技术,通过反射(动态加载类)动态代理
代理的方式提供了三种
1. 基于JDK的动态代理(Proxy-代理,InvocationHandler-调用处理程序)
必须是面向接口的,只有实现了具体接口的类才能生成代理对象
抽象角色Subject
public interface Rent {
public void Rent();
}
真实角色RealSubject
public class Host implements Rent {
public void Rent() {
System.out.println ("房东要出租房子");
}
}
/*改动原有逻辑代码是大忌
* */
代理角色Proxy
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/*处理程序*/
//用这个类自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//处理代理实例(被代理的人)并返回结果 真正执行的方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理机制就是通过反射实现
seeHouse ();
Object invoke = method.invoke (rent, args);//通过反射调用真实角色方法
fee ();
return invoke;
}
//生成得到代理类
public Object getProxy()
{
return Proxy.newProxyInstance (this.getClass ().getClassLoader (), rent.getClass ().getInterfaces (), this);
}
//公共业务类
public void seeHouse()
{
System.out.println ("看房子");
}
public void fee()
{
System.out.println ("收中介费");
}
}
客户Client
public class Client {
public static void main(String[] args) {
//真实角色
Host host=new Host ();
//代理角色
ProxyInvocationHandler pih = new ProxyInvocationHandler ();
//通过调用程序来设置我们要调用的接口对象
pih.setRent (host);
Rent proxy = (Rent) pih.getProxy (); //这里的proxy就是动态生成的,并没有写
proxy.Rent ();
}
}
2. 基于CGLIB动态代理
对于没有实现接口的类,也可以产生代理,产生这个类的子类的方式
为什么不直接使用Java动态代理,而要使用CGLIB呢?
CGLIB相比于JDK动态代理更加强大,JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了
在JDK动态代理中提供一个Proxy类来创建代理类,而在CGLib动态代理中也提供了一个类似的类Enhancer;使用的CGLib版本是2.2.2,不同的版本有点小差异,建议用3.x版本的
首先要导入cglib的依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
抽象角色Subject
public interface Rent {
public void Rent();
}
真实角色RealSubject
public class Host implements Rent {
public void Rent() {
System.out.println ("房东要出租房子");
}
}
/*改动原有逻辑代码是大忌
* */
代理角色Proxy
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor{
public Object getRentProxy(Class clazz){
//Enhancer是CGLIB中最常用的一个类,和Java1.3动态代理中引入的Proxy类差不多
Enhancer enhancer = new Enhancer();
//告诉cglib,生成的子类需要继承那个类
enhancer.setSuperclass(clazz);
//设置回调
enhancer.setCallback(this);
//加载到jvm中,并返回代理对象
return enhancer.create();
}
//第一个是代理对象,租房子这里是房东
//第二个是我们要调用的房东租房子的rent方法,
//第三个是房东租房子rent方法需要的参数
//第四个是代理对象的方法对象,它可以调用rent的方法,因为cglib实现的动态代理,代理对象和真实对象之间是父子关系
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("CGlib房屋中介开始执行。。。");
methodProxy.invokeSuper(o,objects);
System.out.println("CGlib房屋中介结束执行。。。");
return null;
}
}
客户Client
public class Client {
//java动态代理是利用反射机制生成一个实现被代理对象的接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
//cglib动态代理利用asm开源包,对被代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
//执行者 CGlibAgency
//被代理对象 Programmer
public static void main(String[] args) {
Host host= ( Host) new CglibProxy ().getRentProxy(new Host().getClass());
host.Rent ();
}
}
3.java字节码:javasist
Javassist是一个开源的分析、编辑和创建Java字节码的类库。javassist是jboss的一个子项目,其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。
第三种不常用就不做过多介绍