spring之AOP


前言

spring默认支持方法级别的连接点

一、AOP的作用:

在这里插入图片描述
进行权限校验、日志、和事务处理、性能监控、方法增强

二、为什么出现AOP:

1、aop面向切面编程,降低程序的耦合性,提高程序的复用性和开发效率。
2、通俗易懂的话就是不修改源代码,增强功能,可以理解为对相对应方法进行修饰。

三、AOP 的开发中的相关术语:

Aspect(切面): 将横切关注点逻辑进行模块封装的实体对象,封装了切点和通知代码的类。
Advice(通知/增强):切面的工作,给被增强的区域增强的逻辑称为通知。(前置通知、后置通知、环绕通知、返回通知、异常通知)
Joinpoint(连接点): 允许使用通知的地方
Pointcut(切入点):定义一系列规则对joinPoint进行筛选,实际使用通知的地方。
总结关系:apsect是装满导弹的巡洋舰,advice是要发射的导弹,joinPoint是可能打击的地点,
PointCut是实际打击的地方。
Target(目标对象):符合pointcut条件,要被横切逻辑织入的对象,代理的目标对象	
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程.
织入器:完成织入过程的执行这,例如ajc

Introduction :引入型advice
1、为目标类引入新方法或属性,不用目标类做任何实现和继承。
2、使得目标类在使用过程中转型成新接口对象,调用新接口方法。

引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类
动态地添加一些方法或 Field.	
含有
规则筛选
织入
织入
织入
织入
织入
目标类target
连接点Joinpoint
3 切点pointcut
切面类apect
切点表达式pointcut
2 beforeAdvice前置通知
1 AroundAdice环绕通知
6 AfterThrowingAdvice异常通知
7 AfterReturningAdvice返回通知
5 AfterAdvice后置通知

在这里插入图片描述

2、织入的时期

  • 编译期:切面在目标类编译时期织入,需要特殊的编译期,AspectJ的织入编译器就是这种方式。
  • 类加载器:切面在目标类加载到JVM时织入,需要特殊的类加载器,可以将目标类的字节码增强。Aspect5的加载时织入。
  • 运行期:一般情况下,织入切面时,aop容器会为目标类动态创建一个代理对象,spring aop就是这种方式。

四、切面类:

切入点表达式
(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强
(2)语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )
举例 1:对 com.zixue.Test.Logo 类里面的 add 进行增强
	execution(* com.zixue.Test.Logo.test(..))
举例 2:对 com.zixue.Test.Logo 类里面的所有的方法进行增强
	execution(* com.zixue.Test.Logo.* (..))
举例 3:对 com.zixue.dao 包及子包所有类,类里面所有方法进行增强
	execution(* com.zixue.dao..*.* (..))

1、注解实现增强:

①被增强的类

@Component
public class Logo {
    public void test(){
        System.out.println("测试。。。test");
    }
}

②切面类:

@Order(1)//在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高
@Aspect //切面类,生成代理对象
@Component
public class MyAspect {
    @Pointcut("execution(* com.zixue.Test.Logo.test(..))")//定义切入点的注解
    private void pointcut() {
    }

    @Before(value = "MyAspect.pointcut()")//前置通知
    public void before() {
        System.out.println("before===========");
    }
    
	//后置通知,出现异常则不执行如下代码
    @AfterReturning(value = "MyAspect.pointcut()",returning = "result")
    public void afterReturning(Object result) {
        System.out.println("afterReturning==========="+result);
    }

    @Around("MyAspect.pointcut()")//环绕通知
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("around==========");
        Object obj = joinPoint.proceed();
        System.out.println("around==========");
        return obj;
    }

    @AfterThrowing(value = "MyAspect.pointcut()",throwing = "exception")//异常抛出通知.
    public void afterThrowing(Exception exception) {
        System.out.println("afterThrowing========"+exception);
    }

    @After(value = "MyAspect.pointcut()") //最终通知,无论异常与否,都将运行
    public void after(JoinPoint joinPoint) {
        System.out.println("after=========="+joinPoint.getSignature().getName());
    }
}

③配置类:

//可以替代application.xml中<aop:aspectj-autoproxy/>配置aop注解
@EnableAspectJAutoProxy //<!-- 开启 Aspect 生成代理对象-->
@Configuration //配置类 = 配置文件
public class MainConfig {

