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注解的日志管理
待续......
来源:oschina
链接:https://my.oschina.net/u/735642/blog/645531