今日内容
- IOC原理
- AOP
- AOP概述以及相关的术语
- AOP使用
- AOP相关的配置
- 基于注解的AOP
1,IOC原理
1.1 组件扫描过滤器(掌握)
-
xml版(重点掌握)
<!--包扫描--> <context:component-scan base-package="com.itheima"> <!-- 不扫描@Controller注解 --> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
-
注解版
@ComponentScan( value = "com.itheima", excludeFilters = @ComponentScan.Filter( type= FilterType.ANNOTATION, classes = Service.class ) )
1.2 自定义导入器(了解)
作用:如果一个类没有使用 @Controller、@Component、@Service、@Repository ,还需要将给类装载到spring核心容器中,
可以使用自定义导入器。
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 3.加载import.properties文件中的多个类名
ResourceBundle bundle = ResourceBundle.getBundle("import");
String className = bundle.getString("className");
return className.split(",");
}
}
1.3 自定义注册器(了解)
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//自定义注册器
//1.开启类路径bean定义扫描器,需要参数bean定义注册器BeanDefinitionRegistry,需要制定是否使用默认类型过滤器
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry,false);
//2.添加包含性加载类型过滤器(可选,也可以设置为排除性加载类型过滤器)
scanner.addIncludeFilter(new TypeFilter() {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//所有匹配全部成功,此处应该添加实际的业务判定条件
return true;
}
});
//设置扫描路径
scanner.scan("com.itheima");
}
}
@Import注解的作用:
- 引入外部的配置类 (掌握)
- 引入自定义导入器 (了解)
- 引入自定义注册器 (了解)
2,AOP
课前说明:
如果需要对一个对象中的方法进行动态增强,我们使用动态代理。
现在学习AOP动态代理的代码我们不需要书写了,只需要进行配置即可。
2.1 概述
面向切面编程。是一种范式,一种编程思想。目的:插拔式组件。
spring实现了AOP的思想。
2.2 AOP术语
重点术语: 连接点 , 切入点 , 通知 , 切面
术语 | 含义 |
---|---|
连接点 | 业务类中所有方法 |
切入点 | 被抽取了共性功能的方法 |
通知 | 共性功能组成的方法 |
切面 | 描述的切入点与通知的关系 |
目标对象 | 原始类创建的对象 |
通知类型 | 通知的具体在原始功能之前还是之后的位置 |
织入 | 动态加入共性功能的工作 |
eg:
上图中的save、delete、update、findAll方法都是连接点。
save、update是切入点。
开启事务、提交|回滚事务组成的方法是通知。
UserService、OrderService、ProductService类对象是目标对象。
2.3 入门案例
-
引入坐标
<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>
-
开发业务功能
public class UserServiceImpl implements UserService { public void save(){ //0.将共性功能抽取出来 //System.out.println("共性功能"); System.out.println("user service running..."); } }
-
开发通知类
public class AOPAdvice { public void function(){ System.out.println("共性功能"); } }
-
进行aop的配置
<!--3.开启AOP命名空间--> <bean id="userService" class="com.itheima.service.impl.UserServiceImpl"/> <!--2.配置共性功能成功spring控制的资源--> <bean id="myAdvice" class="com.itheima.aop.AOPAdvice"/> <!--4.配置AOP--> <aop:config> <!--6.配置切面(切入点与通知的关系)--> <aop:aspect ref="myAdvice"> <!--7.配置具体的切入点对应通知中那个操作方法--> <aop:before method="function" pointcutf="execution(* *..*(..))"/> </aop:aspect> </aop:config>
-
测试
public class App { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService userService = (UserService) ctx.getBean("userService"); userService.save(); } }
入门案例执行的流程:
3,aop的配置(xml)
3.1 切入点表达式
作用: 匹配要增强的方法。
语法:
execution(【修饰符】 返回值类型 包名.类名.方法名(参数类型))
常用的切入点表达式
execution(* *..*(..))
execution(int *..*.*(..))
execution(* com..service.*.*(..))
execution(* com.itheima.service.*.*(..))
execution(User com.itheima.service.UserService.findById(..))
execution(User com.itheima.service.UserService.findById(int))
eg:编写一个切入点表达式,实现事务控制(只对增删改方法增强)
!execution(* com..service.*.find*(..))
3.2 切入点表达式放置的位置
3.3 通知类型
-
普通通知 : spring会自动的执行目标方法
- 前置通知 : 在目标方法执行前执行
- <aop:before>
- 后置通知 : 在目标方法执行后执行
- <aop:after>
- 返回后通知 : 在目标方法执行后执行,出现异常则不会执行
- <aop:after-returning>
- 异常通知 : 在目标方法执行时出现异常时执行
- <aop:after-throwing>
<aop:config> <aop:pointcut id="pt" expression="execution(* *..*(..))"/> <aop:aspect ref="myAdvice"> <aop:before method="before" pointcut-ref="pt"/> <aop:after method="after" pointcut-ref="pt"/> <aop:after-returning method="afterReturing" pointcut-ref="pt"/> <aop:after-throwing method="afterThrowing" pointcut-ref="pt"/> </aop:aspect> </aop:config>
- 前置通知 : 在目标方法执行前执行
-
环绕通知 : spring不会自动的执行目标方法,需要我们调用
- 可以实现上面四个通知的功能
- <aop:around>
<aop:config> <aop:pointcut id="pt" expression="execution(* *..*(..))"/> <aop:aspect ref="myAdvice"> <aop:around method="around" pointcut-ref="pt"/> </aop:aspect> </aop:config>
public Object around(ProceedingJoinPoint pjp) { System.out.println("around before..."); Object ret = null; try { //对原始方法的调用 ret = pjp.proceed(); } catch (Throwable throwable) { System.out.println("around...exception...."+throwable.getMessage()); } System.out.println("around after..."+ret); return ret; }
- 可以实现上面四个通知的功能
3.4 获取参数
在通知方法中获取目标方法的参数。
-
使用JoinPoint(掌握)
在通知方法上声明一个JoinPoint类型的参数,使用getArgs()方法获取参数。
public void before(JoinPoint jp){ //通过JoinPoint参数获取调用原始方法所携带的参数 Object[] args = jp.getArgs(); Object target = jp.getTarget();//目标对象 Signature signature = jp.getSignature(); String name = signature.getName();// 获取方法名称 System.out.println("before..."+args[0]); }
-
args (了解)
3.5 获取返回值
在通知方法中获取目标方法的返回值。
-
返回后通知
在通知方法上声明参数
public void afterReturing(Object ret){ System.out.println("afterReturing..."+ret); }
进行配置
<aop:config> <aop:pointcut id="pt" expression="execution(* *..*(..))"/> <aop:aspect ref="myAdvice"> <aop:after-returning method="afterReturing" pointcut-ref="pt" returning="ret"/> </aop:aspect> </aop:config>
-
环绕通知
public Object around(ProceedingJoinPoint pjp) { System.out.println("around before..."); Object ret = null; try { //对原始方法的调用 ret = pjp.proceed(); } catch (Throwable throwable) { System.out.println("around...exception...."+throwable.getMessage()); } System.out.println("around after..."+ret); return ret; }
作业:
进行日志记录。
Point pjp) {
System.out.println("around before...");
Object ret = null;
try {
//对原始方法的调用
ret = pjp.proceed();
} catch (Throwable throwable) {
System.out.println("around...exception...."+throwable.getMessage());
}
System.out.println("around after..."+ret);
return ret;
}
作业:
进行日志记录。
记录的信息包含 : 方法执行的时间点(2020-10-10 10:20:30),类的全类名,方法名,参数,返回值,异常