AOP面向切面编程

2.3 AOP面向切面编程

​ 在软件行业中,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

注意

对于实现过接口的类在AOP时,使用基于JDK的动态代理来生成代理对象,从而对功能进行扩展。

对于没有实现接口的类在AOP时,使用CGLIB字节码增强从而生成代理对象(通过设置子类对象,从而对父类的功能进行扩展)

2.3.1 AOP常用概念

  • 目标类(target)

需要进行功能扩展的类

  • 连接点(joinpoint)

目标类中的所有方法称为连接点

  • 切入点(pointCut)

连接点中需要增强(扩展)的方法

  • 增强(通知)(advise)

功能扩展

  • 织入(weaving)

将增强应用到切入点的过程

  • 代理对象(proxy)

织入完成后生成代理对象

  • 切面(aspect)

多个切入点组成切面

2.3.2 基于JDK的动态代理

需求:UserDao在调用saveUser方法时,先判断是否为admin,不能修改UserDao的源码

目标类:接口+实现 UserDaoImpl

增强: MyAdvice

切入点:saveUser()

工厂类:DaoBeanFactory 用于生成UserDao的代理对象

package com.sofwin.dao;

public interface UserDao {
	
	void saveUser();
	
	void deleteUser();

}

package com.sofwin.dao.impl;


import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.sofwin.dao.UserDao;
//如果注解中不声明名称,默认使用类名,首字母小写
public class UserDaoImpl implements UserDao {
	@Value("100")
	private Integer id;
	
	public UserDaoImpl() {
		System.out.println("constructor........userDaoImpl");
	}
	//init-method
	@PostConstruct
	public void init2() {
		System.out.println("init........");
	}
	@PreDestroy
	public void destroy() {
		System.out.println("destroy......");
	}

	@Override
	public void saveUser() {
		System.out.println("saveUser|mysql....."+this.id);
	}

	@Override
	public void deleteUser() {
		System.out.println("deleteUser|mysql.........");
	}

}

package com.sofwin.util;

public class MyAdvice {
	
	public void checkUser() {
		System.out.println("checkUser.......");
	}

}

工厂类:

package com.sofwin.util;

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

import com.sofwin.dao.UserDao;
/**
 * DAO代理对象工厂
 * @author Mac
 *
 */
public class DaoBeanFactory {
	/**
	 * 用于生成UserDao的代理对象
	 * @param dao    目标类的实例
	 * @return  代理对象
	 */
	public static UserDao createUserDao(UserDao dao) {
		//增强对象
		final MyAdvice advice = new MyAdvice();
		UserDao proxyDao = (UserDao)Proxy.newProxyInstance(DaoBeanFactory.class.getClassLoader(), dao.getClass().getInterfaces(), new InvocationHandler() {
			
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				//获取到方法名
				String methodName = method.getName();
				if("saveUser".equals(methodName)) {
					//确定切入点
					advice.checkUser();
				}
				Object obj = method.invoke(dao, args);
				return obj;
			}
		});
		return proxyDao;
	}

}

测试类:

package com.sofwin.test;

import org.junit.Test;

import com.sofwin.dao.UserDao;
import com.sofwin.dao.impl.UserDaoImpl;
import com.sofwin.util.DaoBeanFactory;

public class JdkProxyTest {
	@Test
	public void test01() {
		//获取目标对象
		UserDao dao = new UserDaoImpl();
		dao.saveUser();
		UserDao daoProxy = DaoBeanFactory.createUserDao(dao);
		daoProxy.saveUser();
	}

}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xVLtwJX7-1625656511466)(img/6.png)]

2.3.3 基于CGLIB的动态代理

没有实现过接口,就不能使用jdk的动态代理。在这种情况下,spring使用了cglib基于字节码增强的动态代理。

CGLIB(Code Generation Library)是一个开源项目。

是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。Hibernate支持它来实现PO(Persistent Object 持久化对象)字节码的动态生成。

  • 导入依赖

    • cglib.jar ,asm.jar

    在spring 核心依赖中已经包含有cglib的依赖

注意

