参考视频
https://www.bilibili.com/video/BV1yq4y1N78E?spm_id_from=333.337.search-card.all.click
AOP 简介
AOP概念
AOP 面向切面编程,一种编程范式,主要关注共性功能
AOP优势
AOP入门案例
AOP 核心概念
连接点指所有方法
切入点指拥有共性功能的方法
共性功能称为通知
入门案例(注解 + xml)
pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
dao、service
package com.zs.dao;
public interface AopDao {
void save();
}
package com.zs.dao.impl;
import com.zs.dao.AopDao;
import org.springframework.stereotype.Repository;
@Repository
public class AopDaoImpl implements AopDao {
@Override
public void save() {
System.out.println("save dao ...");
}
}
package com.zs.service;
public interface AopService {
void save();
}
package com.zs.service.impl;
import com.zs.dao.AopDao;
import com.zs.service.AopService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AopServiceImpl implements AopService {
@Autowired
private AopDao aopDao;
@Override
public void save() {
System.out.println("aop service ... ");
aopDao.save();
}
}
通知类
package com.zs.aop;
@Component
public class AOPAdvice {
public void function(){
System.out.println("共性功能");
}
}
applicationContext.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: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">
<import resource="applicationContext-aop.xml"/>
</beans>
applicationContext-aop.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: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:component-scan base-package="com.zs">
<context:exclude-filter type="custom" expression="config.filter.AopTestTypeFilter"/>
</context:component-scan>
<aop:config>
<aop:pointcut id="pt" expression="execution(* *..*(..))"/>
<aop:aspect ref="AOPAdvice">
<aop:before method="function" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
</beans>
AOP配置(XML)(重点)
基本配置
切入点表达式
切入点:描述的是某个方法 切入点表达式是一个快速匹配方法描述的通配格式,类似于正则表达式 关键字(访问修饰符 返回值 报名.类名.方法名(参数) 异常名)
范例: execution(public User com.itheima.service.UserService.findById(int))
关键字
execution:匹配执行指定方法 args:匹配带有指定参数类型的方法 within: this: target: @within @target @args @annotation bean reference pointcut
切入点通配符
- * :任意符
- … : 连续任意符
- + : 专用于匹配子类类型
逻辑运算符
&& 、||、 !
切入点配置的三种方式
切入点配置经验
严格遵循规范命名 先为方法配置局部切入点 再抽取类中公共切入点 最后抽取全局切入点 代码走查过程中检测切入点是否存在越界性包含,非包含性进驻 设定AOP执行检测程序,在单元测试中监控通知被执行次数与预计次数是否匹配 设定完毕的切入点如果发生调整务必进行回归测试 (适用于xml格式)
通知类型
前置通知:原始方法执行前,如果通知中抛出异常,阻止原始方法运行
后置通知:原始方法执行后,无论方法是否出现异常,都将执行通知
返回后通知:原始方法执行后,原始方法抛出异常,无法执行
抛出异常后通知:原始方法抛出异常后执行,如果原始方法没有抛出异常,无法执行
环绕通知:在原始方法执行前后均有对应执行,还可以阻止原始方法的执行
<aop:config>
<aop:pointcut id="pt" expression="execution(* *..*(..))"/>
<aop:aspect ref="AOPAdvice">
<aop:before method="before" pointcut-ref="pt"/>
<aop:after method="after" pointcut-ref="pt"/>
<aop:after-returning method="afterReturning" pointcut-ref="pt"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>
<aop:around method="around" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
package com.zs.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;
@Component
public class AOPAdvice {
public void function(){
System.out.println("共性功能");
}
public void before(){
System.out.println("before");
}
public void after(){
System.out.println("after");
}
public void afterReturning(){
System.out.println("afterReturning");
}
public void afterThrowing(){
System.out.println("afterThrowing");
}
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("around");
Object proceed = pjp.proceed();
System.out.println("around");
return proceed;
}
}
通知中获取参数
通知中获取返回值
after-returning around
通知中获取异常对象
afterThrowing around
AOP配置(注解)(重点)
通知顺序
同一通知类中,相同通知类型以方法名排序为准 不同通知类中,以类名排序为准 使用@Order注解通过变更bean的加载顺序改变通知的加载顺序
企业开发经验
- 通知方法名由3部分组成,分别前缀、顺序编码、功能描述
- 前缀为固定字符串,例如baidu、itheima等,无实际意义
- 顺序编码为6位以内的整数,通常3位
- 功能描述,为方法对应的实际通知功能,例如exception、strLenCheck
综合案例 (重点)
package com.zs.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class RunTimeMonitorAdvice {
@Pointcut("execution(* com.zs.service.AccountService.find*(..))")
public void pt(){}
@Around("pt()")
public Object runtimeAround(ProceedingJoinPoint pjp) throws Throwable {
Signature signature = pjp.getSignature();
String className = signature.getDeclaringTypeName();
String methodName = signature.getName();
long startTime = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
pjp.proceed(pjp.getArgs());
}
long endTime = System.currentTimeMillis();
System.out.println(className + ":" + methodName + " 10000 run: " + (endTime - startTime) + "ms");
return null;
}
}
AOP底层原理 (重点)
装饰者模式
public interface DecoratorService {
void save();
}
package com.zs.service.impl;
import com.zs.domain.Account;
import com.zs.mapper.AccountDao;
import com.zs.service.AccountService;
import com.zs.service.DecoratorService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service("decoratorService")
public class DecoratorServiceImpl implements DecoratorService {
@Override
public void save() {
System.out.println("水泥墙");
}
}
package com.zs.base.decorator;
import com.zs.service.DecoratorService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component("decoratorServiceDecorator")
public class DecoratorServiceImplDecorator implements DecoratorService {
@Autowired
private DecoratorService decoratorService;
public DecoratorServiceImplDecorator(DecoratorService decoratorService) {
this.decoratorService = decoratorService;
}
@Override
public void save() {
decoratorService.save();
System.out.println("刮大白");
}
}
package com.zs.service;
import com.zs.base.decorator.DecoratorServiceImplDecorator;
import com.zs.domain.Account;
import config.SpringConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class DecoratorServiceTest {
@Autowired
private DecoratorService decoratorService;
@Autowired
private DecoratorService decoratorServiceDecorator;
@Test
public void test(){
decoratorServiceDecorator.save();
}
}
JDK Proxy
JDKProxy 是针对对象做代理,要求原始对象具有接口实现,并对接口方法进行增强
package com.zs.base.proxy;
import com.zs.service.DecoratorService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DecoratorServiceJDKProxy {
public static DecoratorService createDecoratorServiceJDKProxy(DecoratorService decoratorService){
ClassLoader cl = decoratorService.getClass().getClassLoader();
Class[] classes = decoratorService.getClass().getInterfaces();
InvocationHandler ih = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(decoratorService, args);
System.out.println("刷大白");
return invoke;
}
};
DecoratorService service = (DecoratorService) Proxy.newProxyInstance(cl,classes,ih);
return service;
}
}
CGLIBProxy
CGLIB,Code生成类库 不限定是否具有接口,可以对任意操作进行增强 无需原始被代理对象,动态创建出新的代理对象
package com.zs.base.cglib;
import com.zs.service.DecoratorService;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class DecoratorServiceImplCglibProxy {
public static DecoratorService createDecoratorServiceCglibProxy(Class clazz){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object ret = methodProxy.invokeSuper(o, args);
if (method.getName().equals("save")){
System.out.println("刮大白");
}
return ret;
}
});
return (DecoratorService) enhancer.create();
}
}
package com.zs.base.cglib;
import com.zs.service.DecoratorService;
import com.zs.service.impl.DecoratorServiceImpl;
public class app {
public static void main(String[] args) {
DecoratorService decoratorService = DecoratorServiceImplCglibProxy.createDecoratorServiceCglibProxy(DecoratorServiceImpl.class);
decoratorService.save();
}
}
spring 切换代理类型
注解: @EnableAspectJAutoProxy(proxyTargetClass = true) XML注解支持: <aop:aspectj-autoproxy proxy-target-class="false" /> XML配置 <aop: config proxy-target-class="false"></aop:config>
|