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

被折叠的 条评论
为什么被折叠?



