Aop的概述
1.什么是Aop的技术
- 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程
- AOP是一种编程范式,隶属于软工范畴,指导开发者如何组织程序结构
- AOP最早由AOP联盟的组织提出的,制定了一套规范.Spring将AOP思想引入到框架中,必须遵守AOP联盟的规范
- 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
- AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型
- 利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
2. AOP:面向切面编程.(思想.---解决OOP遇到一些问题)
3. AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)
4. 为什么要学习AOP
- 可以在不修改源代码的前提下,对程序进行增强!!
Spring框架的AOP的底层实现
-
Srping框架的AOP技术底层也是采用的代理技术,代理的方式提供了两种
- 1基于JDK的动态代理
- 必须是面向接口的,只有实现了具体接口的类才能生成代理对象
1.2.基于CGLIB动态代理
* 对于没有实现了接口的类,也可以产生代理,产生这个类的子类的方式 - 1基于JDK的动态代理
Spring的传统AOP中根据类是否实现接口,来采用不同的代理方式
1. 如果实现类接口,使用JDK动态代理完成AOP
2. 如果没有实现接口,采用CGLIB动态代理完成AOP
JDK的动态代理
原理: 针对内存中Class对象,使用类加载器 动态为目标对象实现接口的创建代理类
使用Proxy类来生成代理对象的一些代码如下:
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;
}
}
CGLIB的代理技术:
CGlib采用非常底层字节码技术,可以为一个类创建子类,解决无接口代理问题
- 引入CBLIB的开发包
* 如果想使用CGLIB的技术来生成代理对象,那么需要引入CGLIB的开发的jar包,在Spring框架核心包中已经引入了CGLIB的开发包了。所以直接引入Spring核心开发包即可!
编写相关的代码:
public static OrderDaoImpl getProxy(){
// 创建CGLIB核心的类
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(OrderDaoImpl.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);
}
});
// 生成代理对象
OrderDaoImpl proxy = (OrderDaoImpl) enhancer.create();
return proxy;
}
```
结论:
1).若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。
2).若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类,程序中应优先对接口创建代理,便于程序解耦维护
Spring基于AspectJ的AOP的开发
技术分析之AOP的相关术语
- Joinpoint(连接点) 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
- Pointcut(切入点) 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义
- Advice(通知/增强) 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
- Introduction(引介) 引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field
- Target(目标对象) 代理的目标对象
- Weaving(织入) 是指把增强应用到目标对象来创建新的代理对象的过程 生成动态代理的过程
- Proxy(代理) 一个类被AOP织入增强后,就产生一个结果代理类
- Aspect(切面) 是切入点和通知的结合,以后咱们自己来编写和配置的
技术分析之AspectJ的XML方式完成AOP的开发
- 步骤一:创建JavaWEB项目,引入具体的开发的jar包
先引入Spring框架开发的基本开发包
再引入Spring框架的AOP的开发包
spring的传统AOP的开发的包
spring-aop-4.2.4.RELEASE.jar
com.springsource.org.aopalliance-1.0.0.jar//aop联盟定义了规范
aspectJ的开发包
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar//aspectJ jar包
spring-aspects-4.2.4.RELEASE.jar//spring整合aspectJ的jar包 - 步骤二:创建Spring的配置文件,引入具体的AOP的schema约束
- 步骤三:创建包结构,编写具体的接口和实现类
* com.demo2
* CustomerDao -- 接口
* CustomerDaoImpl -- 实现类 - 步骤四:将目标类配置到Spring中
- 步骤五:定义切面类
public class MyAspectXml {
// 定义通知
public void log(){
System.out.println("记录日志...");
}
}
- 步骤六:在配置文件中定义切面类
- 步骤七:在配置文件中完成aop的配置
- 完成测试
@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();
}
}
切入点的表达式
- 再配置切入点的时候,需要定义表达式,重点的格式如下:execution(public * *(..)),具体展开如下:
切入点表达式的格式如下:
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
修饰符可以省略不写,不是必须要出现的。
返回值类型是不能省略不写的,根据你的方法来编写返回值。可以使用 * 代替。
包名例如:com..demo3.BookDaoImpl
首先com是不能省略不写的,但是可以使用 * 代替
中间的包名可以使用 * 号代替
如果想省略中间的包名可以使用 ..
类名也可以使用 * 号代替,也有类似的写法:*DaoImpl
方法也可以使用 * 号代替
参数如果是一个参数可以使用 * 号代替,如果想代表任意参数使用 ..
AOP的通知类型
- 前置通知
在目标类的方法执行之前执行。
配置文件信息:
应用:可以对方法的参数来做校验最终通知
在目标类的方法执行之后执行,如果程序出现了异常,最终通知也会执行。
在配置文件中编写具体的配置:
应用:例如像释放资源后置通知
方法正常执行后的通知。
在配置文件中编写具体的配置:
应用:可以修改方法的返回值异常抛出通知
在抛出异常后通知
在配置文件中编写具体的配置:
应用:包装异常的信息环绕通知
方法的执行前后执行。
在配置文件中编写具体的配置:
要注意:目标的方法默认不执行,需要使用ProceedingJoinPoint对来让目标对象的方法执行。
xml配置文件代码如下:
接口类代码:
package demo02;
public interface CustomerDao {
public void save();
public void update();
}
实现类代码:
package demo02;
public class CustomerDaoImpl implements CustomerDao {
@Override
public void save() {
System.out.println("保存客户信息");
}
@Override
public void update() {
System.out.println("修改客户信息");
}
}
切面类代码:
package demo02;
import org.aspectj.lang.ProceedingJoinPoint;
/*
* 切点类:切入点+通知
*/
public class MyaspectXml {
/*
* 通知强
*/
//前置通知
public void log() {
System.out.println("记录日志");
}
//后置通知
public void after() {
System.out.println("后置通知");
}
//环绕通知
public void around(ProceedingJoinPoint joinPoint) {
System.out.println("环绕通知1");
try {
//手动让目标方法执行
joinPoint.proceed();
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("环绕通知2");
}
//异常抛出通知
public void afterthrowing() {
System.out.println("异常抛出通知");
}
//最终通知
public void afterreturning() {
System.out.println("最终通知");
}
}
测试代码:
package demo02;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/*
* 测试aop功能
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class Main {
@Resource(name="customerDao")
private CustomerDao customerDao;
@Test
public void run() {
customerDao.save();
customerDao.update();
}
}
Spring框架的Aop技术(注解方式)
- 步骤一:创建JavaWEB项目,引入具体的开发的jar包
先引入Spring框架开发的基本开发包
再引入Spring框架的AOP的开发包
spring的传统AOP的开发的包
spring-aop-4.2.4.RELEASE.jar
com.springsource.org.aopalliance-1.0.0.jar
aspectJ的开发包
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring-aspects-4.2.4.RELEASE.jar - 步骤二:创建Spring的配置文件,引入具体的AOP的schema约束
步骤三:创建包结构,编写具体的接口和实现类
com.demo1
CustomerDao -- 接口
CustomerDaoImpl -- 实现类-
步骤四:将目标类配置到Spring中
步骤五:定义切面类
具体的代码如下
@Aspect
public class MyAspectAnno {
@Before(value="execution(public void com.demo1.CustomerDaoImpl.save())")
public void log(){
System.out.println("记录日志...");
}
}
- 步骤六:在配置文件中定义切面类
- 步骤七:在配置文件中开启自动代理
- 完成测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo1 {
@Resource(name="customerDao")
private CustomerDao customerDao;
@Test
public void run1(){
customerDao.save();
customerDao.update();
}
}
需要增强的类代码如下:
package demo02;
public class CustomerDaoImpl implements CustomerDao {
@Override
public void save() {
System.out.println("保存客户信息");
}
@Override
public void update() {
System.out.println("修改客户信息");
}
}
切面类代码如下:
package demo02;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.weaver.JoinPointSignatureIterator;
/*
* 注解方式的aop
*/
@Aspect
public class MyaspectAnno {
@Before(value="execution(public void demo02.CustomerDaoImpl.save())")
public void log() {
System.out.println("记录日志");
}
@After(value="execution(public void demo02.CustomerDaoImpl.save())")
public void after() {
System.out.println("最终通知");
}
@Around(value="MyaspectAnno.fn()")
public void around(ProceedingJoinPoint joinPoint) {
System.out.println("环绕一");
try {
joinPoint.proceed();
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("环绕二");
}
//自定义切入点
@Pointcut(value="execution(public void demo02.CustomerDaoImpl.save())")
public void fn() {
}
}
XML文件配置代码如下: