AOP思想的学习以及实现
1.AOP:面向切面编程,通过预编译的方式和运行期动态代理实现程序功能的统一维护的一种技术。
2.AOP在Spring中的作用
3.SpringAOP中,我们使用Advice来定义横切逻辑,Spring中支持5种类型的Advice
分别是:前置通知 MethodBeforeAdvice
后置通知 AfterReturningAdvice
环绕通知 MethodInterceptor
异常抛出通知
引介通知
4.使用AOP织入,需要倒一个maven包
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> <scope>runtime</scope> </dependency>
实现AOP的两种方式:
方法一:使用Spring的API接口 5大点
方法二:使用自定义类 6大点
5.使用前置通知和后置通知实现代理日志的打印(方法一使用Spring的API接口)感觉有点反人类记不住啊
5.1抽象对象
public interface UserService { public void add(); public void delete(); public void update(); public void select(); }
5.2实际对象
import org.springframework.stereotype.Component; //使用了注解,注册到容器中,id是userServiceImpl @Component 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("查询了一个用户"); } }
5.3前置通知日志对象和后置通知日志对象
import org.springframework.aop.MethodBeforeAdvice; import org.springframework.stereotype.Component; import java.lang.reflect.Method; //前置通知日志对象,使用了注解注册到Spring容器中,id第一个字母小写 @Component public class BeforAdvicelog implements MethodBeforeAdvice { public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("之前"+o.getClass().getName()+"调用了"+method.getName()+"的方法"); } }
import java.lang.reflect.Method; //后置日志通知 @Component public class AfterAdvicelog implements AfterReturningAdvice { public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable { System.out.println(""+"调用了"+method.getName()+"方法"+"结果是"+o); } }
5.4XML配置
使用aop要注意添加 aop
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd"
使用注解要注意引入context 跟上面aop类似
注意添加
<context:annotation-config/> //引入配置
<context:component-scan base-package="com.chen"/> //使用注解的包
使用SpringAPI实现AOP aop:config 下使用aop:pointcut 和aop:advisor
<?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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <context:annotation-config/> <context:component-scan base-package="com.chen"/> <aop:config> <!--先有切入点pointcut id="切入点的名字" expression="execution(要执行的位置! (*(修饰词) *(返回值) *(类名) *(方法名) *(参数)))"--> <aop:pointcut id="pointcut" expression="execution(public * com.chen.service.UserServiceImpl.*(..))"/> <!--有了切入点(哪个方法)就可以执行环绕了--> <!--下面这行就是引用哪个切入日志,在哪个切入点上--> <aop:advisor advice-ref="beforAdvicelog" pointcut-ref="pointcut"/> <aop:advisor advice-ref="afterAdvicelog" pointcut-ref="pointcut"/> </aop:config> </beans>
注意:
execution(修饰符 返回值 包名.类名/接口名.方法名(参数列表))可以忽略掉修饰符了 自己可以写上修饰符(public等)
(..)可以代表任意参数,(*)代表一个参数,(*,String)代表第一个参数为任何值,第二个参数为String类型
<aop:pointcut id="pointcut" expression="execution(public * com.chen.service.UserServiceImpl.*(..))"/>
意思是:返回公共的 任意返回值的 com.chen.service.UserServiceImpl 的所有方法,方法的参数是所有参数
5.5测试代码以及执行结果
import com.chen.service.UserService; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Mytest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService userserviceimpl = context.getBean("userServiceImpl", UserService.class);//代理的都是接口,所以要返回接口类型 userserviceimpl.add(); } }
6.使用自定义类实现AOP
6.1首先先自定义一个类作为日志输出类(后续要作为切面)
import org.springframework.stereotype.Component; @Component public class Diylog { public void beforelog(){ System.out.println("======自定义日志执行前调用========"); } public void afterlog(){ System.out.println("======自定义日志执行后调用========"); } }
6.2xml配置
自定义类实现AOP要使用<aop:config> 下的<aop: aspect>
<bean id="diylog" class="com.chen.DiyLog.Diylog"/> <aop:config> <aop:aspect id="qiemian" ref="diylog"> <!--自定义类要先有一个切面 ,切面就是这个自定义类--> <!--定义切点--> <aop:pointcut id="pointcut" expression="execution(public * com.chen.service.UserServiceImpl.*(..))"/> <!--在diylog这个切面上,在pointcut这个切点上,调用diylog类里面的before方法--> <aop:before method="beforelog" pointcut-ref="pointcut"/> <aop:after method="afterlog" pointcut-ref="pointcut"/> </aop:aspect> </aop:config>
6.3测试结果