了解更多关于Spring的基础知识
1、什么是AOP
2、AOP底层原理
AOP底层使用动态代理
第一种有接口情况,使用JDK动态代理创建接口实现类代理对象,增强类的方法(下面会介绍这种方式)
第二种没有接口情况,使用CGLIB动态代理创建子类的代理对象,增强类里面的方法
1、使用JDK动态代理,需要使用proxy类里面的方法创建代理对象
方法里面有三个参数:
- classLoder:类加载器
- 增强的方法所在的类所实现的接口的集合,支持多个接口共同传入
- 实现接口InvocationHandler的类,这个参数传入的就是代理对象,在这个类中写入增强的方法
2、编写JDK动态代理代码
public interface UserDao {
public int add(int a,int b);
}
public class UserDaoIml implements UserDao{
@Override
public int add(int a, int b) {
System.out.println("add方法执行了");
return a+b;
}
}
class UserDaoProxy implements InvocationHandler{
//把创建的是谁的代理对象,把谁传递过来
//有参构造传递
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("方法之前执行"+method.getName()+":传递的参数..."+ Arrays.toString(args));
//被增强的方法执行
Object res = method.invoke(obj,args);
//方法之后
System.out.println("方法之后执行..."+obj);
return res;
}
}
public class JDKProxy {
public static void main(String[] args) {
//用于存储接口的数组,用于newProxyInstance函数的第二个参数
Class[] interfaces = {UserDao.class};
//将被增强的方法所在类作为参数传入InvocationHandler接口实现类
UserDaoIml userDaoIml = new UserDaoIml();
UserDao userDao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(),interfaces,new UserDaoProxy(userDaoIml));
System.out.println(userDao.add(1,2));
}
}
方法之前执行add:传递的参数...[1, 2]
add方法执行了
方法之后执行...spring.UserDaoIml@179d3b25
执行结果为:3
通知的类型:
- 前置通知:在需要增强的方法前执行——@Before
- 后置通知:不管什么情况都会执行,即使有异常也会执行——@After
- 环绕通知:在需要增强的方法前后都要执行——@Around
- 异常通知:在需要增强的方法出现异常时执行——@AfterThrowing
- 返回通知:在需要增强的方法后执行——@AfterReturning
1、AspectJ介绍
Spring框架一般都是基于AspectJ实现的AOP操作
AspectJ不是Spring组成部分,这是一个独立的AOP框架,一般把AspectJ和Spring框架一起使用来实现AOP操作
基于AspectJ的实现AOP操作主要有两种方式:
2、切入点表达式
@Before(value = "切入点表达式放入的位置")
(1)切点表达式的作用:指明对某个类里面的某个方法进行增强
(2)语法结构:execution([权限修饰符][返回类型][类全路径][方法名称][参数列表])
execution(*spring.Userdao.add(..))
execution(*spring.Userdao.*(..))
execution(public double spring.Userdao.*(..))
execution (* *.add(int,..)) || execution(* *.sub(int,..))
1、代码演示
在项目工程里面引入AOP相关的依赖
AspectJ:aspectjweaver下载地址:
https://www.eclipse.org/downloads/download.php?file=/tools/aspectj/aspectj-1.9.6.jar
aopalliance.jar下载地址:
https://sourceforge.net/projects/aopalliance/files/aopalliance/1.0/aopalliance.zip/download
cglib-2.2.0.jar下载地址:
http://www.xwood.net/site_domain/_root/5870/5930/5932/9330/9352/t_c41554.html
创建一个User类,在User类中创建方法
@Component (value = "user")
public class User {
public void add(){
System.out.println("add......");
}
}
环绕通知:
- 对于环绕通知来说,连接点的参数类型必须是ProceedingJoinPoint。它是 JoinPoint的子接口,允许控制何时执行,是否执行连接点。
- 在环绕通知中需要明确调用ProceedingJoinPoint的proceed()方法来执行被代理的方法。如果忘记这样做就会导致通知被执行了,但目标方法没有被执行。
- 环绕通知的方法需要返回目标方法执行之后的结果,即调用 joinPoint.proceed();的返回值,否则会出现空指针异常。
@Component
@Aspect
public class Userproxy {
@Before(value = "execution(* Aop.User.add(..))")
public void before(){
System.out.println("@Before 前置通知");
}
@After(value = "execution(* Aop.User.add(..))")
public void after(){
System.out.println("@After 后置通知");
}
@AfterThrowing(value = "execution(* Aop.User.add(..))")
public void afterThrowing(){
System.out.println("@AfterThrowing 异常通知");
}
@Around(value = "execution(* Aop.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("@Around 环绕通知前");
proceedingJoinPoint.proceed();
System.out.println("@Around 环绕通知后");
}
@AfterReturning(value = "execution(* Aop.User.add(..))")
public void afterreturning(){
System.out.println("@AfterReturning 返回后通知");
}
}
<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="Aop">context:component-scan>
<aop:aspectj-autoproxy>aop:aspectj-autoproxy>
beans>
public class Test_AOP {
@Test
public void testAop(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
User user = context.getBean("user",User.class);
user.add();
}
}
@Around 环绕通知前
@Before 前置通知
add......
@AfterReturning 返回后通知
@After 后置通知
@Around 环绕通知后
2、基于AspectJ的进阶操作
@Pointcut(value = "execution(* Aop.User.add(..))")
public void pointdemo(){
}
@Before(value = "pointdemo()")
public void before(){
System.out.println("before.....");
}
@Component
@Aspect
@Order(1)
public class personProxy {
@Before(value = "execution(* Aop.User.add(..))")
public void before(){
System.out.println("person Before..........");
}
}
@Component
@Aspect
@Order(2)
public class UserProxy {
@Before(value = "execution(* Aop.User.add(..))")
public void before(){
System.out.println("User Before..........");
}
}
@Configuration:表明这是一个配置类
@ComponentScan(basePackages = {“Aop”}):表示扫描包的路径为:Aop
@EnableAspectJAutoProxy(proxyTargetClass = true):表示开启AOP自动代理
@Configuration
@ComponentScan(basePackages = {"Aop"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class configAOP {
}
@Test
public void testAop1(){
ApplicationContext context = new AnnotationConfigApplicationContext(configAOP.class);
User user = context.getBean("user", User.class);
user.add();
}
❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤
若对Spring基础知识感兴趣的可以关注一下博主,我会持续更新Spring基础知识(一边学习一边记录),一起进步,有错误的地方也可以在评论区指出来喔,谢谢大家啦!!!