目录
一、什么是AOP
1、面向切面编程(方面),利用AOP可以对业务逻辑各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
2、通俗描述:不通过该修改源代码方式,在主干功能里面添加新功能
二、底层原理
1、AOP底层使用动态代理
(1)有两种动态情况动态代理
第一种 有接口情况,使用JDK动态代理
*创建接口实现类代理对象,增强类的方法
第二种 没有接口情况,使用CGLIB动态代理
*创建子类的代理对象,增强类的方法
AOP(JDK动态代理)
1、使用JDK动态代理,使用Proxy类里面的方法创建代理对象
(1)调用newProxyInstance(静态)方法
三个参数:类加载器,增强方法所在类实现的接口,实现这个接口InvocationHandler,创建代理对象,写增强方法
2、编写JDK动态代理代码
(1)创建接口,定义方法
public interface UserDao {
public int add(int a, int b);
public String update(String id);
}
(2)创建接口实现类,实现方法
package com.atguigu.spring5;
public class UserDaoImpl implements UserDao {
@Override
public int add(int a, int b) {
return a + b;
}
@Override
public String update(String id) {
return id;
}
}
(3)使用Proxy类创建接口的代理对象
package com.atguigu.spring5;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class JDKProxy {
public static void main(String[] args) {
Class[] interfaces = {UserDao.class};
//创建接口实现类代理对象
UserDao userDao = new UserDaoImpl();
UserDao dao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
int add = dao.add(1, 2);
System.out.println(add);
String update = dao.update("2007940408");
System.out.println(update);
}
}
//创建代理对象代码
class UserDaoProxy implements InvocationHandler{
//创建的是谁的代理对象,把它传进来便于增强
//有参构造器
private Object obj;
public UserDaoProxy(Object obj) {
this.obj = obj;
}
//增强的逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法之前
System.out.println("方法之前执行"+method.getName()+"传递的参数:"+ Arrays.toString(args));
//被增强的方法执行
Object res = method.invoke(obj, args);
//方法之后
System.out.println("方法之后执行"+obj);
return res;
}
}
执行结果如下:
三、AOP术语
1、连接点
类里面哪些方法可以被增强,这些方法称为连接点
2、切入点
实际被真正增强的方法
3.通知(增强)
(1)实际增强的逻辑部分称为通知
(2)通知有多种类型
*前置通知 @Before
*后置通知 @After
*环绕通知 @Around
*异常通知 @AfterThrowing
*最终通知 @AfterReturning
4、切面
是动作
(1)把通知应用到切入点过程
四、AOP操作(准备)
1、Spring框架一般基于AspectJ实现AOP操作
(1)什么是AspectJ
*AspectJ不是Spring组成部分,独立AOP框架,一般把AspectJ和Spring一起用,进行AOP操作
2、基于AspectJ实现AOP操作
(1)基于xml配置文件
(2)基于注解方式(使用)
3、在项目工程里引入AOP相关依赖
4、切入点表达式
(1)切入点表达式作用:知道对哪个类里的哪个方法进行增强
(2)语法结构:
execution(【权限修饰符】【返回类型】【全类名】【方法名称】【参数列表】)
*权限修饰符可省略,返回值可使用通配符*
举例1:对com.atguigu.dao.BookDao类里面的add进行增强
execution(* com.atguigu.dao.BookDao.add(..))
举例2:对com.atguigu.dao.BookDao类里面的所有方法进行增强
execution(* com.atguigu.dao.BookDao.*(..))
举例2:对com.atguigu.dao.包里面所有类和类里所有方法进行增强
execution(* com.atguigu.dao.*.*(..))
五、AOP操作
一、AspectJ注解
1、创建类,在类里面定义方法
public class User {
public void add(){
System.out.println("add....");
}
}
2、创建增强类(编写增强的逻辑)
(1)在增强类里面,创建方法,让不同的方法代表不同通知类型
package com.atguigu.spring5.aopanno;
//增强类
public class UserProxy {
//前置通知
public void before(){
System.out.println("before.....");
}
}
3、进行通知的配置
(1)在spring配置文件中,开启注解扫描
<context:component-scan base-package="com.atguigu"></context:component-scan>
(2)使用注解创建User和UserProxy对象
(3)在增强类上面添加注解@Aspect
(4)在spring配置文件中开启生成代理对象
<!-- 开启AspectJ生成代理对象--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
4、配置不同类型的通知
(1)在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置
//增强类
@Component
@Aspect
public class UserProxy {
//Before表示是前置通知
@Before(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void before(){
System.out.println("before.....");
}
}
5、相同的切入点抽取
//相同切入点的抽取
@Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void pointCutDemo(){
}
//Before表示是前置通知
@Before(value = "pointCutDemo()")
public void before(){
System.out.println("before.....");
}
6、有多个增强类对同一方法进行增强,设置增强类优先级
(1)在增强类上面添加注解@Order(数字类型值),值越小,优先级越高
//增强类
@Component
@Aspect
@Order(3)
public class UserProxy {
二、AspectJ配置文件
略
六、Spring的事务管理
1、事务操作简介
①、事务添加到JavaEE三层结构里面的Service层中,理论上也可以加到DAO层
②、Spring中进行事务管理的操作有两种方式:编程式和声明式
③、声明式事务包括xml方式和注解方式
④、在Spring,事务管理底层原理是AOP
⑤、Spring中提供了一个接口PlatformTransactionManager,代表事务管理器,它为不同的框架提供了不同的实现类,红框中时mybatis的实现类
2、声明式事务管理(注解方式)
1、注解实现
第一步首先创建数据源
第二步 为指定数据源配置事务管理器
第三步开启事务注解
这一步前提是在名称空间中引入事务的名称空间
然后开启事务注解,这里面要用到我们配置的事务管理器
最后在service类上添加事务注解即可实现
注意:
(1)@Transcational,这个注解添加到类上面,也可以添加到方法上面
(2)如果把这个注解添加到类上面,那么类里的所有方法都添加事务
(3)如果把这个注解添加到方法上面,那么为这个方法添加事务
2、参数配置
@Transcational注解中的参数列表如下:
①propagation 事务传播行为
②isolation 事务隔离级别
事务有隔离性
(4)不可重复读,一个未提交事务读取到另一提交事务修改数据
(5)幻读:一个未提交事务读到另一提交事务添加数据
通过设置事务隔离性可以解决三个读的问题
mysql默认是可重复读。
③timeout 超时时间
-1代表不超时
④rollbackFor 回滚
出现那些异常进行事务回滚
⑤noRollbackFor 不回滚
出现那些异常不进行事务回滚
⑥readOnly 是否只读
默认false,可以进行增删改查,而true表示只能进行查询操作
2、声明式事务管理(xml方式)
在spring配置文件中进行配置
第一步 配置事务管理器
第二步 配置通知
针对accountMoney方法添加事务
第三步 配置切入点和切面
3、声明式事务(完全注解方式)
创建配置类代替xml配置文件
配置类中写上数据库连接池,完整代码如下
1、创建配置类,使用配置类替代 xml 配置文件
@Configuration //配置类
@ComponentScan(basePackages = "com.atguigu") //组件扫描
@EnableTransactionManagement //开启事务
public class TxConfig {
//创建数据库连接池
@Bean
public DruidDataSource getDruidDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///user_db");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
//创建 JdbcTemplate 对象
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
//到 ioc 容器中根据类型找到 dataSource
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//注入 dataSource
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
//创建事务管理器
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
如此以来,即可使得@Transactional注解生效