一.什么是AOP?
面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的技术
AOP是OOP的延续,是软件开发中的热点,也是spring框架中的重要内容,是函数式编程的一种衍生范型
利用AOP可以对业务各个部分进行隔离,从而使业务各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
AOP是OOP的扩展和延伸,解决OOP开发遇到的问题
可以通过预编译和运行期动态代理实现不修改源代码情况下给程序添加功能
二.AOP和OOP的区别
OOP面向名词领域,AOP面向动词领域
OOP针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加高效清晰的逻辑单元划分]
而AOP是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,降低各部分耦合度
三.为什么学习AOP?
对程序进行增强,在不修改源代码情况下,AOP可以进行权限校验,日志记录,性能监控,事务控制
四.AOP的由来
1.AOP最早是由AOP联盟提出的,制定了一套规范,spring将AOP思想引入框架中,必须遵守AOP联盟的规范
spring的AOP有自己的实现方式(非常繁琐)
AspectJ是一个AOP的框架,spring引入AspectJ作为自身的AOP开发
2.spring的两套AOP开发
(1)spring传统方式(弃用)
(2)spring基于AspectJ的AOP的开发(使用)
五.AOP底层实现
动态代理
1.JDK动态代理
只能对实现接口的类进行代理
(1)接口
public interface UserDao {
void add();
void delete();
void update();
void find();
}
(2)接口实现类
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("add...");
}
@Override
public void delete() {
System.out.println("delete...");
}
@Override
public void update() {
System.out.println("update...");
}
@Override
public void find() {
System.out.println("find...");
}
}
(3)进行动态代理
对delete()方法进行动态代理
public class JdkProxy implements InvocationHandler {
//被代理类
private UserDao userDao;
public JdkProxy(UserDao userDao) {
this.userDao = userDao;
}
//返回代理对象
public UserDao createProxy(){
UserDao proxy = (UserDao)Proxy.newProxyInstance(userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(),
this);
return proxy;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//代理对象 正在执行的方法 参数
//要代理的方法
if("delete".equals(method.getName())){
System.out.println("代理...");
return method.invoke(userDao,args);
}
return method.invoke(userDao,args);
}
}
(4)测试
public static void main(String[]args){
//jdk动态代理
UserDao userDao=new UserDaoImpl();
UserDao proxy=new JdkProxy(userDao).createProxy();
proxy.delete();
}
2.Cglib动态代理
可以对没有实现接口的类进行动态代理
spring AOP 内部会自动判断有没有实现接口,进而选择JDK动态代理或是Cglib动态代理自动实现动态代理
六.AOP开发中的相关术语
连接点:可以被拦截的点
切入点:真正被拦截的点
通知:增强的方法
引介:类的增强
目标:被增强的对象
织入:将增强应用到目标的过程
代理:织入增强后产生的对象
切面:切入点和通知的组合
七.AOP入门示例
(1)编写目标类
public interface ProductDao {
void add();
void delete();
void update();
void find();
}
(2)编写实现类
public class ProductImpl implements ProductDao{
@Override
public void add() {
System.out.println("add..商品");
}
@Override
public void delete() {
System.out.println("delete..商品");
}
@Override
public void update() {
System.out.println("update..商品");
}
@Override
public void find() {
System.out.println("find..商品");
}
}
(3)在xml中配置目标对象
<!--配置目标对象,将要增强的类交给spring管理-->
<bean id="ProductDao" class="org.qingyu.dao.ProductImpl"></bean>
<!--将切面类交给spring管理-->
<bean id="MyAspect" class="org.westos.service.MyAspect"></bean>
<!--通过AOP的配置完成对目标类产生代理-->
<aop:config>
<!--表达式配置哪些类的那些方法需要增强-->
<aop:pointcut id="pointcut1" expression="execution(* org.qingyu.dao.ProductImpl.add(..))"></aop:pointcut>
<!--配置切面-->
<aop:aspect ref="MyAspect">
<aop:before method="checkPri" pointcut-ref="pointcut1"></aop:before>
</aop:aspect>
</aop:config>
</beans>
(4)编写测试类
public static void main(String[]args){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
ProductDao productDao = (ProductDao) applicationContext.getBean("ProductDao");
productDao.add();
}
八.spring中通知类型
1.前置通知
在目标方法之前执行
可以获得切入点的信息
<aop:aspect ref="MyAspect">
<aop:before method="checkPri" pointcut-ref="pointcut1"></aop:before>
</aop:aspect>
//切面类
public class MyAspect {
public void checkPri(JoinPoint joinPoint){
System.out.println("权限校验--------"+joinPoint);
}
}
输出结果如下:
权限校验--------execution(void org.westos.dao.ProductDao.add())
add..商品
2.后置通知
在目标方法之后执行
可以获得方法的返回值
注意:returning的值要和方法中Object result名一致
<!--后置通知-->
<aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result"></aop:after-returning>
public void writeLog(Object result){
System.out.println("日志.."+result);
}
输出结果如下:
delete..商品
日志..hahahaha
3.环绕通知
在目标方法之前和之后执行
<!--环绕通知-->
<aop:around method="around" pointcut-ref="pointcut3"></aop:around>
切面类
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕前通知");
Object obj = proceedingJoinPoint.proceed();//相当于执行目标程序
System.out.println("环绕后通知");
return obj;
}
输出结果如下:
环绕前通知
find..商品
环绕后通知
4.异常抛出通知
程序中出现异常时进行的操作
5.最终通知
无论方法是否有异常,最终都会被执行
6.引介通知
九.切入点表达式语法
基于execution函数完成的
语法:[访问修饰符] 方法返回值 包名.类名.方法名(参数)
public void org.qingyu.dao.UserDao.add(..)
* *.*.*.dao.add(..) 所有dao里面的add()都可以被增强
*org.qingyu.dao.UserDao+.add(..) 当前类及其子类里面的add()都可以被增强
*org.qingu.dao..*.*(..) 当前包下的所有类的所有方法都可以被增强