cglib因为基于字节码增强,所以可以对没有实现接口和实现过接口的类进行增强。

目标类:UserDaoSqlImpl不实现任何接口,saveUser delete方法

增强类: MyAdvice (同上)

切入点:saveUser

工厂类:DaoBeanFactory

目标类:

package com.sofwin.dao.impl;

public class UserDaoSqlImpl {
	
	public void saveUser() {
		System.out.println("saveUser.....");
	}
	public void delete() {
		System.out.println("delete......");
	}

}

工厂类:

package com.sofwin.util;

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

import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import com.sofwin.dao.UserDao;
/**
 * DAO代理对象工厂
 * @author Mac
 *
 */
import com.sofwin.dao.impl.UserDaoSqlImpl;
public class DaoBeanFactory {
	/**
	 * 用于生成UserDao的代理对象
	 * @param dao    目标类的实例
	 * @return  代理对象
	 */
	public static UserDao createUserDao(UserDao dao) {
		//增强对象
		final MyAdvice advice = new MyAdvice();
		UserDao proxyDao = (UserDao)Proxy.newProxyInstance(DaoBeanFactory.class.getClassLoader(), dao.getClass().getInterfaces(), new InvocationHandler() {
			
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				//获取到方法名
				String methodName = method.getName();
				if("saveUser".equals(methodName)) {
					//确定切入点
					advice.checkUser();
				}
				Object obj = method.invoke(dao, args);
				return obj;
			}
		});
		return proxyDao;
	}

	/**
	 * 基于字节码生成代理对象
	 * @param target 目标对象
	 * @return  代理对象
	 */
	public static UserDaoSqlImpl createSqlImpl(UserDaoSqlImpl target) {
		final MyAdvice advice = new MyAdvice();
		Callback a;
		//cglib的核心类
		Enhancer hancer = new Enhancer();
		//设置父类
		hancer.setSuperclass(target.getClass());
		//方法回调处理
		hancer.setCallback(new MethodInterceptor() {
			//对方法调用的拦截(当方法被调用时先进入该方法)
			@Override
			public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
				String  methodName = method.getName();
				if("saveUser".equals(methodName)) {
					advice.checkUser();
				}
				Object obj = method.invoke(target, args);
				return obj;
			}
		});
		//生成代理对象
		UserDaoSqlImpl proxy = (UserDaoSqlImpl)hancer.create();
		return proxy;
	}
}

测试类:

package com.sofwin.test;

import org.junit.Test;

import com.sofwin.dao.UserDao;
import com.sofwin.dao.impl.UserDaoImpl;
import com.sofwin.dao.impl.UserDaoSqlImpl;
import com.sofwin.util.DaoBeanFactory;

public class JdkProxyTest {
	@Test
	public void test01() {
		//获取目标对象
		UserDao dao = new UserDaoImpl();
		dao.saveUser();
		UserDao daoProxy = DaoBeanFactory.createUserDao(dao);
		daoProxy.saveUser();
	}
	
	@Test
	public void test02() {
		//创建目标对象
		UserDaoSqlImpl dao = new UserDaoSqlImpl();
		dao.saveUser();
		//根据目标对象创建基于字节码增强的代理对象
		UserDaoSqlImpl proxy = DaoBeanFactory.createSqlImpl(dao);
		proxy.saveUser();
	}

}

2.3.4 半自动AOP

2.3.4.1 AOP联盟增强类型
  • 前置增强:目标方法执行前的功能增强
  • 后置增强:目标方法执行后的功能增强
  • 环绕增强:目标方法执行前后都进行了功能扩展
  • 异常增强:抛出异常时执行的增强
  • 引介增强:对目标对象添加方法和属性(了解)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4XCVmbFK-1625656511472)(img/7.png)]

try{
前置增强
目标方法
后置增强
}catch(Exception ex){
	异常增强
}
2.3.4.2 依赖搭建
  • 4个核心+2个日志+spring-aop+aopaliance.jar

工厂对象ProxyFactorBean用于生成代理对象,增强必须实现对应的增强接口。

