Spring详解(二)之AOP面向切面编程

本文深入解析Spring AOP的概念及应用,包括面向切面编程的思想、动态代理和CGLIB库的使用,以及如何利用Spring AOP进行事务管理,探讨了事务传播性和声明式事务的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

spring的AOP(面向切面)
SpringAOP 详解(aop思想,aop利用代理的想法,代理模式.以及Java提供的动态代理和CGLIB库提供的继承性代理)
SpringAOP 的事务管理(如何使用spring管理事务。事务的传播性)

SpringAop详解
Aop的核⼼思想叫做⾯向切⾯编程,它是⼀种⾯对横向业务流提出的⼀种解耦⽅案(何为aop,就好⽐法式⾯包.你想加点葡萄,花⽣仁什么的,你不可能因为你想吃什么,⽽重新做.你可以⽤到刀切成⼏块,在每块当中镶嵌进去)
可能会有⼈说,这已经改变的物理结构本⾝发⽣的变化,现实当中不好实现,但是软件开发中,我们作为上帝,那可以随时的组装和实现
如何实现?从这个例⼦来说,我们可以看到想⾃⼰想加什么就加什么,不想加的时候,又能随时⽅便的去掉.又不承担任何风险.现实当中,我们就要不断的去克隆和复制同⼀块物体,以保证风控程度是接近于0.所以在软件的设计开发当中,就是需要我们考虑的⼀个问题(如何解耦)
所以我们需要使⽤⼀种称作为代理的机制.

代理顾名思义便是帮XXX去相关的事,好比彩票你自己买不了,你需要要彩票的代理点帮你买
回过来,我们用专业的计算术语来解释,功能之间是否能够相互独立,能否保证代码不入侵,能达到同样的效果
代码当中体现,能不能尽量在不改动代码的情况下,只以添加类和删减类的插播方式,达到随用随取

代理模式的实现:
一共有3个人称出现.代理角色,真实角色,代理角色和抽象角色共同的行为方式。
代理角色有真实角色的行为引用
代码示例

/**
 * 代理角色和真实角色共同的行为
 * @author Yun
 *
 */
public interface BuyCaiPiao {
	void  buy500w();
}
/**
 * 真实角色
 * @author Yun
 *
 */
public class RealRole  implements  BuyCaiPiao{
	@Override
	public void buy500w() {
		// TODO Auto-generated method stub
		System.out.println("自己买彩票");
	}
}
/**
 * 代理角色,代理角色需要有真实角色的引用
 * @author Yun
 *
 */
public class ProxyRole implements BuyCaiPiao {
	private BuyCaiPiao bcp;
	public ProxyRole(BuyCaiPiao  bcp) {
		// TODO Auto-generated constructor stub
		this.bcp = bcp;
	}
	@Override
	public void buy500w() {
		// TODO Auto-generated method stub
		bcp.buy500w();
	}

}
public class TestProxy {
		public static void main(String[] args) {
			//构建真实角色
			RealRole  r  = new RealRole();
			//构建代理角色(传入需要被代理的角色)
			ProxyRole  p = new ProxyRole(r);
			//代理角色执行 (这件事是发生在代理身上的,也就意味着风险的承担方是代理角色)
			//从侧方面也可以看到(我们可以额外的在附加其它的动作,而这些都是发生在代理角色身上)
			//从代码方面看来,是否是我们尽量做类的代码增或者减,尽量的去避免源对象的改变
		    p.buy500w();
		}
}```

**代理模式的深入思考**
从上面的代码看,可以发现,如果我们想代理其它的真实角色,那么不说我们需要不断的去扩充接口,扩充真实的代理角色
是否有一种方式,可以动态的实现真实的角色想要实现的动作(其实就是利用我们的反射机制)

**1.3 jdk支持的动态代理方式(实现步骤)**
同样的构造接口和真实的角色

```java
/**
 * 行为接口
 * @author Yun
 *
 */
public interface InterPerson {
     void sayA();
     void sayB(String s);
}
/**
 * 真实的角色
 * @author Yun
 *
 */
