1.AOP简介
是对OOP编程方式的一种补充。翻译过来为“面向切面编程”。
可以理解为一个拦截器框架,但是这个拦截器会非常武断,如果它拦截一个类,那么它就会拦截这个类中的所有方法。如对一个目标列的代理,增强了目标类的所有方法。
两个解决办法:
1.不优雅的做法:
在添加增强时,根据方法名去判断,是否添加增强,但是这样就得一直去维护这个增强类。
2.面向切面:
将增强类和拦截条件组合在一起,然后将这个切面配置到 ProxyFactory 中,从而生成代理。
二、AOP 和 切面的关系
1.类比于 OOP 和 对象,AOP 和 切面就是这样的一种关系。
2.也可以将 切面 看成是 AOP 的一个工具。
三、几个概念
切面(Advisor):是AOP中的一个术语,表示从业务逻辑中分离出来的横切逻辑,比如性能监控,日志记录,权限控制等。
这些功能都可以从核心的业务逻辑中抽离出去。可以解决代码耦合问题,职责更加单一。封装了增强和切点。
增强(Advice):增强代码的功能的类,横切到代码中。
目标:目标方法(JDK代理)或目标类(CGLIB代理)
代理:JDK代理,CGLIB代理。或是通过 ProxyFactory 类生产。
切点:通过一个条件来匹配要拦截的类,这个条件称为切点。如拦截所有带 Controller 注解的类。增强的条件。
连接点:作为增强方法的入参,可以获取到目标方法的信息。
四、概括为一张图
五、增强
1.Weaving(织入):对方法进行增强
(1)前置增强(BeforeAdvice):在目标方法前调用。
(2)后置增强(AfterAdvice):在目标方法后调用。
(3)环绕增强(AroundAdvice):将 Before 和 After ,甚至抛出增强和返回增强合到一起。
(4)返回增强(AfterReturningAdvice):在方法返回结果后执行,该增强可以接收到目标方法返回结果。
(5)抛出增强(AfterThrowingAdvice):在目标方法抛出对应的类型后执行,可以接收到对应的异常信息。
2.Introduction(引入):对类进行增强
(1)引入增强(DeclareParentsAdvice):想让程序在运行的时候动态去实现某个接口,需要引入增强。
2.代码实现
2.1 AOP的xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- xmlns:aop="http://www.springframework.org/schema/aop" 声明要用aop的标签
xsi:schemaLocation= aop的标签地址
"http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
-->
<bean id="studentDao" class="org.jsoft.dao.impl.StudentDaoImpl">
</bean>
<bean id="studentDaoByMybatis" lazy-init="true"
class="org.jsoft.dao.impl.StudentDaoByMybatisImpl">
</bean>
<bean id="studentService" class="org.jsoft.service.impl.StudentServiceImpl"
scope="singleton" autowire="byName">
</bean>
<!-- AOP面向切面的配置 -->
<!-- 切面对应的类,即切面类 -->
<bean id="log" class="org.jsoft.log.Log" />
<!-- -->
<!-- -->
<aop:config>
<aop:aspect id="myAspect" ref="log"><!-- 切面标签,id标记名称,ref对应的切面类bean的id -->
<!-- 切点标签,id标记名称 -->
<aop:pointcut id="businessService"
expression="execution(* org.jsoft.service..*.*(..))" /><!-- 在该范围的类的所有方法下,执行AOP方法 -->
<!-- 程序执行前,要执行的方法"before" -->
<aop:before pointcut-ref="businessService" method="before"/><!-- -->
<!-- 程序执行后,要执行的方法"after" -->
<aop:after pointcut-ref="businessService" method="after"/>
<!-- 程序整个过程,要执行的方法"around" -->
<aop:around pointcut-ref="businessService"
method="around" />
</aop:aspect>
</aop:config>
</beans>
2.2 对应切面类Log
package org.jsoft.log;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import org.aspectj.lang.ProceedingJoinPoint;
//这是一个切面类,已放入IoC容器里.
public class Log implements InvocationHandler{
private Object obj;
public void before(){//(1)前置增强,需要实现:MethodBeforeAdvice 接口
System.out.println("Log.before()");
}
public void after(){//(2)后置增强:实现 AfterReturningAdvice 接口
System.out.println("Log.after()");
}
//(3)环绕增强:实现 org.aopalliance.intercept.MethodInterceptor 接口,
//使用 Object result = methodInvocation.proceed(); 调用目标方法。在目标方法前后添加增强
public Object around(ProceedingJoinPoint pjp) throws Throwable{
long l = System.currentTimeMillis();
Object retVal = pjp.proceed();
//new Thread().sleep(100);
System.out.println(System.currentTimeMillis() - l);
System.out.println("Log.around()");
return retVal;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
before();
Object result = method.invoke(obj, args);
after();
return result;
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
}
2.3 main测试
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**ApplicationContext 是接口,下面的ClassPathXmlApplicationContext也可以用,后者可以用.close()方法*/
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"spring/spring.xml"});
Student s = new Student();
s.setName("Joy");
Log log = new Log();
log.setObj(studentService);
//动态代理
//参数1 classLoader类加载器
//interfaces造出来的类要实现那些接口
IStudentService studentServiceProxy = (IStudentService)Proxy.newProxyInstance(Main.class.getClassLoader(), studentService.getClass().getInterfaces(), log);
studentServiceProxy.add(s);
context.close();