谈到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原生动态代理那么就必须满足一个条件,就是代理和被代理实现同一个接口,在被代理代码不给修改的同时,代理可以扩展其他业务
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不是原生的所以需要导入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没有接口也可以实现,只需要代理是被代理的子类即可