AOP从理论到实践(一)

背景:

AOP(Aspect-Oriented Programming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。简单地说,AOP就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。

实现原理:

AOP 代理其实是由 AOP 框架动态生成的一个对象,该对象可作为目标对象使用。AOP 代理包含了目标对象的全部方法,但 AOP 代理中的方法与目标对象的方法存在差异:AOP 方法在特定切入点添加了增强处理,并回调了目标对象的方法。纵观 AOP 编程,其中需要程序员参与的只有 3 个部分:定义普通业务组件定义切入点,一个切入点可能横切多个业务组件。定义增强处理,增强处理就是在 AOP 框架为普通业务组件织入的处理动作。
以上三部分重点就是定义切入点和增强处理,不难发现Spring AOP的实现原理就是:AOP 框架负责动态地生成 AOP 代理类,这个代理类的方法则由 Advice 和回调目标对象的方法所组成。

从历史到现代

写死代码

我们每研究一个技术,总是会从最简单,最常用的例子开始,下面看看这个例子:
接口:

package com.service;

public interface Greeting {
    void sayHello(String name);
}

实现类:

package com.serviceImp;

import com.service.Greeting;

public class GreetingImpl implements Greeting {

    @Override
    public void sayHello(String name) {
        before();
        after();
    }
    private void before(){
        System.out.print("Before");;
    }
    private void after(){
        System.out.println("After");
    }
}

before() 与 after() 方法写死在 sayHello() 方法体中了,这样的代码的味道非常不好。如果哪位仁兄大量写了这样的代码,肯定要被你的架构师骂个够呛。

比如:我们要统计每个方法的执行时间,以对性能作出评估,那是不是要在每个方法的一头一尾都做点手脚呢?

再比如:我们要写一个 JDBC 程序,那是不是也要在方法的开头去连接数据库,方法的末尾去关闭数据库连接呢?

这样的代码只会把程序员累死,把架构师气死!

一定要想办法对上面的代码进行重构,首先给出三个解决方案:

静态代理:

在上面的基础上添加代理类:

package com.serviceImp;

import com.service.Greeting;

public class GreetingProxy implements Greeting {

    private GreetingImpl greetingImpl;

    public GreetingProxy(GreetingImpl greetingImpl){
        this.greetingImpl=greetingImpl;
    }
    @Override
    public void sayHello(String name) {
        before();
        greetingImpl.sayHello(name);
        after();

    }
    private void before(){
        System.out.println("Before");
    }
    private void after(){
        System.out.println("After");
    }

}

客户端调用:

import com.service.Greeting;
import com.serviceImp.GreetingImpl;
import com.serviceImp.GreetingProxy;


public class Client {

    public static void main(String[] args) {
        Greeting greetingProxy=new GreetingProxy(new GreetingImpl());
        greetingProxy.sayHello("jack");
    }

}

问题:
但是如果这样没增加一个类就增加一个代理类,岂不是有n个类等着我们去添加呢?可不可以用一个代理类呢?于是我们想到了jdk动态代理

jdk动态代理:

package com.jdkProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKDynamicProxy implements InvocationHandler{

    private Object target;

    public JDKDynamicProxy(Object target){
        this.target=target;
    }

    @SuppressWarnings("unchecked")
    public <T>T getProxy(){
        return(T)Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        before();
        Object result=method.invoke(target, args);
        after();
        return result;
    }
    private void before(){
        System.out.println("Before");
    }
    private void after(){
        System.out.println("After");
    }

}

客户端:

import com.jdkProxy.JDKDynamicProxy;
import com.service.Greeting;
import com.serviceImp.GreetingImpl;
import com.serviceImp.GreetingProxy;


public class Client {

    public static void main(String[] args) {
        Greeting greetingProxy=new JDKDynamicProxy(new GreetingImpl()).getClass();
        greetingProxy.sayHello("jack");
    }

}

问题:jdk代理只能代理有接口的,因此可以说,如果要用jdk动态代理的,我们所有的类都必须有接口,但是在实际中并不是这样的,因此我们引用了另一种方式:CGLib动态代理

CGLib动态代理

package com.CGLibProxy;

import java.lang.reflect.Method;


import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CGLibDynamicProxy implements MethodInterceptor{

    private static CGLibDynamicProxy instance=new CGLibDynamicProxy();
    private CGLibDynamicProxy(){

    }
    public static CGLibDynamicProxy getInstance(){
        return instance;
    }
    @SuppressWarnings("unchecked")
    public <T>T getProxy(Class<T> cls){
        return (T) Enhancer.create(cls,this);
    }
    @Override
    public Object intercept(Object target, Method method, Object[] arg2,
            MethodProxy proxy) throws Throwable {
        before();
        Object result=proxy.invokeSuper(target, arg2);
        after();
        return result;
    }
    private void before(){
        System.out.println("Before");
    }
    private void after(){
        System.out.println("After");
    }

}

客户端调用:

import com.CGLibProxy.CGLibDynamicProxy;
import com.jdkProxy.JDKDynamicProxy;
import com.service.Greeting;
import com.serviceImp.GreetingImpl;
import com.serviceImp.GreetingProxy;


public class Client {

    public static void main(String[] args) {
        Greeting greetingProxy=CGLibDynamicProxy.getInstance().getProxy(GreetingImpl.class);
        greetingProxy.sayHello("jack");
    }

}

到此,这样实现是否就足够优雅了呢?不同的方法使用的场景不同,每种方法都有属于自己的那个舞台~

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值