Spring

本文详细介绍了Spring AOP的概念、实现原理和核心概念,如连接点、切入点、通知、切面等。通过XML配置展示了AOP在日志记录中的应用,并探讨了AspectJ框架在AOP开发中的作用。此外,还讲解了注解方式开发AOP的步骤,包括不同类型的通知和切点表达式的使用。

Spring
一. AOP
AOP(Aspect Oriented Programing)面向切面编程,一种软件工程的编程范式 .

OOP(Object Oriented Programing)面向对象编程:制作程序时,设计模型,运行时,靠对象运行,是一种软工的编程范式

AOP联盟:一个组织,致力于AOP的发展研究 ( aop各种规则的制定者 ) .

AOP经典应用:性能监视、事务管理、安全检查(权限管理)、缓存等

Spring AOP指的是spring提供了对对象进行aop编程的支持 .
Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码

AspectJ是一个基于Java语言的AOP框架,Spring2.0开始,Spring AOP引入对Aspect的支持,A spectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入

AOP思想:AOP关注的是程序中的共性功能,开发时,将共性功能抽取出来制作成独立的功能模块,此时原始功能中将不具有这些被抽取出的共性功能代码。在这些具有被抽取的共性功能的模块运行时候,将共性功能,模块进行运行,即可完成原始的功能。

优点:加强代码的复用性,同时程序开发时可以只考虑个性化的功能,不需要考虑共性功能

AOP实现原理 (动态代理和cglib代理的混合使用)

动态代理 : ( 优先使用 )
被代理对象必须要实现接口 , 才能产生代理对象 , 如果没有接口 , 将不能使用动态代理技术

Cglib代理 : (没有接口的时候使用)
可以对任何没有被final修饰的类生产代理对象 , 代理的原理是对目标了进行继承代理 , 如果该类被final修饰 , 将无法被cglib代理 .

二.AOP中的基本概念
连接点(Joinpoint):具有特定功能的方法,一般方法(类中的方法)
切入点(Pointcut):具有共性功能的方法的统称一种称呼方式
目标对象(Target Object):包含切入点的类
通知(Advice):将共性功能抽取走,制作成独立的功能模块
切面(Aspect):切入点与通知匹配的一种情况
AOP代理(AOP Proxy):运行过程使用AOP创建代理对象进行运行,运行过程中将抽取的功能执行该过程由AOP自动完成,所以称为AOP代理
织入(Weaving):称将抽取的功能加入原始功能运行的整个过程叫做织入(动态)织入控制的是字节码
工作流程:
开发时,制作功能类(目标对象),将其中的方法中的通用功能(通知)抽取出来,制作成独立的类(通知类),原始目标对象中的方法(切入点)不再进行通用功能的制作。该功能被抽取后,无法完成完整的业务逻辑,需要在运行时将通知加入到对应的位置执行。为了完成此操作,必须将切入点与通知进行一对一的对应关系设定(切面)。
运行流程:
运行时,Spring一直监控切面中所配置的切入点对应的方法的执行,发现执行了该方法,使用AOP的代理机制,创建代理对象(AOP代理),将原始方法(切入点)与通知进行融合,形成一个完整的业务逻辑进行运行,此过程称为织入。

三.XML开发AOP
需求:
在添加用户之前执行打印日志功能 ;
开发步骤:
1.添加依赖jar包

	<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-aspects</artifactId>
		<version>4.3.10.RELEASE</version>
	</dependency>

2.制作目标对象

package cn.bgs.service;
/**

  • 目标对象接口
  • @author Mr.Wang

*/
public interface UserService {

void add();
void delete();
void update();
void select();

}

package cn.bgs.service;
/**

  • 目标对象
  • @author Mr.Wang

*/
public class UserServiceImpl implements UserService{

/**
 * 连接点(可以被增强的方法)
 */
public void add() {
	System.out.println("添加用户");
}

/**
 * 连接点(可以被增强的方法)
 */
public void delete() {
	System.out.println("删除用户");
}

/**
 * 连接点(可以被增强的方法)
 */
public void update() {
	System.out.println("更新用户");
}

/**
 * 连接点(可以被增强的方法)
 */
public void select() {
	System.out.println("查询用户");
}

}

3.制作通知	
	package cn.bgs.aop;

/**

  • 通知类
  • @author Mr.Wang

*/
public class AdviceDemo {

/*
 * 需要增强的功能
 */
public void fun(){
	System.out.println("打印日志信息!");
}

}

4. 开启AOP空间支持 ( 导约束 )
<?xml version="1.0" encoding="UTF-8"?>

5. 配置切面:切入点与通知之间的关系完成织入
<!-- 目标对象 -->
<bean name="userService" class="cn.bgs.service.UserServiceImpl"></bean>

<!-- 使用aop完成切面编程 -->
<aop:config>
	<!-- 切入点 -->
	<aop:pointcut expression="execution(public void cn.bgs.service.UserServiceImpl.add())" id="pc"/>
	<!-- 将通知类中的通知织入目标对象的连接点 -->
	<aop:aspect ref="adviceDemo">
		<aop:before method="fun" pointcut-ref="pc"/>
	</aop:aspect>
</aop:config>
6.创建测试类完成测试

package cn.bgs.test;

import static org.junit.Assert.*;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.bgs.service.UserService;
public class AopTest {
@Test
public void test() throws Exception {
ApplicationContext app=new ClassPathXmlApplicationContext(“beans-context.xml”);

	//在调用方法之前自动添加调用打印日志功能
	UserService us = (UserService) app.getBean("userService");
	us.add();
}

}