2.3.4.3 半自动AOP编程

目标类:UserDaoImpl

增强类:implement MethodIntercepor接口

工厂类:使用spring提供的ProxyFactorBean

增强类:

package com.sofwin.util;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
 * 环绕增强
 * @author Mac
 *
 */
public class MyAdviceInterceptor implements MethodInterceptor {

	/**
	 * 反射方法
	 * 
	 */
	@Override
	public Object invoke(MethodInvocation methodInvocation) throws Throwable {
		System.out.println("前置增强.....");
		//调用或执行目标方法
		Object obj = methodInvocation.proceed();
		System.out.println("后置增强.....");
		return obj;
	}

}

工厂类配置:

<?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:s="http://www.springframework.org/schema/p"
    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">
<!-- 
目标类
 -->
<bean id="userDao" class="com.sofwin.dao.impl.UserDaoImpl"></bean>
<!-- 
增强类
 -->
<bean id="myAdvice" class="com.sofwin.util.MyAdviceInterceptor"></bean>
<!-- 
工厂类

 -->
<bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 
taret:注入目标对象,参数类型Object(引用类型)
 -->
<property name="target" ref="userDao"></property>
<!-- 
interceporNames:注入增强,如果是单个直接使用value
 -->
<property name="interceptorNames" value="myAdvice"></property>
<!-- 
如果使用jdk动态代理,需要使用Interfaces来指定目标类实现的接口

 -->
<property name="interfaces" value="com.sofwin.dao.UserDao"></property>
<!-- 
optimize:是否强制使用cglib
如果为false,spring会根据是否实现接口来调用jdk动态代理
如果为true,即使目标类实现了接口,也会使用cglib
 -->
<property name="optimize" value="true"></property>
</bean>
</beans>

测试类:

@Test
	public void test03() {
		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
		//取代理对象的配置名称
		UserDao dao = (UserDao)context.getBean("userDaoProxy");
		dao.saveUser();
	}

2.3.5 传统AOP

  • 导入AOP约束
  • 导入weaver包

目标类:UserDaoImpl

增加类:必须实现指定接口MethodInterceptor MyAdviseInterceptor

工厂对象不需要spring来配置,直接使用AOP标签

配置文件:

<?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:s="http://www.springframework.org/schema/p"
    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">
<!-- 
所以以save开头的方法进行增强
 -->
<!-- 
目标类的配置
 -->
<bean id="userDao" class="com.sofwin.dao.impl.UserDaoImpl"></bean>
<bean id="userDaoSql" class="com.sofwin.dao.impl.UserDaoSqlImpl"></bean>
<!-- 
增强的配置
 -->
<bean id="myAdvise" class="com.sofwin.util.MyAdviceInterceptor"></bean>
<!-- 
传统AOP配置标签
proxy-target-class:是否强制使用cglib
 -->
<aop:config proxy-target-class="true">
<!-- 
	aop:pointcut:代表成员变量,在真个aop:config中的所有的aop:advisor都可以去通过pointcut-ref去使用它
	 -->
	<aop:pointcut expression="execution(* com.sofwin.dao.impl.*.save*(..))" id="point1"/>
<!-- 
配置增强:
advice-ref:配置增强的引用得知
pointcut:切面,切入点表达式:用于寻找目标类并确定切入点,要求所有的目标类
					是基于execution()函数的
pointcut-ref:是用于指定切面的ID,需要手动定义
	pointcut:方法中的局部变量,他只能被引用他的advisor来使用

 -->
	<aop:advisor  advice-ref="myAdvise" pointcut-ref="point1"/>
	
</aop:config>
</beans>

测试类:

package com.sofwin.test;

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

import com.sofwin.dao.UserDao;
import com.sofwin.dao.impl.UserDaoImpl;
import com.sofwin.dao.impl.UserDaoSqlImpl;
import com.sofwin.util.DaoBeanFactory;

public class JdkProxyTest {
	@Test
	public void test01() {
		//获取目标对象
		UserDao dao = new UserDaoImpl();
		dao.saveUser();
		UserDao daoProxy = DaoBeanFactory.createUserDao(dao);
		daoProxy.saveUser();
	}
	
