AOP概述
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程。
- AOP是一种编程范式,隶属于软工范畴,指导开发者如何组织程序结构。
- AOP最早由AOP联盟的组织提出的,制定了一套规范.Spring将AOP思想引入到框架中,必须遵守AOP联盟的规范。
- 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
- AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。
- 利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
- AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)
Spring框架的AOP的底层实现
Spring的传统AOP中根据类是否实现接口,来采用不同的代理方式
- 如果实现类接口,使用JDK动态代理完成AOP
- 如果没有实现接口,采用CGLIB动态代理完成AOP
1,基于JDK的动态代理
必须是面向接口的,只有实现了具体接口的类才能生成代理对象
/**
* 使用JDK的方式生成代理对象
* @author Administrator
*/
public class MyProxyUtils {
public static UserDao getProxy(final UserDao dao) {
// 使用Proxy类生成代理对象
UserDao proxy = (UserDao) Proxy.newProxyInstance(dao.getClass().getClassLoader(),
dao.getClass().getInterfaces(), new InvocationHandler() {
// 代理对象方法一直线,invoke方法就会执行一次
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("save".equals(method.getName())){
System.out.println("记录日志...");
// 开启事务
}
// 提交事务
// 让dao类的save或者update方法正常的执行下去
return method.invoke(dao, args);
}
});
// 返回代理对象
return proxy;
}
}
2,基于CGLIB动态代理
对于没有实现了接口的类,也可以产生代理,产生这个类的子类的方式
public class MycglibUtils {
public static BookDaoImpl getProxy(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(BookDaoImpl.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args,MethodProxy methodProxy) throws Throwable {
if("save".equals(method.getName())){
// 记录日志
System.out.println("记录日志了...");
}
return methodProxy.invokeSuper(obj, args);
}
});
BookDaoImpl proxy = (BookDaoImpl) enhancer.create();
return proxy;
}
}
Spring基于AspectJ的AOP的开发
1,术语
名称 | 意义 | 解释 |
---|---|---|
Joinpoint | 连接点 | 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点 |
Pointcut | 切入点 | 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义 |
Advice | 通知/增强 | 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能) |
Introduction | 引介 | 引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field |
Target | 目标对象 | 代理的目标对象 |
Weaving | 织入 | 是指把增强应用到目标对象来创建新的代理对象的过程 |
Proxy | 代理 | 一个类被AOP织入增强后,就产生一个结果代理类 |
Aspect | 切面 | 是切入点和通知的结合,以后咱们自己来编写和配置的 |
2,AspectJ的XML方式完成AOP的开发
2.1,依赖导入与约束
<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.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
2.2,创建包结构,编写具体的接口和实现类
- package:com.itheima.demo2
- Interface:CustomerDao
- Impl:CustomerDaoImpl
2.3,目标类配置到Spring中
<bean id="customerDao" class="com.itheima.demo3.CustomerDaoImpl"/>
2.4,定义切面类
public class MyAspectXml {
// 定义通知
public void log(){
System.out.println("记录日志...");
}
}
2.5,在配置文件中定义切面类
<bean id="myAspectXml" class="com.itheima.demo3.MyAspectXml"/>
2.6,在配置文件中完成aop的配置
<aop:config>
<!-- 引入切面类 -->
<aop:aspect ref="myAspectXml">
<!-- 定义通知类型:切面类的方法和切入点的表达式 -->
<aop:before method="log" pointcut="execution(public * com.itheima.demo3.CustomerDaoImpl.save(..))"/>
</aop:aspect>
</aop:config>
2.7,完成测试
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml")
public class Demo3 {
@Resource(name="customerDao")
private CustomerDao customerDao;
@Test
public void run1(){
customerDao.save();
customerDao.update();
customerDao.delete();
}
}
3,切入点的表达式
配置切入点的时候,需要定义表达式,重点的格式如下:execution(public * *(..)),具体展开如下:
<aop:before method="log" pointcut="execution(public * com.itheima.demo3.CustomerDaoImpl.save(..))"/>
解释:execution([修饰符] 返回值类型 包名.类名.方法名(参数))
- 修饰符可以省略不写,不是必须要出现的
- 返回值类型是不能省略不写的,根据你的方法来编写返回值。可以使用 * 代替
- 包名例如:com.itheima.demo3.BookDaoImpl
- 首先com是不能省略不写的,但是可以使用 * 代替
- 中间的包名可以使用 * 号代替
- 如果想省略中间的包名可以使用 ..
- 类名也可以使用 * 号代替,也有类似的写法:*DaoImpl
- 方法也可以使用 * 号代替
- 参数如果是一个参数可以使用 * 号代替,如果想代表任意参数使用 ..
4,AOP的通知类型
4.1,前置通知
- 在目标类的方法执行之前执行。
- 配置文件信息
<aop:after method="before" pointcut-ref="myPointcut3"/>
- 应用:可以对方法的参数来做校验
4.2,最终通知
- 在目标类的方法执行之后执行,如果程序出现了异常,最终通知也会执行。
- 配置文件信息
<aop:after method="after" pointcut-ref="myPointcut3"/>
- 应用:例如像释放资源
4.3,后置通知
- 方法正常执行后的通知。
- 配置文件信息
<aop:after-returning method="afterReturning" pointcut-ref="myPointcut2"/>
- 应用:可以修改方法的返回值
4.4,异常抛出通知
- 在抛出异常后通知
- 配置文件信息
<aop:after-throwing method="afterThorwing" pointcut-ref="myPointcut3"/>
- 应用:包装异常的信息
4.5,环绕通知
- 方法的执行前后执行。
- 配置文件信息
在配置文件中编写具体的配置:<aop:around method="around" pointcut-ref="myPointcut2"/>
- 注意:目标的方法默认不执行,需要使用ProceedingJoinPoint对来让目标对象的方法执行。
public void around(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("around 11111");
joinPoint.proceed();
System.out.println("around 222222");
}