AOP
面向切面编程 ,利用AOP可以对业务的部分进行隔离,从而使得业务逻辑部分之间的耦合度降低,提高程序的可重复性。
一、AOP的底层原理
AOP的底层使用的是动态代理
(1). 有两种情况的动态代理
-
有接口情况
-
使用JDK的动态代理
public interface UserDao{ public int add(int a, int b); public String update(String id); }
public class UserDaoImpl implements UserDao{ public int add(int a, int b) { return a + b; } public String update(String id){ return id; } }
public class JdkProxy{ public static void main(String[] args){ Class[] interfaces = {UserDao.class}; UserDao userDao = new UserDaoImpl(); UserDao dao = Proxy.newProxyInstance(JdkProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao)); int res = dao.add(1, 2); System.out.println(res); } } class UserDaoProxy implements Proxy{ private Object obj; public UserDaoProxy(Object obj){ this.obj = obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{ System.out.println("方法执行之前"); Object invoke = method.invoke(obj, args); System.out.println("方法执行之后"); return invoke; } }
-
-
没有接口情况
- 使用cglib的动态代理
工厂模式创建
public class UserFactory{
public static UserDao getUserDao(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean1.xml");
User user = applicationContext.getBean("user",User.class);
// 通过反射获取到类对象
Class clazz = Class.forName(User.class);
// 通过类对象的一个方法获取该对象实例
return (UserDao)clazz.newInstance();
}
}
二、 AOP术语
-
连接点
类里面的哪些方法可以被增强,这些方法称为连接点
-
切入点
实际被真正增强的方法,这些方法被称为切入点
-
通知(增强)
实际增强的逻辑部分被称为通知
- 通知有多种类型
- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 最终通知
-
切面
把通知应用到切入点的过程就是切面
三、AOP操作
spring框架一般基于AspectJ
实现AOP操作
AspectJ: 并不是spring的组成部分,独立AOP框架,一般把AspectJ和spring框架一起使用,进行AOP操作
准备工作
引入aop的依赖spring-aspectj.jar
,aop-alliance.jar
,aspectjweaver.jar
,com.springsource.net.sf.cglib.jar
配置文件表头信息
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<!-- p命名空间使用 -->
xmlns:p="http://www.springframework.org/schema/p"
<!-- 扫描注解包使用 -->
xmlns:context="http://www.springframework.org/schema/context"
<!-- aop使用 -->
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
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
切入点表达式
作用:知道对哪个类里面的哪个方法进行增强
语法结构:execution([权限修饰符][返回值类型][方法名称]([参数列表]))
<!-- 对com.wyxz.dao.Book对add方法增强 -->
execution(*com.wyxz.dao.Book.add(..))
<!-- 对com.wyxz.dao.Book对所有方法增强 -->
execution(*com.wyxz.dao.Book.*(..))
<!-- 对com.wyxz.dao.Book对所有类所有方法增强 -->
execution(*com.wyxz.dao.*.*(..))
四、基于xml配置文件实现
-
创建两个类,一个增强类,一个被增强类
public class Book { public void buy() { System.out.println("买数~~~"); } }
public class BookProxy { public void before() { System.out.println("before~~~"); } }
-
在spring配置文件中,创建两个类对象进行配置
<bean id="Book" class="com.wyxz.config.Book"></bean> <bean id="bookProxy" class="com.wyxz.config.BookProxy"></bean>
-
在spring配置文件中,配置切入点
<!-- 配置aop增强 --> <aop:config> <!-- 这里的id为p表示的是要切的是哪个类,id的值可以自定义,expression中就是对这个类中的哪个方法进行增强 --> <aop:pointcut id="p" expression="execution(* com.wyxz.config.Book.buy(..))"/> <!-- 声明切入点是哪个类 --> <aop:aspect ref="bookProxy"> <!-- 配置切面 method:表示使用哪个方法 pointcut-ref : 切入点使用在哪个类的哪个方法上 --> <aop:before method="before" pointcut-ref="p"/> </aop:aspect> </aop:config>
五、基于注解方式实现(常用)
-
创建类,在类里面定义方法
@Component public class User{ public void add(){ System.out.println("add~~~"); } }
-
创建一个类(增强类), 让不同的类代表不同的增强
@Component @Aspect public class UserProxy { // 前置通知 @Before(value = "execution(* com.wyxz..annotation.User.add(..))") public void before() { System.out.println("before~~~"); } // 当增强的方法发生异常的时候执行 @AfterThrowing(value = "execution(* com.wyxz..annatation.User.add(..))") public void afterThrowsing() { System.out.println("after~~~"); } // 当增强方法执行之后执行 @After(value = "execution(* com.wyxz..annotation.User.add(..))") public void after() { System.out.println("after~~~"); } // 当增强方法返回之后执行 @AfterReturning(value = "execution(* com.wyxz.annotation.User.add(..))") public void afterReturning() { System.out.println("afterReturning~~~"); } // 在增强方法执行前后都会执行 @Around(value = "execution(* com.wyxz.annatation.User.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("around之前~~~"); proceedingJoinPoint.proceed(); System.out.println("around之后~~~"); } }
-
进行通知的配置
-
在spring配置文件中,开启注解扫描
<context:component-scan base-package="com.wyxz.annotation"></context:component-scan>
-
使用注解创建User和UserProxy对象
@Component // 在上面两个类上边加上这个注解
-
在增强类上面添加注解@AspectJ
// 在UserProxy中添加注解 @Aspect
-
在spring配置文件中开启生成代理对象
<!-- 开启注解扫描 --> <context:component-scan base-package="com.wyxz.annotation"></context:component-scan> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
-
使用
class
配置类的方式(不适用xml配置文件,完全配置类实现方式)@Configuration @ComponentScan(basePackages = "com.wyxz") @EnableAspectJAutoProxy(proxyTargetClass = true) public class Config { }
-
-
配置不同类型的通知
-
在增强类里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置
// 在UserProxy中的方法中添加如下内容 @Before(value = "execution(* com.wyxz..annotation.User.add(..))") @AfterThrowing(value = "execution(* com.wyxz..annatation.User.add(..))") @AfterReturning(value="execution(* com.wyxz.annatation.User.add(..))") @Around(value="execution(* com.wyxz.annatation.User.add(..))") @After(value="execution(* com.wyxz.annatation.User.add(..))")
-
-
将表达式的公式抽取使用
@PointCut(value="execution(* com.wyxz.annatation.User.)") public void point(){ } @Before(value = "point()") public void before() { System.out.println("before~~~"); }
-
设置增强类的优先级
//在增强类上加注解@Order()值越小优先级越高