public class RealPerson implements  InterPerson{

	@Override
	public void sayA() {
		// TODO Auto-generated method stub
		System.out.println("sayA");
	}

	@Override
	public void sayB(String s) {
		// TODO Auto-generated method stub
		System.out.println("sayB");
	}

}

实现动态代理特征接口

/**
 * 动态代理接口
 * @author Yun
 *
 */
public class Myhanlder implements  InvocationHandler{

	//需要被代理的真实角色
	 private Object  target;
	 
	 public Myhanlder(Object obj) {
		// TODO Auto-generated constructor stub
		 this.target  = obj;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// TODO Auto-generated method stub
		//执行真实角色拥有的方法 ,args代表如果有参数执行有参数
		Object  o  = method.invoke(target, args);
		return o;
	}
}

通过接口特征构造动态代理对象

public class TestJdkProxy {
		public static void main(String[] args) {
			//构建真实角色
			InterPerson  ip = new RealPerson();
			//通过proxy类进行代理角色创建
			InterPerson iproxy = (InterPerson) Proxy.newProxyInstance(InterPerson.class.getClassLoader(),new Class[] {InterPerson.class} , new Myhanlder(ip));
			iproxy.sayA();
			iproxy.sayB("ahha");
		}
}

使用cglib的方式
如果只有类,主要通过对字节码的操作.以继承的方式对原有类进行扩展
构建真实角色

public class NewRole {

	public void go(){
		System.out.println("回家");
	};
	public  void gohome(){
		System.out.println("回重庆");
	};
}

构建代理拦截器

public class CglibProxy implements MethodInterceptor {

	/**
	 * 通过拦截真实对象的方法
	 */
	@Override
	public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
		// TODO Auto-generated method stub
		Object obj = methodProxy.invokeSuper(o, objects);
		return obj;
	}

}

生成代理对象

public static void main(String[] args) {
		//构建一个增强类
		Enhancer  eh  = new Enhancer();
		// 增强类需要设置被代理的类型。以及代理的方法的回调
		eh.setSuperclass(NewRole.class);
		eh.setCallback(new CglibProxy());
		//构建代理类
		NewRole  nr = (NewRole) eh.create();
		nr.go();
		nr.gohome();
		
	}

1.5 spring利用aop机制的实现
名词概念:

Aspect:切面,由一系列切点、增强和引入组成的模块对象,可定义优先级,从而影响增强和引入的执行顺序
Join point:接入点,程序执行期的一个点,例如方法执行、类初始化、异常处理(一般用来获取方法中的一些元数据)
Advice:增强,切面在特定接入点的执行动作,包括 “around,” “before” and “after”等多种类型
Pointcut:切点,用来匹配特定接入点的谓词(表达式)
Weaving:织入,将一个或多个切面与类或对象链接在一起创建一个被增强对象(也就是构建代理对象的过程)
通知名词:

前置通知 在目标方法执行之前执行执行的通知
后置通知 在目标方法执行之后执行的通知 (出现异常便不再调用)
环绕通知 在目标方法执行之前和之后都可以执行额外代码的通知。
异常通知 在目标方法抛出异常时执行的通知
最终通知 是在目标方法执行之后执行的通知。
通知的应用场景:
在这里插入图片描述
实现方式:

构建真实角色

public class UserServiceImpl   implements  UserService{

	@Override
	public void save() {
		// TODO Auto-generated method stub
		System.out.println(1/0);
	}
}

定义增强类

package com.wwj.springaop;

import org.aspectj.lang.ProceedingJoinPoint;

public class Myadvice {
	public void before() {
		System.out.println(" 这是前置通知! ");
	}

	// 后置通知
	public void afterReturning() {
		System.out.println(" 这是后置通知 ( 如果出现异常不会调用 )");
	}

	// 环绕通知
	public Object around(ProceedingJoinPoint pjp) throws Throwable {
		System.out.println(" 这是环绕通知之前的部分! ");
		Object proceed = pjp.proceed();// 调用目标方法
		System.out.println(" 这是环绕通知之后的部分! ");
		return proceed;
	}

