发布时间:2025-12-10 11:44:32 浏览次数:26
案例gitee地址:https://gitee.com/jsxlliar/spring-aop.git.
target
用来表示目标对象,即需要通过aop来增强的对象。
proxy
代理对象,target通过aop增强之后生成的代理对象。
AspectJ
AspectJ是一个面向切面的框架,和spring中的aop可以集成在一起使用,通过Aspectj提供的一些功能实现aop代理变得非常方便。
AspectJ使用步骤
1.创建一个@Aspect标注类
2.@Aspect标注的类中,通过@Pointcut定义切入点
3.@Aspect标注的类中,通过AspectJ提供的一些通知相关的注解定义通知
4.使用AspectJProxyFactory结合@Ascpect标注的类,来生成代理对象
pom依赖
<properties><spring.version>5.3.12</spring.version></properties><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>${spring.version}</version></dependency></dependencies> public class Service1 {public void m1(){System.out.println("我是 m1 方法");}public void m2(){System.out.println(1/0);System.out.println("我是 m2 方法");}} @Aspectpublic class Aspect1 {@Pointcut("execution(* com.jsxl.pointcut1.Service1.*(..))")public void pointcut1(){}@Before(value = "pointcut1()")public void before(JoinPoint joinPoint) {//输出连接点的信息System.out.println("前置通知," + joinPoint);}@AfterThrowing(value = "pointcut1()", throwing = "e")public void afterThrowing(JoinPoint joinPoint, Exception e) {//发生异常之后输出异常信息System.out.println(joinPoint + ",发生异常:" + e.getMessage());}} public class AopTest1 {public static void main(String[] args) {try {//对应目标对象Service1 target = new Service1();//创建AspectJProxyFactory对象AspectJProxyFactory proxyFactory = new AspectJProxyFactory();//设置被代理的目标对象proxyFactory.setTarget(target);//设置标注了@Aspect注解的类proxyFactory.addAspect(Aspect1.class);//生成代理对象Service1 proxy = proxyFactory.getProxy();//使用代理对象proxy.m1();proxy.m2();} catch (Exception e) {}}} execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)| public *.*(..) | 任何公共方法 |
| * com.jsxl..IPointcut.*() | com.jsxl包及所有子包下IPointcut接口中的任何无参方法 |
| * com.jsxl..*.*(..) | com.jsxl包及所有子包类中的任何方法(参数不限) |
| * com.jsxl..IPointcut.() | com.jsxl包及所有子包下IPointcut接口中的任何只有一个参数的方法 |
| * com.jsxl..IPointcut+.*() | com.jsxl包及所有子包下IPointcut接口及子类型的任何无参方法 |
| * Service.*(String) | Service中只有1个参数且参数类型是String的方法 |
| * Service.*(*,String) | Service中只有2个参数且第二个参数类型是String的方法 |
| * Sereice.*(..,tring) | Service中最后1个参数类型是String的方法 |
AspectJ类型匹配的通配符:
用法
within(类型表达式):目标对象target的类型是否和within中指定的类型匹配
匹配原则
target.getClass().equals(within表达式中指定的类型) public class S1 {public void m1() {System.out.println("我是m1");}public void m2() {System.out.println("我是m2");}} public class S2 extends S1 {@Overridepublic void m2() {super.m2();}public void m3() {System.out.println("我是m3");}} @Aspectpublic class Aspect2 {@Pointcut("within(S1)")// @Pointcut("within(S1+)")// @Pointcut("within(S2)")public void pc() {}@Before("pc()")public void beforeAdvice(JoinPoint joinpoint) {System.out.println(joinpoint);}} public class AopTest2 {public static void main(String[] args) {S2 target = new S2();AspectJProxyFactory proxyFactory = new AspectJProxyFactory();proxyFactory.setTarget(target);proxyFactory.addAspect(Aspect2.class);S2 proxy = proxyFactory.getProxy();proxy.m1();proxy.m2();proxy.m3();}}@Pointcut(“within(S1)”)
@Pointcut(“within(S1+)”)
@Pointcut(“within(S2)”)
用法
this(类型全限定名):通过aop创建的代理对象的类型是否和this中指定的类型匹配;注意判断的目标是代理对象;this中使用的表达式必须是类型全限定名,不支持通配符。
匹配原则
如:this(x),则代理对象proxy满足下面条件时会匹配
| within | target对象 | target.getClass().equals(表达式中指定的类型) |
| this | proxy对象 | x.getClass().isAssignableFrom(proxy.getClass()); |
| target | target对象 | x.getClass().isAssignableFrom(target.getClass()); |
AOP(aspect-oriented programming)
什么是面向切面编程
面向切面编程是一种编程范式,试图解决横切关注点(cross-cutting concerns)的问题。面向切面编程(AOP)是对面向对象编程(OOP)的一种补充,它提供了一种不同的方式去思考程序的结构。
在 OOP 中最小的单元是类(class),而在 AOP 中最小的单元是切面(aspect)。
更通俗地讲就是,AOP 有助于我们将不同但是有必要的重复性代码重构为不同的模块。这么做的好处是,我们可以将这些重复性代码集中管理起来复用,而不是每次都要重复写一遍。
这种方法的好处是,代码将会变得更易于维护,从而将业务逻辑从杂乱的代码中脱离出来,专注于业务逻辑代码的开发。我们将这些不同的功能划分到不同的切面中。
一个切面是对杂乱地散落在各个类中的横切关注点的模块化。比如,集中日志记录或事务管理就是最好的例子。
| Signature getSignature(); | *获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息* |
| Object[] getArgs(); | 获取传入目标方法的参数对象 |
| Object getTarget(); | 获取被代理的对象 |
| Object getThis(); | 获取代理对象 |
Role 类
public class Role {private String name;public Role(String name) { this.name = name; }public String getName() { return name; }public void setName(String name) { this.name = name; }} public interface RoleService {void print(Role role);} @Componentpublic class RoleServiceImp implements RoleService {//连接点public void print(Role role) {System.out.println(role.getName());}}方案一
@Aspectpublic class RoleAspect1 {@Before("execution(* com.jsxl.service.impl.RoleServiceImp.print()) && args(role ,sort)")public void before(Role role,int sort) {System.out.println(role.getName()+"Before传参");System.out.println("进入方法before...");}@After("execution(* com.jsxl.service.impl.RoleServiceImp.print())")public void after() {System.out.println("进入方法after...");}@AfterReturning("execution(* com.jsxl.service.impl.RoleServiceImp.print())")public void afterReturning() {System.out.println("进入方法afterReturning...");}@AfterThrowing("execution(* com.jsxl.service.impl.RoleServiceImp.print())")public void afterThrowing() {System.out.println("afterThrowing...");}}方案二:切点
@Aspect//类需要使用@Aspect进行标注public class RoleAspect {//"+"表示RoleServiceImp的所有子类;defaultImpl 表示默认需要添加的新的类@DeclareParents(value = "com.jsxl.service.impl.RoleServiceImp+",defaultImpl = RoleVerifierImp.class)public RoleVerifier roleVerifier;//定义了一个切入点,可以匹配RoleServiceImp中所有方法@Pointcut("execution(* com.jsxl.service.impl.RoleServiceImp.*(..))")// @Pointcut("target(com.jsxl.service.impl.RoleServiceImp)")public void printCut() {}//@Before定义了一个前置增强,对切入点中的所有方法有效@Before("printCut() && args(role)")//args(role)传参public void before(JoinPoint joinPoint, Role role) {System.out.println("Before传入参数修改前name"+role.getName());role.setName("李四");System.out.println("Before传入参数修改后name"+role.getName());System.out.println("前置通知"+joinPoint);}//@After定义了一个后置增强,对切入点中的所有方法有效@After("printCut()")public void after() {System.out.println("进入方法after...");}//@AfterReturning定义了一个正常返回增强,对切入点中的所有方法有效@AfterReturning("printCut()")public void afterReturning() {System.out.println("进入方法afterReturning...");}//@AfterThrowing定义了一个异常返回增强,对切入点中的所有方法有效@AfterThrowing("printCut()")public void afterThrowing() {System.out.println("afterThrowing...");}//@AfterThrowing定义了一个环绕增强,对切入点中的所有方法有效@Around("printCut()")public void aroundfunc(ProceedingJoinPoint func) {System.out.println("执行环绕方法");try {func.proceed();} catch (Throwable e) {e.printStackTrace();}System.out.println("环绕方法执行完毕");}}通过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:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-4.3.xsd"><beans><!--声明自动为spring容器中那些配置@aspect切面的bean创建代理,织入切面。--><aop:aspectj-autoproxy proxy-target-class="true"/><bean id="roleAspect" class="com.jsxl.aspect.RoleAspect"/><bean id="roleService" class="com.jsxl.service.impl.RoleServiceImp"/></beans></beans>通过注解配置
@Configuration@EnableAspectJAutoProxy//自动代理@ComponentScan("com.jsxl")public class AopConfig {@Beanpublic RoleAspect1 getRoleAspect1(){ return new RoleAspect1(); }@Beanpublic RoleAspect getRoleAspect(){return new RoleAspect();}} public interface RoleVerifier {public abstract boolean verify(Role role);} public class RoleVerifierImp implements RoleVerifier {@Overridepublic boolean verify(Role role) {return role != null;}} public class SpringAopTest {//public static void main(String[] args) {//ApplicationContext context1 = new AnnotationConfigApplicationContext(AopConfig.class);//ApplicationContext context2 =new ClassPathXmlApplicationContext("applicationContext.xml");//RoleService bean = context2.getBean(RoleService.class);//Role role = new Role("张三");//bean.print(role);//role=null;//System.out.println("-----------------------------");//bean.print(role);//}public static void main(String[] args) {ApplicationContext context1 = new AnnotationConfigApplicationContext(AopConfig.class);ApplicationContext context2 =new ClassPathXmlApplicationContext("applicationContext.xml");RoleService bean = context2.getBean(RoleService.class);RoleVerifier roleVerifier = (RoleVerifier)bean;Role role = new Role("张三");if (roleVerifier.verify(role)){bean.print(role);}role=null;System.out.println("-----------------------------");if (roleVerifier.verify(role)) {bean.print(role);}}}定义注解检查是否登录
@Retention(RUNTIME)@Target({METHOD})public @interface CheckLogin {}定义注解检查方法是否有权限
@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface CheckActionRight {//要执行这个方法必须要有的权限String value();}判断是否拥有该权限
@RestController@CrossOrigin@RequestMapping("/admin/right")@Api(description = "菜单那权限和动作权限接口")public class AdminRightController {@Autowiredprivate RightService rightService;@GetMapping("/search/check")@CheckActionRight("TEST_SEARCH")@ApiOperation("判断该用户是否有查询权限")public R CheckSearch() {return new R(ResponseEnum.SUCCESS, null);}@GetMapping("/audit/check")@CheckActionRight(value = "TEST_AUDIT")@ApiOperation("判断该用户是否有审批权限")public R CheckAaudit() {return new R(ResponseEnum.SUCCESS, null);}}ProceedingJoinPoint对象
ProceedingJoinPoint对象是JoinPoint的子接口,该对象只用在@Around的切面方法中,
添加了
Object proceed() throws Throwable //执行目标方法
Object proceed(Object[] var1) throws Throwable //传入的新的参数去执行目标方法