动态代理学习:https://www.cnblogs.com/daniels/p/8242592.html
第一节
AOP基础
Aspect Oriented Programming (面向切面编程)
作用:1)一次解决一类问题
2)在不修改原有代码的基础上来扩展程序(开闭原则)
第一种 针对有接口的类,是采用jdk动态代理(横向扩展 接口-实现类)
第二种 针对无接口的类,是采用cglib代理(纵向扩展 继承)
名词:
关键的
Joinpoint(连接点):一个类当中的方法,或者说一个类里的哪些方法是可以被扩展或增强的
Pointcut(切入点):(从哪下手) 类里面有很多方法都可以被扩展或增加,但是我这次要扩展或增加的哪个方法 (事先写好的)
Advice(通知/增强):扩展或增强的逻辑(比如我在add方法前加日志,那么这个日志功能就叫通知或增强) 新写了一个方法—写的(后写的)
Aspect(切面)(配置实现):把增强应用到切点的过程。也就是把新写的方法应用到原有方法的过程 --配置的 (配置的 满足哪些条件的类或方法)
***** 通知或增强的类别(时机) (配置的)
1)前置增强:有原有方法执行前执行 before
2)后通知(After (finally) advice)最终:当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)
3)环绕:在方法前和后都可以执行他可以把原方法包起来 他需要拿到原有方法 around advice
无论方法是否正常结束, after通知都会被执行
4)返回后增强:有原有方法执行后执行 after-returning
5)抛出异常后通知:有原有方法执行产生异常 after-throwing
4或5只能执行一个
执行顺序参考
https://blog.csdn.net/qq_32331073/article/details/80596084
同一个aspect,不同advice的执行顺序:
注意:spring的5.2.7版本以后,执行顺序有所调整,具体的顺序以你选择的spring版本测试结果为准
try{
try{
//@Around
//@Before
method.invoke(..);
//@Around
}catch(){
throw.....;
}finally{
//@After
}
//@AfterReturning
}catch(){
//@AfterThrowing
}
①没有异常情况下的执行顺序:
@Around(before)-> @Before-> 目标方法-> @Around(after)-> @After-> @AfterReturning;
②有异常情况下的执行顺序:
@Around(before)-> @Before-> 目标方法->@Around(after)(如果没有写在finally块里就执行不到了)-> @After-> @AfterThrowing;
以下了解
Target:目标对象- 被增强方法所在的类
Waving:织入-把增强应用到目标的过程
Proxy:代理-一个类被aop织入增强后,就会在底层产生一个代理类
如果有接口的类-动态代理
没有接口的类-cglib代理
第2节 xml配置方式
工程spring study下
1)引jar包pom.xml里加入
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.1</version>
</dependency>
2)原有的dao类
package com.neuedu.dao.impl;
import org.springframework.stereotype.Repository;
import com.neuedu.dao.IUserDao;
@Repository("userDao")
public class UserDaoImpl implements IUserDao {
@Override
public int login(String username, String password) {
System.out.println("dao方法被调用");
return 1;
}
}
2)写一个用于增强的类
public class LogHandler{
private void logbegin(){//横切逻辑
System.out.println("---------------记录时间------------------"+System.currentTimeMillis());
}
private Object transaction(ProceedingJoinPoint jp) throws Throwable {
System.out.println("-------------事务开始-------------");
Object result = jp.proceed();
System.out.println("-------------事务结束-------------");
return result;
}
public void logError(){
System.out.println("异常了");
}
}
3)配置
修改头部定义
<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
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
写配置
<!-- 定义增强类,我计划把他织入到指定方法里面去 (普通的bean)-->
<bean id="logHandler" class="com.neuedu.springstudy.LogHandler"></bean>
<!-- 开始配置aop -->
<aop:config>
<!-- 定义一个切面,引用切面类-->
<aop:aspect ref="logHandler">
<!--定义通知和切入点-->
<!-- execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?) -->
<!--前置通知-->
<aop:before method="logbegin" pointcut="execution (* login*(..))"/>
<!--环绕通知-->
<aop:around method="transaction" pointcut="execution (* com.neuedu.dao.*.*(..))"/>
<!--异常通知-->
<aop:after-throwing method="logError" pointcut="execution(* com.neuedu.model.dao.impl.*.login*(..))"/>
</aop:aspect>
</aop:config>
常用的表达式
execution(访问修饰符 <返回值类型><方法名)(参数列表))
execution(* spring.aop.chap02.Book.add(..)) 表示对spring.aop.chap02. Book类的add方法进行增强
execution(* spring.aop.chap02.Book.*(..)) 表示对spring.aop.chap02. Book类的所有方法进行增强
execution(* *.*(..)) 表示对所有类的所有方法进行增强(不常用)
下面是用以指定字符开头的方法
execution(* save*(..))
execution(* update*(..))
execution(* del*(..))
注解扫描方式
注意开启代理开关 <aop:aspectj-autoproxy />
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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-4.1.xsd">
<!-- 在这里,配置包扫描,扫描model层所在的包 (service+dao) -->
<context:component-scan base-package="com.neuedu"></context:component-scan>
<!-- aop自动代理,加上这行后,会扫描带有@Aspect的注解 -->
<aop:aspectj-autoproxy />
</beans>
类的写法同下
第3节
第3节 java类注解方式
工程名:spring-java
1)引jar包改pom
2)拷贝刚才写的增强类loghandler
注意加必要的注解
@Aspect//该注解表示这个类是一个切面
@Component
public class LogHandler{
@Before("execution(* login*(..))")
private void logbegin(){//横切逻辑
System.out.println("---------------记录时间------------------"+System.currentTimeMillis());
}
@Around("execution(* com.neuedu.module.dao.*.*(..))")
private Object transaction(ProceedingJoinPoint jp) throws Throwable {
long begin=System.currentTimeMillis();
System.out.println("-------------事务开始-------------"+begin);
Object result = jp.proceed();
long end=System.currentTimeMillis();
System.out.println("-------------事务结束-------------"+end);
System.out.println("执行时间"+(end-begin));
return result;
}
}
3)修改ModuleConfig.java,AspectJ自动扫描支持
@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages={"com.neuedu"})
public class ModuleConfig {
}
4)运行原有测试类。看效果
public class SpringJTest {
@Test
public void test1(){
//加载配置文件 (AnnotationConfigApplicationContext注解配置文件方式)
ApplicationContext ctx=new AnnotationConfigApplicationContext(ModuleConfig.class);
//读取bean
IUserService service=(IUserService) ctx.getBean("userService");
//调用bean里的方法
service.login("陈昊", "123456");
}
}
目前学到的注解:
@Service @Repository @Component @Before @After @Around @Configuration @EnableAspectJAutoProxy @ComponentScan @Aspect @Bean @Autowired @Qulifired
作业:
1)看线上学习部分
2)做一套笔试题