	// 异常通知
	public void afterException() {
		System.out.println(" 异常出现了! ");
	}

	// 最终通知
	public void after() {
		System.out.println(" 这是后置通知 ( 出现异常也会调用 )");
	}
}

配置xml 实例化角色 增强类 以及切面,和织入的过程

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://www.springframework.org/schema/beans" 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.2.xsd 
 http://www.springframework.org/schema/context 
 http://www.springframework.org/schema/context/spring-context-4.2.xsd 
 http://www.springframework.org/schema/aop 
 http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">

<!-- 1.构建需要被代理的对象 -->
<bean name="userService" class="com.wwj.springaop.UserServiceImpl"></bean>
<!-- 2. 构建需要增强的方法 -->
<bean name="myAdvice" class="com.wwj.springaop.Myadvice"></bean>
<!-- 3. 定义aop -->
	<!-- aop 配置 -->
	<aop:config>
		<!-- 自定义切入点 -->
		<!-- 
 1execution(): 表达式主体。

 2、第一个*号:表示返回类型,*号表示所有的类型。

 3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包。

 4、第二个*号:表示类名,*号表示所有的类。

 5*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。
 -->
		<aop:pointcut expression="execution(* com.wwj.springaop..*.*(..))" id="anyMethod"/>
		
		<aop:aspect ref="myAdvice">
			<!-- 前置通知 -->
			<!-- 测试自定义切入点 -->
			<aop:before method="before" pointcut-ref="anyMethod"/>
			<!-- 最终通知 -->
			<aop:after method="after" pointcut-ref="anyMethod"/>
			<!-- 后置通知 -->
			<aop:after-returning method="afterReturning" pointcut-ref="anyMethod"/>
			<!-- 环绕通知 -->
			<aop:around method="around" pointcut-ref="anyMethod"/>
			<!-- 异常抛出通知 -->
			<aop:after-throwing method="afterException" pointcut-ref="anyMethod"/>
		</aop:aspect>
	</aop:config>
</beans>

.测试执行

public static void main(String[] args) {
			ApplicationContext ac=new ClassPathXmlApplicationContext("application3.xml");
			UserService userService = (UserService) ac.getBean("userService");
			userService.save();
		}

SpringAOP进行事务管理 (如何使用spring管理事务。事务的传播性)
通过上面的例子,我们可以看到即将接触到的事务采用环绕通知的方式为最佳.因为全程都在跟进

何为事务的传播性

当服务被定义,就有可能会有事务的产生
服务和服务之间相互调用,就会产生我们称作为事务的传播
事务的传播存在于多个服务相互调用,要保证遵循满足首次产生事务的的ACID
举例说明

转账是一个服务,扣款是一个服务(转账的时候需要调用扣款的服务就构成了)
转账的时候,扣款出了问题,就需要回滚到首次事务,来保证事务的完整性
代码说明(未加入事务)

mysql数据库一张账户表(李四余额1000,张三余额1000) 张三转账给李四1000(动作)
代码示例

构建dao层以及接口

public interface AccountDao {

	void  updateAddMoney(int aid,int money);
	
	void  updateDeleteMoney(int aid,int money);
	  
}
//分隔

@Repository(value="acDao")
public class AccountDaoImpl  implements  AccountDao{

	@Resource
	private  JdbcTemplate  jdbcTemplate;
	
	@Override
	public void updateAddMoney(int aid, int money) {
		// TODO Auto-generated method stub
		jdbcTemplate.update("update t_account set money=money-? where aid=?",money,aid);
	}

	@Override
	public void updateDeleteMoney(int aid, int money) {
		// TODO Auto-generated method stub
		jdbcTemplate.update("update t_account set money=money+? where aid=?",money,aid);
	}

}

构建服务接口和接口实现
扣款

/**
 * 收款
 * @author Yun
 *
 */
public interface ReciveMoney {
    /**
     * 
     * @param aid  接收人id
     * @param money 接收人金额
     */
	void Recivemoney(int aid,int money);
}
//分隔
@Service("recs")
public class ReciveMoneyImpl  implements  ReciveMoney{
	
