什么是AOP
AOP意思是面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,降低各部分的耦合度,提高程序的可重用性,提高开发效率;
通俗来说,AOP指的是不改变源代码,在原有的基础之上可以加新的功能,这过程就叫AOP;
AOP底层原理和实现方式
有接口情况下,通过JDK动态代理
创建接口实现类的代理对象,增强类的方法,使用Proxy类里面的方法创建代理对象,使用newProxyInstance方法返回指定接口的实例,演示如下;
newProxyInstance(ClassLoader loader,类<?>[] interfaces ,InvocationHandler h)
ClassLoader loader : 类加载器;
类<?>[] interfaces : 增强方法所在的类,这个类实现的接口,支持多个接口;
InvocationHandler h : 需要实现这个接口,创建代理对象,写增强方法
public interface UserDao {
public int add(int a,int b);
public String update(String id);
}
public class UserDaoImpl implements UserDao {
@Override
public int add(int a,int b) {
return a + b;
}
@Override
public String update(String id) {
return id;
}
}
public class JDKProxy {
public static void main(String[] args) {
//创建接口实现类代理对象
Class[] interfaces = {UserDao.class};
UserDaoImpl userDao = new UserDaoImpl();
UserDao dao =(UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
int result = dao.add(1, 2);
System.out.println(result);
}
}
//创建代理对象
class UserDaoProxy implements InvocationHandler{
//创建的是谁的代理对象就得把谁传递过来
//有参构造传递
private Object obj;
public UserDaoProxy(Object obj){
this.obj = obj;
}
/*增强逻辑写在invoke中*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法之前执行的代码
System.out.println("方法之前执行...");
//被执行的方法也得执行
Object res = method.invoke(obj, args);
//方法之后执行的代码
System.out.println("方法之后执行 ...");
return res;
}
}
没有接口情况下,通过CGLIB动态代理
创建当前类的子类的代理对象,增强类的方法;
AOP术语
- 连接点
在一个类中,哪些方法可以被增强,这些方法就叫连接点;
- 切入点
实际真正被增强的方法,被称为切入点;
- 通知(增强)
实际增强的代码逻辑部分,就是通知;
通知有多种类型:
前置通知:被增强的方法前执行;
后置通知:被增强的方法后执行;
环绕通知:被增强的方法前后都执行;
异常通知:被增强的方法出现异常会执行;
最终通知:类似于finally,永远会执行;
- 切面
是动作上的操作,把通知应用到切入点的过程,就叫切面;
AspectJ相关
AspectJ本身是一个单独的框架,它不是Spring框架的组成部分,但一般会把它和Spring一起使用,进行AOP操作;
实现方式有两种,一种基于配置文件,一种基于注解;
切入点表达式
execution(权限修饰符 返回类型 类全路径 方法名称 (参数列表))
举例:对com.example.dao.UserDao的add方法进行增强;
execution(* com.example.dao.UserDao.add(. .))
*表示所有权限修饰符
返回类型可以省略
参数列表可以用(. .)表示
案例
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启组件扫描-->
<context:component-scan base-package="com.demo"/>
<!--寻找有@Aspect注解的类,并生成代理对象-->
<aop:aspectj-autoproxy/>
</beans>
@Component
public class User {
public void add(){
System.out.println("被增强的类执行了add ...");
}
}
//增强类,用来增强User类
@Component
@Aspect //生成代理对象
public class UserProxy {
//前置通知
//加上注解和切入点表达式
@Before("execution(* com.demo.domain.User.add(..))")
public void before(){
System.out.println("前置执行 ..");
}
//后置通知
@After("execution(* com.demo.domain.User.add(..))")
public void after(){
System.out.println("后置执行 ..");
}
//环绕通知
@Around("execution(* com.demo.domain.User.add(..))")
public void around(ProceedingJoinPoint point) throws Throwable {
System.out.println("环绕前通知 ..");
//表示执行被增强的方法
point.proceed();
System.out.println("环绕后通知 ..");
}
//异常通知
@AfterThrowing("execution(* com.demo.domain.User.add(..))")
public void afterThrowing(){
System.out.println("异常通知 ..");
}
//最终通知
@AfterReturning("execution(* com.demo.domain.User.add(..))")
public void afterReturning(){
System.out.println("最终通知 ..");
}
}
//测试类
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
User user = context.getBean("user", User.class);
user.add();
}
相同切入点表达式抽取
上面的案例中,切入点表达式都是相同的,现在对相同切入点表达式进行抽取;
//抽取相同切入点表达式
@Pointcut("execution(* com.demo.domain.User.add(..))")
public void pointExpression(){}
@Before("pointExpression()")
public void before(){
System.out.println("前置执行 ..");
}
多个增强类情况下
多个增强类对同一个方法进行增强,可以设置他们的优先级;
使用@Order注解,再给一个值,值越小,优先级越高;
@Component
@Aspect
@Order(1) //设置优先级,值越小,优先级越高
public class AnotherPoxy {
@Before("execution(* com.demo.domain.User.add(..))")
public void before(){
System.out.println("AnotherPoxy前置执行 ..");
}
}
@Component
@Aspect
@Order(2)
public class UserProxy {
@Before("execution(* com.demo.domain.User.add(..))")
public void before(){
System.out.println("UserProxy前置执行 ..");
}
}
public class TestDemo {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
User user = context.getBean("user", User.class);
user.add();
}
}