1 概念
- 定义:面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高开发的效率
- 通俗描述:不通过修改源代码的方式,在主干功能中添加新功能
2 底层原理
1) 动态代理
- 有接口情况,使用JDK动态代理:
- 无接口情况,使用CGLIB代理
2) aop(jdk动态代理)代码
-
使用Proxy类,调用newProxyInstance方法
class Subject{ } Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler); /* loader: 类加载器 interface: 增强方法所在的类,这个类实现的接口,支持多个接口 hander: InvocationHandler接口,需要实现,创建代理对象,写增强的部分 */
-
具体代码
public interface UserDao { public int add(int a,int b); public void update(String id); } public class UserDaoImpl implements UserDao{ @Override public int add(int a, int b) { return a+b; } @Override public void update(String id) { System.out.println(id); } } public class JDKProxy { public static void main(String[] args) { Class[] interfaces = {UserDao.class}; // Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() { // @Override // public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // return null; // } // }) UserDao userDao = new UserDaoImpl(); UserDao o = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao)); int result = o.add(1,2); System.out.println(result); } } //创建代理对象代码 class UserDaoProxy implements InvocationHandler{ private Object obj; //将创建的代理对象传递进来 public UserDaoProxy(Object o){ obj = o; } public UserDaoProxy(){} //增强的逻辑 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before invoke :"+method.getName()); Object result = method.invoke(obj,args); System.out.println("after invoke "+method.getName()); return result; } }
3) AOP术语
-
连接点:
类里面哪些方法可以被增强.这些方法称为连接点
-
切入点
实际被真正增强的方法,称为切入点
-
通知(增强)
(1)时机增强的逻辑部分称为通知(增强)
(2)通知的类型
- 前置通知: 方法前
- 后置通知: 方法后
- 环绕通知: 前后都执行
- 异常通知: 异常时执行
- 最终通知: 类似finally
-
切面
把通知应用到切入点的过程,是一种动作
4) AOP(Spring)
-
概念:Spring框架一般基于AspectJ实现Aop操作,但不是Spring的组成部分,独立AOP框架,一般把二者一起使用,来进行AOP操作
-
可以通过xml或者注解方式实现(通常使用注解方式)
-
导入相关依赖
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring-aspects-5.2.6.RELEASE.jar
-
切入点表达式:知道对那个类里面的那个方法进行增强
// 模板:execution([权限修饰符][返回类型][类全路径][方法名称]([参数列表])) //例1:对com.atguigu.dap.BookDao的add方法增强 execution(*com.atguigu.dap.BookDao.add(..)) //*表示任意修饰符,类型可以省略 //例2:对com.atguigu.dap.BookDao的所有方法增强 execution(*com.atguigu.dap.BookDao.*(..)) //例3:对com.atguigu.dap的所有方法增强 execution(*com.atguigu.dap.*.*(..))
-
AspectJ注解
@Component public class User { public void add(){ System.out.println("User add"); } } @Component @Aspect //生成代理对象 public class UserProxy { @Before(value = "execution(* com.atguigu.spring5.aop.User.add(..))") public void before(){ System.out.println("before add"); } @After(value = "execution(* com.atguigu.spring5.aop.User.add(..))") public void after(){ System.out.println("after add"); } //环绕通知 @Around(value = "execution(* com.atguigu.spring5.aop.User.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("before around add"); proceedingJoinPoint.proceed(); System.out.println("before around add"); } //有异常时执行 @AfterThrowing(value = "execution(* com.atguigu.spring5.aop.User.add(..))") public void afterThrowing(){ System.out.println("wrong"); } } public class Demo1 { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); User user = context.getBean("user",User.class); user.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.atguigu.spring5.aop"></context:component-scan> <!--开启AspectJ生成代理对象--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
-
相同切入点抽取:
@Pointcut(value = "execution(* com.atguigu.spring5.aop.User.add(..))") public void point(){}//可以直接将value值替换为此函数名,即抽取相同切入点 @Before(value = "point()") public void before(){ System.out.println("before add"); }
-
多个代理类同时代理一个方法
@Component @Aspect //生成代理对象 @Order(1) public class StudentProxy { @Before(value = "execution(* com.atguigu.spring5.aop.User.add(..))") public void studentAdd(){ System.out.println("student 666"); } }
-
完全注解开发(AOP)
@Configuration//作为配置类,替代xml配置文件 @ComponentScan(basePackages = {"com.atguigu"}) @EnableAspectJAutoProxy(proxyTargetClass = true) public class SpringConfig { }