从代理模式到Spring AOP

匆匆过客 提交于 2020-04-17 04:28:09

【推荐阅读】微服务还能火多久?>>>

1、现实场景

场景:小A要在丰台科技园租一个单间。

如果不使用房产中介,一般的流程是这样:

浏览广告--->筛选房源--->约见房东--->鉴别真假--->谈判签约--->入住

小A的核心目标是租房,但在入住前他和大多数人一样都得亲自去筛选房源、约见房东、鉴别真假等,然而这些附加的任务并不是他们的专长,而且还浪费了精力。

如果使用正规中介,一般的流程是这样:

告知需求--->中介处理--->通知入住

用经济学的话来解释,就是“劳动分工,物尽其用,提高生产效率,维护社会稳定" , 在软件设计中的我们这样说“高内聚,低耦合,提高了开发效率,便于软件维护”。

2、Java代理模式

2.1、概念

 

Subject:抽象主题,申明目标方法operation()

RealSubject:具体的主题,实现Subject,在operation()下完成具体的业务逻辑

Proxy:代理,实现Subject,并引用RealSubject,在operation()中调用RealSubject的operation(),并在调用前后添加一些附加功能

2.2、Java模拟中介租房

下面使用Java代理模式来实现最上面的小A通过中介找房的场景:

  • 租房Subject

public interface Rent  
{   
    /**租房*/
    void rentHouse(String prams);
}
  • 租房RealSubject

public class RealRent implements Rent  
{   
    /**租房*/
    public void rentHouse(String prams){
        System.out.println("租客入住中:"+prams);
    }
}
  • 租房Proxy

public class RentProxy  implements Rent
{
 // 引用租房业务插件
 private Rent rent = null;
 public RentProxy(Rent rent) {
  this.rent = realRent;
 }
 /** 代理租房 */
 public void rentHouse(String prams) {
  // 前置
  beforeRent();
  // 调用目标方法:租客入住
  rent.rentHouse(prams);
  // 后置
  afterRent();
 }
 /** 租房前 */
 public void beforeRent() {
  System.out.println("租客入住前:记录租户需求、筛选认证房源、约见房东...");
 }
 /** 租房后 */
 public void afterRent() {
  System.out.println("租客入住后:售后跟踪...");
 }
}
  •  租房Client

public class Client {
 public static void main(String[] args) {
  // 租房业务实例
  Rent realRent = new RealRent();
  // 租房代理实例
  RentProxy proxy = new RentProxy(realRent);
  // 通过代理租房
  proxy.rentHouse("小A,中关村,主卧");
 }
}

 测试运行结果如下:

3、静态代理&动态代理

在上面的代理模式下,Proxy类需要在编译前创建出来,而且新增一个业务Interface,就得新创建一个代理类,这就是所谓的“静态代理”;

如果Proxy类不用定义,在程序运行时利用Java反射机制动态创建Proxy实例,即所谓的“动态代理”;

需要使用到 java.lang.reflect.Proxy 类和java.lang.reflect.InvocationHandler 接口

 下面用Java的动态代理实现日志功能

/**
 * 接口A
 */
public interface ServiceA {
 void service(String name);
}
/**
 * 接口A实现类
 */
public class ServiceAImpl implements ServiceA {
 @Override
 public void service(String name) {
  System.out.println("service "+name+" is executing...");
 }
}
/**
 * 接口B
 */
public interface ServiceB {
 void service(String name);
}
/**
 * 接口B实现类
 */
public class ServiceBImpl implements ServiceB {
 @Override
 public void service(String name) {
  System.out.println("service "+name+" is executing...");
 }
}
/**
 * InvocationHandler实现类:处理日志并调用目标方法
 */
public class LogInvocation implements InvocationHandler{
 //目标类
 private Object target;
 
 public LogInvocation(Object target){
  this.target=target;
 }
 
 /**
  * 执行日志功能,并调用被代理类的方法
  * @param proxy 
  * @param method 
  * @param args
  */
 public Object invoke(Object proxy, Method method, Object[] args)
   throws Throwable {
  System.out.println("代理者:"+proxy.getClass().getName()); 
  System.out.println("被代理者:"+target.getClass().getName()); 
  //前置
  before(args[0]);
  //调用目标方法
  method.invoke(target, args);
  //后置
  after(args[0]);
  return null;
 }
 /**before日志*/
 private void before(Object name) {
  System.out.println("loging for "+name+" before target method is invoked");
 }
 
 /**after日志*/
 private void after(Object name) {
  System.out.println("loging for "+name+" after target method is invoked");
  System.out.println();
  
 }
}
/**
 * 客户端
 */
public class Client {
 public static void main(String[] args) {
  // 创建目标实例serviceB
  ServiceA serviceA = new ServiceAImpl();
  // 动态ServiceA的代理实例
  ServiceA proxyA = (ServiceA) Proxy.newProxyInstance(serviceA.getClass()
    .getClassLoader(), serviceA.getClass().getInterfaces(),
    new LogInvocation(serviceA));
  // 执行代理
  proxyA.service("A");
  // 创建目标实例serviceB
  ServiceB serviceB = new ServiceBImpl();
  // 动态ServiceB的代理实例
  ServiceB proxyB = (ServiceB) Proxy.newProxyInstance(serviceB.getClass()
    .getClassLoader(), serviceB.getClass().getInterfaces(),
    new LogInvocation(serviceB));
  // 执行代理
  proxyB.service("B");
 }
}

测试运行结果如下:

代理者:com.sun.proxy.$Proxy0
被代理者:com.breadoffer.member.module.index.ServcieAImpl
loging for A before target method is invoked
service A is executing...
loging for A after target method is invoked

代理者:com.sun.proxy.$Proxy1
被代理者:com.breadoffer.member.module.index.ServcieBImpl
loging for B before target method is invoked
service B is executing...
loging for B after target method is invoked

通过上面的代码,可以发现,如果没有接口Interface,动态代理是没法实现的,通常把这种代理叫做“Jdk动态代理”,java还有另外一种不需要接口的动态代理,叫做“Cglib动态代理”,这里不再介绍。

4、Spring AOP

Spring AOP基于动态代理,根据目标类有无接口来自动选择“Jdk动态代理" 或 "Cglib动态代理"

4.1、概念

  • Aspect(切面):一个提供横切服务的组件(比如日志模块,狭义一点就是Log类)

  • Join Point(连接点):目标方法被调用的地方

  • Advice(通知):连接点上执行的动作,发生在调用目标方法前后,包括before、after、after-returning、after-throwing、around

  • PointCut (切入点):连接点的集合

  • Introduction(引入):允许对一个存在的类添加新的方法和属性

  • Target Object(目标对象):被通知的对象,An object advised by one or more aspects

  • Weaving(织入):将切面和其他对象连接起来创建一个Target Object

     

  

4.2、应用

利用Spring AOP可以很方便地将系统中一些共通的需求封装成Aspect,实现模块之间的解耦,比如日志、事务、数据同步等

下面简单列举基于Spring AOP注解的日志管理

待续......

 

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!