一:实验目的
1、 理解Spring AOP原理和基本概念;
2、掌握基于XML/注解方式的AOP编程;
二:实验内容
1、定义交易接口:
public interface Transaction{
public void income(float amount);//收入
public void expenditure(float amount);//支出
}
定义银行账号实现Transaction接口:
public class BankAccount implements Transaction{
private String account;//账号
private float balance;//存款余额
public void income(float amount) //提示收入xxx,当前账户余额xxx
{ … }
public void expenditure (float amount) //提示收入xxx,当前账户//余额xxx
{ … }
… //其他方法略
推荐课程:【黑马程序员2022新版SSM框架教程_Spring+SpringMVC+Maven高级+SpringBoot+MyBatisPlus企业实用开发技术】 https://www.bilibili.com/video/BV1Fi4y1S7ix?p=31&share_source=copy_web&vd_source=8613079943ff0493ec0d6d3985cc70cc
# 不会做的小伙伴记得多看几遍慢慢理解!
# 需要源码记得在评论下面留言哦!
2、基于XML实现AOP,使得账户余额变动前后进行信息提示;
在com.bankxml包下创建Transaction接口:
package com.bankxml;
public interface Transaction {
public void income(float amount);//收入
public void expenditure(float amount);//支出
public void setBalance(float balance);//设置balance的初始值
public void setAccount(String account);//设置account的初始值
}
在com.bankxml包下创建BankAccount实现类(实现Transaction接口):
package com.bankxml;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//@Component("BankAccount")
public class BankAccount implements Transaction {
// 有三种方法给属性赋初值,这里我采用基于setter的方式,其他两种在下面被注释了,
// 你们只需选中文本然后用快捷键ctrl+/就可以打开注解了,
// 不懂的小伙伴可以百度一下哦,多用快捷键可以提高编程效率
// @Value("dgut")
private String account;//账号
// @Value("123")
private float balance;//存款余额
//构造方法
// public BankAccount(String account, float balance) {
// this.account = account;
// this.balance = balance;
// }
//setter方法
public void setAccount(String account) {
this.account = account;
}
public void setBalance(float balance) {
this.balance = balance;
}
@Override
public void income(float amount) { //提示收入XXX,当前账户余额XXX
System.out.println(balance + "元");
balance += amount; //收入amount元
System.out.println("当前账户:" + account + ";收入:" + amount +"元");
System.out.print(balance + "元为");
}
@Override
public void expenditure(float amount) { 提示支出XXX,当前账户余额XXX
System.out.println(balance+"元");
if(amount > balance) {
System.out.println(balance);
System.out.println("余额不够");
}else {
balance -= amount;
System.out.println("当前账户:" + account + ";支出:" + amount + "元");
System.out.print(balance + "元为");
}
}
}
在com.bankxml包下创建XmlAdvice类:
package com.bankxml;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Pointcut;
public class XmlAdvice {
//基于XML方式
//此方法无返回值所以用void即可,若有返回值用Object
public void around (ProceedingJoinPoint pjp) throws Throwable { //提示收入XXX,当前账户余额XXX
System.out.print("账户余额未变动前为:");
pjp.proceed();
System.out.println("当前账户余额!!!");
}
}
在com.bankxml包下创建XmlAdviceTest测试类:
package com.bankxml;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Scanner;
public class XmlAdviceTest {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
Transaction transaction = (Transaction) ac.getBean("transaction");
Scanner sc = new Scanner(System.in);
System.out.println("请输入账户:");
String account = sc.next();
transaction.setAccount(account);
System.out.println("请输入初始金额:");
float balance = sc.nextFloat();
transaction.setBalance(balance);
while (true) {
System.out.println("请选择操作选项:1.存款,2.取款,3,退出");
String str = sc.next();
if (str.equals("3")) {
System.out.println("退出成功!");
break;
}
switch (str) {
case "1":
System.out.println("请输入存款金额:");
transaction.income(sc.nextFloat());
continue;
case "2":
System.out.println("请输入取款金额:");
transaction.expenditure(sc.nextFloat());
continue;
default:
System.out.println("输入错误,请重新输入!");
}
}
}
}
运行结果:
基于XML方式的xml文件配置:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--三种方法在xml文件中给属性赋初值(只是想复习一下实验一),但我不在xml文件中给属性赋初值,我直接让用户自己输入-->
<!--1、基于注解的装配方式,即给属性赋初值-->
<!-- <context:component-scan base-package="bankxml.BankAccount"></context:component-scan>-->
<!--基于XML方式配置-->
<!--BankAccount对象-->
<bean id="transaction" class="com.bankxml.BankAccount">
<!--2、基于setter方法注入,即给属性赋初值-->
<!-- <property name="account" value="dgut"></property><!–账号–>-->
<!-- <property name="balance" value="123"></property><!–存款余额–>-->
<!--3、基于构造方法注入,即给属性赋初值-->
<!-- <constructor-arg name="account" value="dgut"></constructor-arg><!–账号–>-->
<!-- <constructor-arg name="balance" value="123"></constructor-arg><!–存款余额–>-->
</bean>
<!--增强所在的类的对象,切面类对象-->
<bean id="xmlAdvice" class="com.bankxml.XmlAdvice"></bean>
<!--aop配置核心一句话:在 切面 中配置 切入点 和 增强的(通知) 关系-->
<!-- aop配置根标签,声明开始aop配置-->
<aop:config>
<!--全局的切入点-->
<!--这样子写感觉代码有点冗余(为了不影响用户输入账号和初始金额),但是目前想不到更好的方法,希望小伙伴们可以提点意见-->
<aop:pointcut id="pointcut1" expression="execution(* com.bankxml.Transaction.income(..))"></aop:pointcut>
<aop:pointcut id="pointcut2" expression="execution(* com.bankxml.Transaction.expenditure(..))"></aop:pointcut>
<!-- 这种写法有bug(即用户输出账户和初始金额界面受影响)-->
<!-- <aop:pointcut id="pointcut" expression="execution(* bankxml.BankAccount.*(..))"></aop:pointcut>-->
<!--配置切面-->
<aop:aspect ref="xmlAdvice">
<!--配置切入点与通知的关系-->
<!--环绕通知-->
<aop:around method="around" pointcut-ref="pointcut1"></aop:around>
<aop:around method="around" pointcut-ref="pointcut2"></aop:around>
</aop:aspect>
</aop:config>
</beans>
3、基于注解方式实现AOP,使得账户余额变动前后进行信息提示。
创建TransactionAnno接口:
package com.bankanno;
public interface TransactionAnno {
public void income(float amount);//收入
public void expenditure(float amount);//支出
public void setBalance(float balance);//设置balance的初始值
public void setAccount(String account);//设置account的初始值
}
创建BankAccountAnno实现类(实现TransactionAnno接口):
package com.bankanno;
import com.bankxml.Transaction;
public class BankAccountAnno implements TransactionAnno {
private String account;//账号
private float balance;//存款余额a
//setter方法
public void setAccount(String account) {
this.account = account;
}
public void setBalance(float balance) {
this.balance = balance;
}
@Override
public void income(float amount) { //提示收入XXX,当前账户余额XXX
System.out.println(balance + "元");
balance += amount; //收入amount元
System.out.println("当前账户:" + account + ";收入:" + amount +"元");
System.out.print(balance + "元为");
}
@Override
public void expenditure(float amount) { 提示支出XXX,当前账户余额XXX
System.out.println(balance+"元");
if(amount > balance) {
System.out.println(balance);
System.out.println("余额不够");
}else {
balance -= amount;
System.out.println("当前账户:" + account + ";支出:" + amount + "元");
System.out.print(balance + "元为");
}
}
}
创建AnnoAdvice类:
package com.bankanno;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class AnnoAdvice {
//基于注解方式
//切入点的配置,这样子写感觉代码有点冗余(为了不影响用户输入账号和初始金额),
// 但是目前想不到更好的方法,希望小伙伴们可以提点意见
@Pointcut("execution(* com.bankanno.BankAccountAnno.income(..))")
public void pt1(){
}
@Pointcut("execution(* com.bankanno.BankAccountAnno.expenditure(..))")
public void pt2(){
}
@Around("pt1()")
public void around1(ProceedingJoinPoint pjp1) throws Throwable {
System.out.print("账户余额未变动前为:");
pjp1.proceed();
System.out.println("当前账户余额!!!");
}
@Around("pt2()")
public void around2(ProceedingJoinPoint pjp2) throws Throwable {
System.out.print("账户余额未变动前为:");
pjp2.proceed();
System.out.println("当前账户余额!!!");
}
}
创建AnnoAdviceTest测试类:
package com.bankanno;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Scanner;
public class AnnoAdviceTest {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
TransactionAnno transactionAnno = (TransactionAnno) ac.getBean("transactionAnno");
Scanner sc = new Scanner(System.in);
System.out.println("请输入账户:");
String account = sc.next();
transactionAnno.setAccount(account);
System.out.println("请输入初始金额:");
float balance = sc.nextFloat();
transactionAnno.setBalance(balance);
while (true) {
System.out.println("请选择操作选项:1.存款,2.取款,3,退出");
String str = sc.next();
if (str.equals("3")) {
System.out.println("退出成功!");
break;
}
switch (str) {
case "1":
System.out.println("请输入存款金额:");
transactionAnno.income(sc.nextFloat());
continue;
case "2":
System.out.println("请输入取款金额:");
transactionAnno.expenditure(sc.nextFloat());
continue;
default:
System.out.println("输入错误,请重新输入!");
}
}
}
}
运行结果:
基于注解方式的xml文件配置:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--基于注解方式-->
<bean id="transactionAnno" class="com.bankanno.BankAccountAnno"></bean>
<bean id="annoadvice" class="com.bankanno.AnnoAdvice"></bean>
<!--开启注解aop-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>