	@Autowired
	private  AccountDao  acDao;

	@Override
	public void Recivemoney(int aid, int money) {
		// TODO Auto-generated method stub
		acDao.updateDeleteMoney(aid, money);
	}

}

转账

/**
 * 
 * @author Yun
 * 扣款
 */
public interface PayMoney {

	/**
	 * 
	 * @param aid  扣款人id
	 * @param rid   收款人id
	 * @param money 扣款人金额
	 */
	void  paymoney(int aid,int money,int rid);
}
//分隔
@Service("pays")
public class PaymoneyImpl  implements  PayMoney{
	
	@Autowired
	private  AccountDao  acDao;
	
	@Autowired
	private  ReciveMoney  recs;

	@Override
	public void paymoney(int aid, int money,int rid) {
		acDao.updateAddMoney(aid, money);
		//构成了事务的传播性
		recs.Recivemoney(rid, money);
	}

}

测试

@Component(value="testTX")
public class TestSpringTX {
	
	@Autowired
	private  PayMoney  pays;
	
	public void test1(){
		pays.paymoney(1,1000, 2);
	}
	
	public static void main(String[] args) {
		
		ApplicationContext ac=new ClassPathXmlApplicationContext("application4.xml");
		TestSpringTX  tx   = (TestSpringTX) ac.getBean("testTX");
		tx.test1();
	}}

在这里插入图片描述
SpringAOP的事务管理(采用spring进行事务的管理)
spring管理事务2种方式
声明式事务 (全局管理事务 可以采用xml方式 或者是 @Transactional 注解的类级别支持和方法的级别)
编程式事务 (spring推荐使用TransactionTemplate)类似在jdbc中开启事务(了解)
一个功能是否要事务,必须纳入设计.编码考虑.不能仅仅完成了基本功能就ok

spring使用声明式事务
基于XML

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
                            http://www.springframework.org/schema/beans/spring-beans-4.2.xsd 
                            http://www.springframework.org/schema/context 
                            http://www.springframework.org/schema/context/spring-context-4.2.xsd 
                            http://www.springframework.org/schema/aop 
                            http://www.springframework.org/schema/aop/spring-aop-4.2.xsd 
                            http://www.springframework.org/schema/tx 
                            http://www.springframework.org/schema/tx/spring-tx-4.2.xsd ">
	<!-- 自动根据扫描对应包下面的注解,并实例化对应的对象 -->
	<context:component-scan base-package="com.wwj.springtx"></context:component-scan>

	<!-- 连接管理交给C3P0 -->
	<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring"></property>
		<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
		<property name="user" value="root"></property>
		<property name="password" value="root"></property>
	</bean>

	<!--JDBCTemplate 需要 datasource 连接池 -->
	<bean name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>

	<!-- 1使用spring事务管理管理数据源操作 -->
	<bean name="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>

	<!-- 2.定义需要被增强的方法,也就是那些需要介入事务的管理 -->
	<!-- 
propagation:REQUIRED(依赖) 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
isolation: 隔离级别  
read-only: 是否只读
	 -->
	<tx:advice id="advice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="pay*" propagation="REQUIRED" /><!-- *是对所有方法都加 -->
		</tx:attributes>
	</tx:advice>
	
<!-- 3.配置切入点,切哪 也就是 织入的过程 -->
<!-- 配置织入 -->
<aop:config  >
    <!-- 配置切点表达式 -->
    <aop:pointcut expression="execution(* com.wwj.springtx..*.*(..))" id="txPc"/>
    <!-- 配置切面 : 通知+切点
             advice-ref:通知的名称
             pointcut-ref:切点的名称
     -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPc" />
</aop:config>
</beans>

基于注解的方式进行
在这里插入图片描述
直接在方法或者类上面加:(注解方式)
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.REPEATABLE_READ,timeout=-1)
xml中加入

<!-- 1使用spring事务管理管理数据源操作 -->
	<bean name="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	  <!-- 支持事务注解 -->
   <tx:annotation-driven transaction-manager="transactionManager"/>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值