    @Bean("persion") //将bean注入ioc容器,类型为返回值类型,id默认为方法名
    public Person person(){
        return new Person("li",20);
    }

④测试:

public class AopDemo {
    public static void main(String[] args) {
        ApplicationContext applicationContext =  new AnnotationConfigApplicationContext(MainConfig.class);
        Logo log = applicationContext.getBean(Logo.class);
        log.test();
    }
}

结果:

around==========
before===========
测试。。。test
around==========
after==========test
afterReturning===========2

2、AOP 的xml配置:

<!-- 配置切面类 -->
<bean id="myAspect" class="cn.zixue.MyAspect"></bean>
<!-- 进行 aop 的配置 -->
<aop:config>
<!-- 配置切入点表达式:哪些类的哪些方法需要进行增强 -->
	<aop:pointcut expression="execution(*cn.zixue.*Dao.save(..))" id="pointcut1"/>
	<aop:pointcut expression="execution(*cn.zixue.*Dao.delete(..))" id="pointcut2"/>
	<aop:pointcut expression="execution(*cn.zixue.*Dao.update(..))" id="pointcut3"/>
	<aop:pointcut expression="execution(*cn.zixue.*Dao.find(..))" id="pointcut4"/>
	<!-- 配置切面 -->
	<aop:aspect ref="myAspect">
		<aop:before method="before" 	pointcut-ref="pointcut1"/>
		<aop:after-returning method="afterReturing" 	pointcut-ref="pointcut2"/>
		<aop:around method="around" 	pointcut-ref="pointcut3"/>
		<aop:after-throwing method="afterThrowing" 	pointcut-ref="pointcut4"/>
		<aop:after method="after" 	pointcut-ref="pointcut4"/>
	</aop:aspect>
</aop:config>

总结:
1)、将业务逻辑组件和切面类都加入到容器中;告诉Spring哪个是切面类(@Aspect)
2)、在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式)
3)、开启基于注解的aop模式;@EnableAspectJAutoProxy

五、aop的原理:动态代理

动态代理机制:
1、通过包名的类获对应class二进制字节流
2、根据读取的字节流,将代表的静态存储结构转换成运行时数据结构
3、生成该类的class对象,作为该类在方法区的数据访问入口
4、根据一定规则去改动或生成新的字节流,将切面逻辑织入其中
5、根据目标类或者接口,计算出代理类的字节码并加载到jvm中

spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装在期织入
Proxy(代理) :一个类被 AOP 织入增强后,就产生一个结果代理类	
Spring 的 AOP 的底层用到两种代理机制:(运行时动态生成字节码,并加载到JVM,没有实际class文件)
	* JDK 的动态代理 :针对实现了接口的类产生代理,最终生成的代理类和目标类实现相同的接口
       在com.sun.proxy包下,类名为$proxy2。
	* Cglib 的动态代理 :针对没有实现接口的类产生代理. 应用的是底层的字节码增强的技术 生成当前类
的子类对象,它及支持接口也支持没有接口的类,最终生成的代理类会继承目标类,并且和目标类在相同的包下。

在这里插入图片描述

1、jdk接口代理

//和aspect一样,包装横切逻辑
public class MyJDKProxy implements InvocationHandler {
	//目标类对象
    private UserDao userDao;
    public MyJDKProxy(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override//proxy方便获取到代理类对象
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if("add".equals(method.getName())){
            System.out.println("加方法增强============proxy:"+ proxy + " userDao: " +userDao);
        }//调用目标类对象的方法
         return method.invoke(userDao,args);
    }
}

 public static void main(String[] args) {
      	// 编写工具方法:生成代理:
        UserDao userDao = new UserDaoImpl();
        UserDao userDaoProxy = (UserDao)Proxy.newProxyInstance(userDao.getClass().getClassLoader(),
                userDao.getClass().getInterfaces(), new MyJDKProxy(userDao));
        userDaoProxy.add();
 
    }  
//控制台输出:====================================================================   
加方法增强============proxy:com.atguigu.spring5.dao.UserDaoImpl@ea30797 
userDao: com.atguigu.spring5.dao.UserDaoImpl@ea30797
dao add.....  

实现了InvocationHandler接口的代理类,调用方法是会调用invoke方法从而实现增强。

2、cglib代理:子类增强

cglib是代码生成库:code generation library
1、目标类可是实现接口,或者没有实现接口的类
2、内部主要封装了ASM java字节码操控框架
3、通过生成目标类(static、final修饰的类)的子类,覆盖非final的方法,绑定勾子回调自定义拦截器
4、Enhancer创建一个被代理对象的子类并且拦截所有的方法调用(包括从Object中继承的toString和
hashCode方法)。Enhancer不能够拦截final方法,例如Object.getClass()方法,这是由于Java final
方法语义决定的。
1.2.5.2 Cglib 动态代理增强一个类中的方法:
public class MyCglibProxy implements MethodInterceptor{
	private CustomerDao customerDao;
	public MyCglibProxy(CustomerDao customerDao){
		this.customerDao = customerDao;
	}
	// 生成代理的方法:
	public CustomerDao createProxy(){
		// 创建 Cglib 的核心类:
		Enhancer enhancer = new Enhancer();
		// 设置父类型:
		enhancer.setSuperclass(CustomerDao.class);
		// 设置回调:
		enhancer.setCallback(this);
		// 生成代理 
		CustomerDao customerDaoProxy = (CustomerDao) enhancer.create();
		return customerDaoProxy;
	}
		@Override
	public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
		if("delete".equals(method.getName())){
		Object obj = methodProxy.invokeSuper(proxy, args);
		System.out.println("日志记录================");
		return obj;
		}
		return methodProxy.invokeSuper(proxy, args);
	}
}

CGLIB详解(最详细)
aop细说

六、静态代理

代理对象实现目标类接口,并持有目标类对象,在目标类方法前后添加操作

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值