IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> SPRING-了解4-AOP1 -> 正文阅读

[Java知识库]SPRING-了解4-AOP1

主要理念:不用继承,而是用Java Reflect类中的Proxy来实现对方法的增强

面向切面编程? ?oop的延续,对业务进行隔离,降低耦合度,目标是不改变原来的代码在主干中增加新功能

比如已经写好登录功能,现在需要加入权限,那么应该额外写权限模块,之后配置即可

AOP底层原理:

? ? 底层使用动态代理,以下两者差别在于一个接口实现类代理,一个子类实现代理

? ? (1)有接口情况:使用JDK动态代理

? ? ? ? ? ?比如有UserDao 接口? 以及UserDaoImpl实现类

? ? ? ? ? ? ??JDK动态代理就是UserDao 接口实现类代理对象,不通过new的方法,以增强类的方法

? ? ?(2)无接口情况:使用CGLIB动态代理

? ? ? ? ? ?目标是不改变以下类的add实现方法

public class User{
public void add(){
}
}

原始方法:可以创建User类的子类

新方法:创建User类的子类代理对象,不用new方法

实例

? 使用JDK动态代理

? ? ?使用Proxy里面的方法创建代理对象

? ? 查阅文档:Java 8 中文版 - 在线API中文手册 - 码工具

java.lang.reflect --》Class Proxy

?其中有方法

newProxyInstance(ClassLoader?loader,?<?>[]?interfaces,?InvocationHandler?h)

?newProxyInstance(ClassLoader?loader,?<?>[]?interfaces,?InvocationHandler?h)

注意其返回指定接口代理了的实例,静态的方法,可以调用

第一个参数:类加载器??ClassLoader?loader

第二个参数:数组,增强方法所在的类,这个类实现的接口,可为多个

第三个参数:实现这个接口InvocationHandler?,创建代理对象? 写增强的方法

底层原理代码实现

1)创建接口与实现类

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) {
        System.out.println("add方法执行了");
        return a+b;
    }

    @Override
    public String update(String id) {
        System.out.println("update方法执行了");
        return  id;

    }
}

1)创建JDKProxy类,名字任意

package com.i7i8i9.spring5;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class JDKProxy {
    public static void main(String[] args) {
        Class[] interfaces={UserDao.class};//为了给Proxy.newProxyInstance提供第二个参数
        //创建接口实现类代理对象
        UserDaoImpl userDao=new UserDaoImpl(); //这个是为了Proxy.newProxyInstance第三个参数设计的类构造方法
        UserDao dao=(UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(),interfaces,new UserDaoProxy(userDao));
        int sum=dao.add(3,5);
        System.out.println("求和"+sum);
    }
}
//创建代理对象
class UserDaoProxy implements InvocationHandler {
    //创建谁的代理对象,就要把谁传进来,多种方式
    //有参构造  Object obj可以用UserDaoImpl,但是范围会缩小
    private Object obj;
    public UserDaoProxy(Object obj){
      this.obj=obj;

    }

    //增强的逻辑
    @Override
    //invoke方法意思是对象一创建,方法就会被调用
    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;
    }
}

main中? UserDao dao=(UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(),interfaces,new UserDaoProxy(userDao)); 相当于 new 对象

这里面分为两个类,第一个是JDKProxy,命名任意,里面包含main方法

主要是围绕Proxy.newProxyInstance三个参数进行

针对第二个参数定义了一个集合:Class[] interfaces={UserDao.class};

针对第三个参数创建了一个类?class UserDaoProxy implements InvocationHandler

2)class UserDaoProxy

? ?在这个类里面第一个要求是:创建谁的代理对象,就要把谁传进来,多种方式实现

这里使用了构造方法传值,为了增强通用性,使用了Object

  private Object obj;
    public UserDaoProxy(Object obj){
      this.obj=obj;

    }

第二部分写了增强逻辑

也就是在被增强方法执行前后加入了新逻辑

注意被增强方法执行处其实可以加一个判断

根据method.getName()进行判断

 //增强的逻辑
    @Override
    //invoke方法意思是对象一创建,方法就会被调用
    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;
    }

3)执行过程

先看执行结果

? ?