7.输出结果

四.切入点表达式
1.execution:表示执行某个切入点方法(常用)
•格式:execution(方法格式表达式)
•例一:匹配所有指定返回值类型的方法
•void (…) int (…) double (…)
•例二:匹配指定包或子包中类的方法
() cn.bgs.aop.user.dao.
.(…) cn…dao..(…)
•例三:匹配指定类或接口的方法
…UserImpl.(…) * Impl.(…) * DAO.(…)
•例四:匹配指定方法名或部分匹配方法名的方法
.add(…) .e(…) .get*(…)
•例五:匹配所有指定参数个数或类型的方法

  • .(int,int) * .(,) * .(,…) aop:config


    <aop:aspect ref=“myAdvice”>























    <aop:before pointcut=“execution(public void cn.bgs.aop. UserServiceImpl.add()) && args(int)" method=“fn”/>
    </aop:aspect>
    </aop:config>
    2.格式:
    “execution( [方法访问控制修饰符] 返回值类型 包名.类名.方法名(参数列表)) ”
    3.切入点可以配置接口,运行时走实现类
    4.切入点可以配置成公共的
    aop:config

    <aop:pointcut expression="execution(public void cn.bgs.service.UserServiceImpl.add(
    ))” id=“pt2”/>
    <aop:aspect ref=“myAdvice”>

    <aop:pointcut expression=“execution(public void cn.bgs.service.UserServiceImpl.add(*))” id=“pt1”/>

    <aop:before pointcut-ref=“pt2” method=“fn”/>
    <aop:before pointcut-ref=“pt2” method=“fn2”/>
    </aop:aspect>
    </aop:config>
    五.通知类别
    before:前置通知(应用:各种校验)
    在方法执行前执行,如果其中抛出异常:在异常前执行
    after:后通知(应用:清理现场)
    方法执行完毕后执行,无论方法中是否出现异常:都执行
    afterReturning:返回后通知(应用:常规数据处理)
    方法正常返回后执行,如果方法中抛出异常:无法执行
    afterThrowing:抛出异常后通知(应用:包装异常信息)
    方法抛出异常后执行,如果方法没有抛出异常,无法执行
    around:环绕通知(应用:十分强大,可以做任何事情)
    方法执行前后分别执行,可以阻止方法的执行
    //环绕通知
    public void around(ProceedingJoinPoint pjp) throws Throwable{
    System.out.println(“around before…”);
    //调用原始方法
    pjp.proceed();
    System.out.println(“around after…”);
    }
    1.通知的配置格式
    <aop:before pointcut-ref=“pt2” method=“before”/>
    <aop:after pointcut-ref=“pt2” method=“after”/>
    <aop:after-returning pointcut-ref=“pt2” method=“afterReturning”/>
    <aop:after-throwing pointcut-ref=“pt2” method=“afterThrowing”/>
    <aop:around pointcut-ref=“pt2” method=“around”/>
    2.通知顺序:与配置顺序有关
    多个切面间
    先声明的before先运行,
    后声明的before后运行
    先声明的after后运行
    后声明的after先运行
    总结:配置时以最终运行顺序为准
    六、AOP切面
    AOP切面描述的一组切入点与通知方法之间的绑定关系
    一个AOP配置中,可以使用多个切面
    七、AspectJ AOP框架
    介绍
    AspectJ是一个基于Java语言的AOP框架
    Spring2.0以后新增了对AspectJ切点表达式支持
    @AspectJ 是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面
    新版本Spring框架,建议使用AspectJ方式来开发AOP
    AspectJ 开发中,一般进行自定义内容编写,及需要进行功能增强,编写AOP。

八、AOP注解开发
使用注解开发AOP要保障所有的相关类必须受到Spring控制,因此在进行注解开发AOP之前,首先将所有相关类配置成Bean
使用aop注解开发的步骤:
1.添加依赖


org.springframework
spring-aspects
4.3.10.RELEASE

2.准备目标对象

3.准备通知

4.将通知和目标对象加入到spring容器中

5.在spring中开启使用注解

6.在通知类中完成对目标对象的增强
package cn.bgs.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

/**

  • 通知类
  • @author Mr.Wang

*/
@Aspect //这个注解是明确表示这个类是通知类
public class AdviceDemo {

/*
 * 需要增强的功能
 */

//前置通知
@Before("execution(public void cn.bgs.service.UserServiceImpl.add(int, int))")
public void before(){	
	System.out.println("fun打印日志信息!");
}

//后者通知(异常不执行)
@AfterReturning("execution(public void cn.bgs.service.UserServiceImpl.add(int, int))")
public void afterReturning(){	
	System.out.println("fun1打印日志信息afert异常不执行!");
}

//异常通知(异常才执行)
@AfterThrowing("execution(public void cn.bgs.service.UserServiceImpl.add(int, int))")
public void afterThrowing(){	
	System.out.println("fun2打印日志信息afert异常才执行!");
}

//后置通知(有没有异常都执行)
@After("execution(public void cn.bgs.service.UserServiceImpl.add(int, int))")
public void after(){	
	System.out.println("fun3打印日志信息afert有没有异常读执行!");
}

//环绕通知
@Around("execution(public void cn.bgs.service.UserServiceImpl.add(int, int))")
public void around(ProceedingJoinPoint pc) throws Throwable{	
	System.out.println("fun4在执行前打印日志");
	pc.proceed();
	System.out.println("fun4在执行后打印日志");
}

}
7.功能测试

8.测试结果

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值