	@Test
	public void test02() {
		//创建目标对象
		UserDaoSqlImpl dao = new UserDaoSqlImpl();
		dao.saveUser();
		//根据目标对象创建基于字节码增强的代理对象
		UserDaoSqlImpl proxy = DaoBeanFactory.createSqlImpl(dao);
		proxy.saveUser();
	}
	
	@Test
	public void test03() {
		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
		//取代理对象的配置名称
		UserDao dao = (UserDao)context.getBean("userDaoProxy");
		dao.saveUser();
	}
	@Test
	public void test04() {
		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
		//使用原来的bean的ID,但获取到的是目标对象的代理对象,因为满足切入点表达式
		//织入的过程 aopweaving
		UserDao dao = (UserDao)context.getBean("userDao");
		dao.saveUser();
	}
	@Test
	public void test05() {
		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
		//使用原来的bean的ID,但获取到的是目标对象的代理对象,因为满足切入点表达式
		//织入的过程 aopweaving
		UserDaoSqlImpl dao = (UserDaoSqlImpl)context.getBean("userDaoSql");
		dao.saveUser();
	}
}

2.3.6 切入点表达式

切入点表达式是基于execution函数的,execution(表达式)

修饰符 返回值 包名.类名.方法名() throws

1修饰符(public private * )【一般省略】

2返回值(Integer int void *)不能省略

3.包名com.sofwin 【不能省】

4.类名(全称也可以使用通配符)

5.方法名(可以是全称也可以是通配符)

6.括号中输入形参类型,(…)代表任意个任意类型的参数

7.抛出的异常【可以省略的】

execution(* com.sofwin.dao.impl.UserDaoImpl.*(…))

exectuion(* com.sofwin.dao.impl.*.*(..)) || execution(* com.sofwin.util.*.*(..))

2.3.7 基于AspectJ的AOP

  • AspectJ是一个java的AOP框架
  • Spring 2.0开始支持AspectJ
  • @Aspect是AspectJ的新注解,基于JDK5的注解,可以直接在java Bean上声明

商用场景:自定义代码或自定义增强推荐使用

框架搭建

spring-aspects.jar

基于aspectJ的增强类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RgvCU7MY-1625656511475)(img/8.png)]

try{
	前置增强(准备资源)
	目标方法
	后置增强(功能扩展)
}cache(){
	异常增强(异常处理)
}fianlly{
	最终增强(释放资源)
}
2.3.7.1 xml方式的AspectJ

目标类:UserDaoImpl

增强类:MyAspectJ

工厂对象也有spring容器自动生成

增强类:

package com.sofwin.util;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;

public class MyAspectj {
	
	public void before(JoinPoint point) {
		Signature signature = point.getSignature();
		System.out.println("before........"+signature.getName());
	}
	
	/**
	 * 
	 */
	public void afterReturnning(JoinPoint point,Object obj) {
		System.out.println("after returnning......."+obj);
	}
	/**
	 * 
	 * @param point
	 * @throws Throwable
	 */
	public Object around(ProceedingJoinPoint point) throws Throwable {
		System.out.println("前置....");
		//调用目标方法
		Object obj = point.proceed();
		System.out.println("后置......");
		return obj;
	}
	
	public void afterThrowing(Throwable ex) {
		if(ex instanceof ArithmeticException) {
			System.out.println("throwing........除0异常");
			
		}
	}
	
	public void after() {
		System.out.println("清理资源");
	}

}

配置文件:

<?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:s="http://www.springframework.org/schema/p"
    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">
<!-- 
所以以save开头的方法进行增强
 -->
<!-- 
目标类的配置
 -->
<bean id="userDao" class="com.sofwin.dao.impl.UserDaoImpl"></bean>
<bean id="userDaoSql" class="com.sofwin.dao.impl.UserDaoSqlImpl"></bean>
<!-- 
增强
 -->
 <bean id="myAspectj" class="com.sofwin.util.MyAspectj"></bean>
