一、AOP的概念和原理
AOP(Aspect-Oriented Programming):面向切面编程,是一种新的方法论,切面能够帮助我们模块化横切关注点,简言之,横切关注点可以被描述为影像应用多出的功能,列入安全就是一个横切关注点,应用中的许多方法都涉及到安全规则,如下图所示:

对传统OOP(Ojected-Oriented Programming)的补充和完善.
切面的优点:
• 每个关注点度集中在一个地方,而不是分散到多处代码中
• 服务模块更简洁,因为他们只包含主要关注点,(或者核心功能)的代码,而次要关注点的代码被转移到切面中了.
• 实现了业务逻辑和系统级的服务进行隔离,使得业务逻辑和系统级的服务耦合度降低 ,从而提高系统的重用性和开发效率.
名词解释:
业务逻辑: 某个类的某个方法本身要实现的功能。
系统级的服务: 系统的日志、事务、权限验证。
二、如何实现AOP(底层实现原理)
每个动态代理类度必须实现,InvocationHandler这个接口,并且每个代理类的实列都关联到了一个handler当我们通过一个代理类调用一个方法的时候,我们方法的调用就会被转发为由InvocationHandler这个接口的invoke方法来调用,invoke是唯一的唯一的方法.
主要有两种方法:jdk的动态代理和cglib的动态代理
1. jdk动态代理(利用java的反射思想)
JDK的动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。其中InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑,在并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编织在一起。
首先编写IUserDao接口编写两个方法
package com.dqsy.spring.proxy;
public interface IUserDao {
public void addUser();
public void serchUser();
}
实现这个接口的方法
package com.dqsy.spring.proxy.impl;
import com.dqsy.spring.proxy.IUserDao;
public class IUserDaoImpl implements IUserDao {
@Override
public void addUser() {
System.out.println("添加方法....");
}
@Override
public void serchUser() {
// TODO Auto-generated method stub
System.out.println("寻找方法....");
}
}
创建测试类
package com.dqsy.spring.test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.junit.Test;
import com.dqsy.spring.proxy.IUserDao;
import com.dqsy.spring.proxy.impl.IUserDaoImpl;
public class AopTest {
/**
* 常规的调用
*/
@Test
public void test(){
IUserDao userDao = new IUserDaoImpl();
userDao.serchUser();
}
/**
* JDk动态代理方法
*/
@Test
public void proxytest(){
//实列化目标对象
IUserDao target = new IUserDaoImpl();
IUserDao proxy = ((IUserDao) Proxy.newProxyInstance(
//目标类ClassLoad定义了有那个ClassLoad对象生成代理进行加载
target.getClass().getClassLoader(),
//目标类的接口,一个interface接口,表示我们将为我们的代理对象提供一组接口,如果我们吗提供一组接口给它
//那么代理就宣称实现该接口(多态)这样我们就调用该接口中的方法了
target.getClass().getInterfaces(),
//匿名内部类
new InvocationHandler(){
/**
* proxy:指我们要代理的那个真实对象
* method:指我们要调用真是对象的方法的method对象
* params:指我们要调用真是方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
// TODO Auto-generated method stub
System.out.println("添加方法...");
System.out.println(""+method.getName());
Object result = method.invoke(target, params);
System.out.println("编写日志...");
return result;
}
}));
proxy.serchUser();
}
}
结果如下:
那么另一个方法结果又是怎样呢?
以上方法就是在没有用spring的知识,而实现aop思想的.
2.cglib动态代理
使用JDK创建代理有一个限制,即它只能为接口创建代理,这一点我们从Proxy的接口方法newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)就看得很清楚,第三个入参interfaces就是为代理实例指定的实现接口。虽然,面向接口的编程被很多很有影响力人(包括Rod Johnson)的推崇,但在实际开发中,开发者也遇到了很多困惑:难道对一个简单业务表的操作真的需要创建5个类(领域对象类、Dao接口,Dao实现类,Service接口和Service实现类)吗?对于这一问题,我们还是留待大家进一步讨论。现在的问题是:对于没有通过接口定义业务方法的类,如何动态创建代理实例呢?JDK的代理技术显然已经黔驴技穷,CGLib作为一个替代者,填补了这个空缺。
CGLib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,并在拦截方法相应地织入横切逻辑。
小结:
Spring AOP在底层就是利用JDK动态代理或CGLib动态代理技术为目标Bean织入横切逻辑。在这里,我们对以上两节动态创建代理对象做一个小结。
在PerformaceHandler和CglibProxy中,有三点值得注意的地方是:第一,目标类的所有方法都被添加了性能监视横切的代码,而有时,这并不是我们所期望的,我们可能只希望对业务类中的某些方法织入横切代码;第二,我们手工指定了织入横切代码的织入点,即在目标类业务方法的开始和结束前调用;第三,我们手工编写横切代码。以上三个问题,在AOP中占用重要的地位,因为Spring AOP的主要工作就是围绕以上三点展开:Spring AOP通过Pointcut(切点)指定在哪些类的哪些方法上施加横切逻辑,通过Advice(增强)描述横切逻辑和方法的具体织入点(方法前、方法后、方法的两端等),此外,Spring还通过Advisor(切面)组合Pointcut和Advice。有了Advisor的信息,Spring就可以利用JDK或CGLib的动态代理技术为目标Bean创建织入切面的代理对象了。
JDK动态代理所创建的代理对象,在JDK 1.3下,性能强差人意。虽然在高版本的JDK中,动态代理对象的性能得到了很大的提高,但是有研究表明,CGLib所创建的动态代理对象的性能依旧比JDK的所创建的代理对象的性能高不少(大概10倍)。而CGLib在创建代理对象时性能却比JDK动态代理慢很多(大概8倍),所以对于singleton的代理对象或者具有实例池的代理,因为不需要频繁创建代理对象,所以比较适合用CGLib动态代理技术,反之适合用JDK动态代理技术。此外,由于CGLib采用生成子类的技术创建代理对象,所以不能对目标类中的final方法进行代理。