什么是AOP
- aop指的是面向切面编程,aop采取横向抽取机制取代了传统的纵向继承重复性代码
- aop使用纯java语言实现,不需要专门的编译过程和类加载器
AOP的作用
- Aop一般用来处理权限控制,日志记录,加载事务等非主要业务逻辑的事情。底层是采用cglib和jdk的动态代理实现的。
aop的术语(术语解释直接套用某位大神的文章)
- AOP术语
连接点( Joinpoint ):可以被增强的方法都可以成为连接点
程序执行的某个特定位置:如类开始初始化前、类初始化后、类某个方法调用前、调用后、方法抛出异常后。-个类或-段程序代码拥有一 些具有边界性质的特定点 ,这些点中的特定点就称为"连接点”。Spring仅支持方法的连接点 ,即仅能在 方法调用前、方法调用后、方法抛出异常时以及方法调用前后这些程序执行点织入增强。连接点由两个信息确定:第是用方法表示的程序执行点;第二是用相对点表示的方位。
切点( Pointcut ):真正被拦截到的点
每个程序类都拥有多个连接点,如一-个拥有两个方法的类,这两个方法都是连接点,即连接点是程序类中客观存在的事物。AOP通过“ 切点”定位特定的连接点。连接点相当于数据库中的记录,而切点相当于查询条件。切点和连接点不是一-对一的 关系,一个切点可以匹配多个连接点。在Spring中 ,切点通过org.springframework. aop.Pointcut接口进行描述,它使用类和方法作为连接点的查询条件, Spring AOP的规则解析引擎负责切点所设定的查询条件,找到对应的连接点。其实确切 地说,不能称之为查询连接点,因为连接点是方法执行前、执行后等包括方位信息的具体程序执行点,而切点只定位到某个方法上,所以如果希望定位到具体连接点上,还需要提供方位信息。
增强( Advice ):拦截后要做的事情,对方法增加权限的校验等
增强是织入到目标类连接点上的一段程序代码,在Spring中,增强除用于描述-段程序代码外, 还拥有另- -个和连接点相关的信息,这便是执行点的方位。结合执行点方位信息和切点信息,我们就可以找到特定的连接点。
目标对象(Target):要被增强的对象
增强逻辑的织入目标类。如果没有AOP ,目标业务类需要自己实现所有逻辑,而在AOP的帮助下,目标业务类只实现那些非横切逻辑的程序逻辑,而性能监视和事务管理等这些横切逻辑则可以使用AOP动态织入到特定的连接点上。
引介(Introduction)
引介是一种特殊的增强,它为类添加一些属性和方法。这样,即使一个业务类原本没有实现某 个接口,通过AOP的引介功能,我们可以动态地为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。
织入(Weaving):指advice应用到target的过程
织入是将增强添加对目标类具体连接点上的过程。AOP像-台织布机,将目标类.增强或引介通过AOP这台织布机天衣无缝地编织到-起。根据不同的实现技术, AOP有三种织入的方式:
a.编译期织入,这要求使用特殊的Java编译器。
b.类装载期织入,这要求使用特殊的类装载器。
C.动态代理织入,在运行期为目标类添加增强生成子类的方式。
Spring采用动态代理织入,而Aspect]采用编译期织入和关装载期织入。
代理(Proxy)
一个类被AOP织入增强后,就产出了一个结果类,它是融合了原类和增强逻辑的代理类。根据不同的代理方式,代理类既可能是和原类具有相同接口的类,也可能就是原类的子类,所以我们可以采用调用原类相同的方式调用代理类。
切面(Aspect)
切面由切点和增强(引介)组成,它既包括了横切逻辑的定义,也包括了连接点的定义,Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。
两种动态代理JDK动态代理和CGLIB动态代理
jdk动态代理(有接口的情况下)
创建一个接口UserDao,再创建一个类实现这个接口
接下来我们创建一个类使用JDK动态代理
- 继承InvocationHandler接口
public class JdkProxy implements InvocationHandler
- 创建一个对象
private UserDao userDao;
- 使用构造方法初始化对象
public JdkProxy(UserDao userDao){ this.userDao=userDao; }
public Object createProxy(){
Object proxy= Proxy.newProxyInstance(userDao.getClass().getClassLoader(),userDao.getClass().getInterfaces(),this);
return proxy;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("name".equals(method.getName())){
System.out.println("权限校验...");
return method.invoke(userDao,args);
}
return method.invoke(userDao,args);
}
测试
CGLIB代理(无接口的情况下)
1、创建一个类
public class StudentDao{
public void save(){
System.out.println("保存商品...");
}
public void delete(){
System.out.println("删除商品...");
}
}
2、创建一个类继承MethodInterceptor接口
public class MyCglibProxy implements MethodInterceptor {
private StudentDao studentDao;
public MyCglibProxy(StudentDao studentDao){
this.productDao=productDao;
}
public Object createProxy(){
//创建核心类
Enhancer enhancer=new Enhancer();
//设置父类(目标类)
enhancer.setSuperclass(studentDao.getClass());
//设置回调
enhancer.setCallback(this);
//生成代理
Object proxy=enhancer.create();
return proxy;
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if("save".equals(method.getName())){
System.out.println("权限校验==========");
return methodProxy.invokeSuper(proxy,args);
}
/*引入invokeSuper表示调用父类的方法:相当于调用productDao中的方法*/
return methodProxy.invokeSuper(proxy,args);
}
}
3、测试
@Test
public void demo1(){
StudentDao studentDao=new StudentDao ();
StudentDao proxy=(StudentDao)new MyCglibProxy(studentDao).createProxy();
proxy.save();
proxy.delete();
}