<!-- 
基于aspectj的AOP织入生成代理对象使用aop:config
 -->
 <aop:config proxy-target-class="true">
 <!-- 
 定义aspect
 id:唯一名称
 ref:用于指定普通的增强类
  -->
 	<aop:aspect id="aspect1" ref="myAspectj">
 	<aop:pointcut expression="execution(* com.sofwin.dao.impl.*.save*(..))" id="p1"/>
 	<!-- 
 	aop:before  前置增强
 		method:指定增强类中前置增强的方法的方法名
 	pointcut:定义局部切入点,只能当前before使用
 	pointcut-ref:可以使用公共的切入点
 	arg-names:用于定义前置增强获取目标方法的相关数据,前置增强的方法中的JoinPoint类型的参数的参数名
 	 -->
 		<aop:before method="before" pointcut-ref="p1" arg-names="point"/>
 		<!-- 
 		配置后置增强:
 		method:用于指定后置增强的方法名
 		returning:用于获取目标方法的返回值的,
 			第一个参数为JoinPoint 用于获取目标方法的信息
 		after-retunning增强方法中的第二个参数,类型Object
 		 -->
 		<aop:after-returning method="afterReturnning" pointcut-ref="p1" returning="obj"/>
 		<!-- 
 		环绕增强:
 		method:方法名
 		
 		 -->
 		<aop:around method="around" pointcut-ref="p1"/>
 		<!-- 异常增强
 		throwing:用于获取捕获的异常信息
 		 -->
 		<aop:after-throwing method="afterThrowing" pointcut-ref="p1" throwing="ex"/>
 		<!-- 指定最终增强 -->
 		<aop:after method="after" pointcut-ref="p1"/>
 	</aop:aspect>
 </aop:config>
</beans>
2.3.7.2 基于注解的AspectJ
  • 开启组件扫描
  • 开启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:s="http://www.springframework.org/schema/p"
    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.sofwin"></context:component-scan>
<!-- 开启注解驱动
用来配置强制使用cglib
 -->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
<!-- 
所以以save开头的方法进行增强
 -->
<!-- 
目标类的配置
 -->
<bean id="userDao" class="com.sofwin.dao.impl.UserDaoImpl"></bean>
<bean id="userDaoSql" class="com.sofwin.dao.impl.UserDaoSqlImpl"></bean>


</beans>
package com.sofwin.util;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
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;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
//IOC
@Component
//用于声明该类是一个增强类<aop:aspect  ref="">
@Aspect
public class MyAspectj {
	//等于<aop:before 
	//value中放切入点表达式
	@Before(value = "a()")
	public void before(JoinPoint point) {
		Signature signature = point.getSignature();
		System.out.println("before........"+signature.getName());
	}
	
	/**
	 * 
	 */
	//value:切入点表达式
	//returnning   <aop:after-returning returning="")
	@AfterReturning(value = "a()",returning = "obj")
	public void afterReturnning(JoinPoint point,Object obj) {
		System.out.println("after returnning......."+obj);
	}
	/**
	 * 
	 * @param point
	 * @throws Throwable
	 */
	@Around(value="a()")
	public Object around(ProceedingJoinPoint point) throws Throwable {
		System.out.println("前置....");
		//调用目标方法
		Object obj = point.proceed();
		System.out.println("后置......");
		return obj;
	}
	@AfterThrowing(value="a()",throwing = "ex")
	public void afterThrowing(Throwable ex) {
		if(ex instanceof ArithmeticException) {
			System.out.println("throwing........除0异常");
			
		}
	}
	@After(value="a()")
	public void after() {
		System.out.println("清理资源");
	}
	//<aop:pointcut expression=""
	@Pointcut(value="execution(* com.sofwin.dao.impl.*.save*(..))")
	public void a() {
		
	}

}

总结

@Aspect //用于声明该类是一个增强类
@Before //用于声明前置增强
@AfterReturning //声明后置增强
@Aroud  //声明环绕增强
@After  //声明最终增强
@AfterThrowing //声明异常增强
@PointCut  //用于声明切入点

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值