1、概念
OOP,对一个实体的属性和行为进行抽象,封装成一个对象,这样能获得清晰高效的逻辑单元的划分。
AOP,对影响多个业务模块的公共行为定义到一个可重用模块,封装成一个切面,这样能够减少重复的代码,降低业务模块和非业务模块之间的耦合性,增加代码的可操作性和可维护性。比如日志记录、权限认证、性能优化、事务等功能,都可以用切面实现。
2、底层实现
1)JDK动态代理
public class JdkProxyFactory implements InvocationHandler {
// 被代理对象
private Object target;
// 在构造方法对象时,传入被代理对象
public JdkProxyFactory(Object target) {
this.target = target;
}
// 创建代理
public Object createProxy() {
// 三个参数: 类加载器、 实现接口、 invocationhandler
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("记录日志!!!!!!");
// target:被代理对象, args:方法参数 , method:被调用的方法
return method.invoke(target, args);
}
}
JDK动态代理要求target必须实现接口,若没有实现接口,不能使用JDK动态代理。
2)Cglib动态代理
public class CglibProxyFactory implements MethodInterceptor {
// 被代理目标对象
private Object target;
// 在构造工厂时传入被代理对象
public CglibProxyFactory(Object target) {
this.target = target;
}
// 创建代理对象方法
public Object createProxy() {
// 1、 创建Enhancer对象
Enhancer enhancer = new Enhancer();
// 2、 cglib创建代理,对目标对象,创建子类对象
enhancer.setSuperclass(target.getClass());
// 3、传入 callback对象,对目标增强
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("记录日志......");
// 按照JDK编程
return method.invoke(target, args);
}
}
Cglib不但可以对接口实现代理,也可以对目标类对象实现代理,解决了JDK只能代理接口的问题。
Spring AOP 框架对代理类的处理原则:
如果目标对象的实现类实现了接口,Spring AOP 将会采用 JDK 动态代理来生成 AOP 代理类;
如果目标对象的实现类没有实现接口,Spring AOP 将会采用 CGLIB 来生成 AOP 代理类。
3、AOP编程
applicationContext.xml文件中开启AspectJ自动代理
<aop:aspectj-autoproxy/>
定义切面
@Aspect
public class MyAspect {
/**
* 可定义多个切点
*/
@Pointcut("execution(* cn.demo.personDaoImpl.*(..))")
private void myPointCut1() {
}
@Pointcut("execution(* cn.demo.userDaoImpl.*(..))")
private void myPointCut2() {
}
/**
* 定义多种类型的通知
*/
@Before(value = "myPointCut1()")
public void before(JoinPoint joinPoint) {
System.out.println("前置通知...");
}
@AfterReturning(value = "myPointCut1()")
public void afterReturning(JoinPoint joinPoint) {
System.out.println("后置通知...");
}
@Around(value = "myPointCut2()")
public void around(JoinPoint joinPoint) {
System.out.println("环绕通知...");
}
@AfterThrowing(value = "myPointCut2()", throwing = "e")
public void afterThrowing(JoinPoint joinPoint, Throwable e) {
System.out.println(joinPoint.toLongString() + "方法发生异常:" + e.getMessage());
}
@After(value = "myPointCut2()")
public void after(JoinPoint joinPoint) {
System.out.println("最终通知...释放资源...");
}
}
测试:
public class AopTest{
public static void main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
PersonDao pd = context.getBean("personDao" ,PersonDao.class);
pd.eat("西瓜");
System.out.println(pd.getClass());
}
}
输出:
前置通知...
西瓜
后置通知...
class $Proxy7
虽然程序是在调用 PersonDao的eat 方法,但从上面运行结果不难看出:实际执行的绝对不是 PersonDao 对象的方法,而是 AOP 代理的方法。也就是说,Spring AOP 为 PersonDao类生成了 AOP 代理类。
System.out.println(p.getClass())输出 class $Proxy7,这说明此时的 AOP 代理并不是由 CGLIB 生成的,而是由 JDK 动态代理生成的。
4、AOP相关术语
1)JoinPoint(连接点)
连接点,指被拦截到的某个方法。
2)PointCut(切点)
指代对哪些方法进行拦截(PointCut >= JoinPoint)。
3)Advice(通知)
拦截到JoinPoint之后要执行的增强方法。
4)Aspect(切面)
切点和通知的结合,指一个切面类。