? ? ? a)第一步 准备阶段:? ?传入三个参数

? ? 重点是? ? UserDao dao=(UserDao) Proxy.newProxyInstance(三个参数)

? ? ? 因为Proxy类中创建了UserDaoImpl userDao并把它传给了第三个参数,即

new UserDaoProxy(userDao) 

? 第三个参数返回了

因为是object,所以在UserDao dao=(UserDao)对其进行强转

b)第二步: 调用方法

int sum=dao.add(3,5);

这个触发了被增强方法执行,并返回了Object?对象 com.i7i8i9.spring5.UserDaoImpl@31cefde0

?
方法之前执行add传递的参数[3, 5]
add方法执行了
方法之后,com.i7i8i9.spring5.UserDaoImpl@31cefde0
求和8

?
//被增强方法执行
Object res=method.invoke(obj,args);

invoke执行完毕之后,再执行:

System.out.println("求和"+sum);

另外如果main中执行的是userdaoImpl另外一个方法,那么也会被增强,也就是说这个实现类中所有的方法都被增强了,当如不想增强,可以再invoke实现类中用if+method.getName获取当前是什么方法进入执行操作

AOP术语

1.连接点:

? ? ?从上面可以看出,假设一个实现类中有4个方法, 那么这四个方法都被增强了,他们就是连接点

2.切入点:实际被真正增强的方法

3.通知(增强):实际增强的逻辑部分,就是上文中的权限判断

? ?通知有多种类型

1)前置通知

2)后置通知

3)环绕通知:前后都执行

4)异常通知

5)最终通知:类似try catch 中的finally,无论有没有异常都会执行

切面:是个动作,把通知应用到切入点的过程

AOP操作(准备)

1.Spring框架一般是基于AspectJ实现AOP操作

? ?AspectJ不是Spring组成部分,是一个单独框架,不依赖Spring

一般把AspectJ和Spring结合起来做AOP操作

? 两种实现方式

? ?1)基于xml配置文件

? ?2)基于注解方式实现

2.引入AOP操作相关依赖

? 1) spring-framework中的spring-aspects-5.3.9.jar

还有其他三个

2)?sf.cglib.jar

? ? ??https://mvnrepository.com/artifact/net.sourceforge.cglib/com.springsource.net.sf.cglib/2.2.0

点击files下载,出不来的话换一个网址

Download com.springsource.net.sf.cglib-2.2.0.jar : com.springsource.net???c???Jar File Download

3)aopalliance-1.0

第一个网址比较容易查,但有时候不是最新的

aopalliance-1.0.jar下载及Maven、Gradle引入代码,pom文件及包内class -时代Java

或者

https://mvnrepository.com/artifact/aopalliance/aopalliance/1.0

4)Aspectj

https://mvnrepository.com/artifact/org.aspectj/aspectjweaver/1.9.9.1

切入点表达式

作用:知道对哪个类的哪个方法进行增强

语法结构:execution([权限修饰符][返回类型][类全路径][方法名称]([参数列表]))

举例1: 对 com.i7i8i9.dao.BookDao里面的add方法进行增强

? ? ? ? ? execution(* com.i7i8i9.dao.BookDao.add(..))注意*代表public private都可以? ?两个点代表方法参数

举例2: 对 com.i7i8i9.dao.BookDao里面的所有方法进行增强

? ? ? ? ? execution(* com.i7i8i9.dao.BookDao.*(..))注意第二个*代表所有方法

举例3: 对 com.i7i8i9.dao包里面的所有类的所有方法进行增强

? ? ? ? ? execution(* com.i7i8i9.dao.*.*(..))注意第二个*代表所有类

编写代码

1.建立类:User

2.创建增强类:UserProxy

3.通知配置5步

? 1)开启注解扫描

? ? ? 比如使用src下xml

?类似bean,引入conte和aop命名空间:各插入一行xmlns? ?增加两个http(修改其中3处名字)

<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.i7i8i9.spring5.aopanno"></context:component-scan>

? 2)使用注解创建被增强和增强类的对象创建

@Component
public class User {
    public void add(){
        System.out.println("add.......");
    }
}
@Component
public class UserProxy {
    public void before(){
        System.out.println("before----");
    }
}

