浅谈Spring-AOP(一)

Spring-AOP

谈到Spring那就必然避不开两个核心问题,一个是IOC/DI(控制反转/依赖注入),另一个就是我们今天要谈的AOP

何为AOP?我们都叫做面向切面编程,首先我们来谈一谈它的作用

##AOP的作用

我们都知道IOC的作用是大大降低了对象之间的耦合性,我们将对象的创建以及管理统统交给Spring容器管理,再深究一下,它可以管理框架.虽然AOP不能像IOC一样可以站在更高的角度去解决问题,但是一屋不扫何以扫天下呢?所以AOP的作用,其实是解决了业务层代码的耦合性,使业务层代码变得更加纯粹.

总结:AOP的作用是,不改变源代码的情况下,可以扩展其他业务

代码说明

我们通过一段伪代码来看一下,假如没有AOP的思想,我们业务层代码会是什么样子

public class UserServiceImp implements UserService{
    @Override
    public void addUser() {
        System.out.println("增加成功");
    }

这是一个实现类,假如我现在还需要加入日志的功能,我们如何做呢?大概是这个样子~~

public class UserServiceImp implements UserService{
    @Override
    public void addUser() {
        System.out.println("增加一条日志");
        System.out.println("增加成功");
    }

加入我现在又要加入获取方法名以及方法参数呢?

public class UserServiceImp implements UserService{
    @Override
    public void addUser() {
        System.out.println("增加一条日志");
        System.out.println("增加成功");
        System.out.println("方法名");
        System.out.println("方法参数");
    }

通过上述伪代码,可以看出,每次增加新的需求都会大动干戈,修改我的源代码,使业务层的代码变得混乱不堪.那该怎么办,如何使业务层代码松耦合呢?我们的AOP就是在解决这个问题的.那么AOP又是如何解决这个问题的呢?其实AOP的核心思想就是动态代理机制.为了更好的理解AOP我们先来聊聊动态代理

##关于动态代理

在聊动态代理之前我们先来聊聊什么是代理

在生活当中其实有很多代理角色,比如:房屋中介,婚介所,等等我们就以房屋中介为例

假如我现在是包租公,手上有几栋房子要出租,因为我只想单纯的出租房,并不想和租房者签合同,谈价钱(毕竟不缺钱),那怎么办呢?我就把房子托管到房屋中介,让中介帮我租房,这个时候,我什么都不用管了,所有的一切都交给中介,包括租房,签合同等等,并且不影响租房者租房.这样我就单纯给中介租我的房,不用做其他了,我就变得很纯粹,其他业务交给中介就好了.接下来我们用代码实现一下这个场景.

场景还原

1.首先,房东要出租房屋,中介也出租房屋,所以我们将出租房屋这件事提取为一个接口,让房东和中介同时实现接口

public interface House {
    void rent();
}

2.房东实现接口

public class Host implements House{
    @Override
    public void rent() {
        System.out.println("我要出租房");
    }
}

3.中介实现接口

public class HouseProxy implements House{
    Host host = new Host();
    @Override
    public void rent() {
        host.rent();
    }
}

4.租房者租房

public class User {
    public static void main(String[] args) {
        HouseProxy houseProxy = new HouseProxy();
        houseProxy.rent();
    }
}

通过中介这层关系我们不用找到房东也可以租到房子,并且假如我们提出需求要看房,我们可以在中介的代码中修改业务而不用改动房东的业务,我们实现一下

public class HouseProxy implements House{
    Host host = new Host();
    @Override
    public void rent() {
        look();
        host.rent();
    }
    public void look(){
        System.out.println("看房");
    }
}

改好之后,租房者调用方法后,既能看房也能租房了,通过这一顿操作我们发现,房东的业务就变得很纯粹了,只是单纯的租房业务,想要添加其他业务找中介就行,大大降低了业务层代码的耦合性.

这个逻辑就是代理机制,只不过这是静态代理,静态代理的弊端就是,我们必须手动的创建代理对象,这样代码就会很啰嗦,知道了静态代理,我们接下来就可以聊动态代理了

动态代理说明

所谓动态代理,就是我们不用自己创建代理而是自动生成代理对象,在降低了业务层耦合的同时也提高了开发的效率.动态代理有两种实现方式

1.jdk原生代码中的动态代理

2.CGLIB动态代理

jdk原生动态代理机制

如果使用jdk原生动态代理那么就必须满足一个条件,就是代理和被代理实现同一个接口,在被代理代码不给修改的同时,代理可以扩展其他业务

public class JDKProxy {
    public static Object getProxy(Object target){
        //获取被代理对象的类加载器
        ClassLoader loader = target.getClass().getClassLoader();
        // 获取接口数组
        Class<?>[] interfaces = target.getClass().getInterfaces();
        // 创建代理对象
        return Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("事务开启");
                Object result = method.invoke(target, args);
                System.out.println("事务提交");
                return result;
            }
        });
    }
}

我们通过查看Proxy底层代码发现,几乎都是静态方法,那么我们通过其中的newProxyInstance方法创建一个代理对象,其中有三个重要参数

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

loader:类加载器

interfaces:一个接口数组,因为java是多实现

h:将扩展方法加载到容器

这里的第三个参数InvocationHandler是一个接口,我们需要实现其中的一个方法Invoke()

Invoke中也有三个参数

proxy:obj对象

method:方法对象,通过反射机制,这个是获取核心业务方法的

args:参数

CGLIB动态代理

CGLIB不是原生的所以需要导入jar包,并且不需要有接口,但是要满足代理是被代理的子类(继承),原理是通过子类扩展父类业务.



public class CglibProxy {

    public static Object getObject(Object target){
        //创建增强器对象
        Enhancer enhancer = new Enhancer();
        //设置父级
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(getMethodInterceptor());
        return enhancer.create();
    }

    public static MethodInterceptor getMethodInterceptor(){

        return new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("数据库事务开始");
                //执行父类的方法
                Object proxy = methodProxy.invokeSuper(obj,objects);
                System.out.println("数据库事务提交");
                return proxy;
            }
        };
    }

}


jdk原生动态代理与CGLIB动态代理的区别

1.jdk原生动态代理是jdk本身拥有的,不用导入jar包,CGLIB需要导入jar包

2.jdk原生动态代理,被代理必须有接口,并且代理和被代理都要实现相同接口,CGLIB没有接口也可以实现,只需要代理是被代理的子类即可

你可能感兴趣的:(spring,java,后端)