3)在增强类上增加注解 @Aspect,表示要生成代理对象

@Component
@Aspect
public class UserProxy {
    public void before(){
        System.out.println("before----");
    }
}

4)在Spring配置文件中开启生成代理对象,意思就是开启这个之后就去找@Aspect,找到就生成代理对象

<?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.i7i8i9.spring5.aopanno"></context:component-scan>
    <!--开启Aspect生成代理对象-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

5)配置通知

? 在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式

通知类型注解有5种Before After??AfterReturning? ??AfterReturning? ? Around

package com.i7i8i9.spring5.aopanno;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class UserProxy {
//@Before是Aspect注解
    @Before(value = "execution(* com.i7i8i9.spring5.aopanno.User.add(..))")
    public void before(){
        System.out.println("before----");
    }
    //方法执行之后,也是finally无论有没有异常都会执行
    @After(value = "execution(* com.i7i8i9.spring5.aopanno.User.add(..))")
    public void after(){
        System.out.println("after----");
    }
   //在返回值之后就执行 有异常就不执行
    @AfterReturning(value = "execution(* com.i7i8i9.spring5.aopanno.User.add(..))")
    public void afterReturn(){
        System.out.println("afterReturn----");
    }


    @AfterThrowing(value = "execution(* com.i7i8i9.spring5.aopanno.User.add(..))")
    public void afterThrow(){
        System.out.println("异常通知----");
    }


    @Around(value = "execution(* com.i7i8i9.spring5.aopanno.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        System.out.println("环绕之前----");
        proceedingJoinPoint.proceed();//用于区分执行前后,有异常抛出Throwable
        System.out.println("环绕之后----");//有异常就不执行
    }
}

测试

public class TestUserAop {

    @Test
    public void userAopTest(){
        ApplicationContext context=new ClassPathXmlApplicationContext("bean1.xml");
        User user=context.getBean("user", User.class);
        user.add();
        
    }
}

没有异常结果

环绕之前----
before----
add.......
afterReturn----
after----
环绕之后----

? ?制造一个异常

在User中增加:int i=10/0;

package com.i7i8i9.spring5.aopanno;

import org.springframework.stereotype.Component;

@Component
public class User {
    public void add(){
        int i=10/0;
        System.out.println("add.......");
    }
}

测试结果

环绕之前----
before----
异常通知----
after----

java.lang.ArithmeticException: / by zero

相同切入点提取

新建一个方法加@Pointcut

其他方法value=该方法

@Pointcut(value = "execution(* com.i7i8i9.spring5.aopanno.User.add(..))")

public void pointDemo(){ }



//@Before是Aspect注解 @Before(value = "pointDemo()")
public void before(){
        System.out.println("before----");
    }

增强优先级

多个类都对一个方法增强,可以在增强类上加@Order(数字类型值),越小优先级越高

似乎没有太大效果

@Order(13)
public class PersonProxy {
    @Before(value = "execution(* com.i7i8i9.spring5.aopanno.User.add(..))")
    public void afterReturn(){
        System.out.println("另外一个增强----");
    }
}



@Order(2)
public class UserProxy {

AspectJ XML方式--很少使用

也是分为三段:创建对象? 创建切入点? 配置切面

<?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
">
<!--创建对象-->
    <bean id="book" class="com.i7i8i9.spring5.aopxml.Book"></bean>
    <bean id="bookProxy" class="com.i7i8i9.spring5.aopxml.BookProxy"></bean>
<!--    切入点配置-->
    <aop:config>
<!--        切入点 p1名字随便起-->
        <aop:pointcut id="p1" expression="execution(* com.i7i8i9.spring5.aopxml.Book.buy(..))"/>
<!--        配置切面-->
    <aop:aspect ref="bookProxy">
<!--        配置增强在具体方法上-->
        <aop:before method="before" pointcut-ref="p1" />
    </aop:aspect>
    </aop:config>
</beans>

全注解方式

即不用xml

创建一个配置类

?

@Configuration
@ComponentScan(basePackages = {"com.i7i8i9"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop {
}

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-12-25 10:52:39  更:2022-12-25 10:56:36 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/22 18:37:26-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码