AOP编程
什么是面向切面编程 AOP?
在软件业,AOP为Aspect 0riented Programming 的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP 是 OOP的延续,是软件开发中的一个热点,也是 Spring 框架中的一个重要内容,是函数式编程的一种衍生范型。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
Spring底层的AOP原理:
静态代理
静态代理案例
/*明星的工作业务内容
* */
public interface StarBussiness {
//面谈
public void face();
//签合同
public void contrank();
//预订行程
public void orderPlane();
//表演
public void sing();
//收款
public void getMoney();
}
/*描述明星的类(被代理的类)*/
public class Star implements StarBussiness{
@Override
public void face() {
}
@Override
public void contrank() {
}
@Override
public void orderPlane() {
}
//明星亲自做的 其他的交给经纪人完成
@Override
public void sing() {
System.out.println("明星亲自代言。。表演。。");
}
@Override
public void getMoney() {
}
}
/*描述经纪人的类(代理的类)*/
public class StarProxy implements StarBussiness{
//经纪人持有管理明星的工作作为属性
private Star star;
public StarProxy(Star star) {
this.star = star;
}
@Override
public void face() {
System.out.println("经纪人谈业务");
}
@Override
public void contrank() {
System.out.println("经纪人签合同");
}
@Override
public void orderPlane() {
System.out.println("经纪人订行程");
}
//明星亲自做的 其他的交给经纪人完成
@Override
public void sing() {
//明星自己做的事
star.sing();
}
@Override
public void getMoney() {
System.out.println("经纪人收款");
}
}
public class Demo {
public static void main(String[] args) {
//被代理对象
Star star = new Star();
//代理对象
StarProxy starProxy = new StarProxy(star);
//代理对象调用自己的方法
starProxy.face();
starProxy.contrank();
starProxy.orderPlane();
starProxy.sing();//这里实际执行的是被代理对象Star的工作
starProxy.getMoney();
}
}
动态代理
类实现了接口,Spring 就用 JDK动态代理,没有实现接口的,用 Cglib 动态代理,Spring底层可以自动切换
JDK 动态代理:
面向接口的,只能对实现了接口的类产生代理面
JDK 动态代理案例:
/*出租房屋的业务接口*/
public interface JdkProxyRent {
//出租业务
public void renting();
}
/*房东的房子(被代理的类)*/
public class HouseOwner implements JdkProxyRent{
@Override
public void renting() {
System.out.println("房东的房子要出租");
}
}
/*房产中介(切面类/代理类)*/
public class ZhongJie {
//以下的是增强的功能
//出租前
public void before(){
System.out.println("中介带客户看房");
}
//出租后
public void after(){
System.out.println("中介完成房东出租房子之后的服务");
}
}
/*代理增强被代理对象的功能*/
public class JdkProxyFactory {
//target 传入被代理的对象(房东的房子 HouseOwner houseOwner)
public static Object getProxyBean(Object target) {
//创建功能增强的对象(中介 ZhongJie)
ZhongJie zhongJie = new ZhongJie();
//获取要被代理的对象(target)的字节码文件(c=HouseOwner)
Class c = target.getClass();
return Proxy.newProxyInstance(c.getClassLoader(), c.getInterfaces(), new InvocationHandler() {
/*
* invoke方法是动态执行代理对象的方法
* proxy 是动态生成代理的代理对象(中介)
* method 目标方法的方法对象(renting方法的对象)
* args 是renting方法中的参数列表
* */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//开始完成代理(中介ZhongJie)对被代理(房东的房子HouseOwner houseOwner)对象的增强功能织入操作
zhongJie.before();
// target=房东的房子 HouseOwner
Object obj = method.invoke(target, args);
zhongJie.after();
return obj;
}
});
}
}
public class Demo {
public static void main(String[] args) {
JdkProxyRent jdkProxyRent=new HouseOwner();
//创建代理对象
JdkProxyRent zhongjie =(JdkProxyRent)JdkProxyFactory.getProxyBean(jdkProxyRent);
zhongjie.renting();//中介出租房子
}
}
Cglib 动态代理:
(类似于 Javassit,第三方代理技术) 对没有实现接口的类产生代理对象(生成子类对象)
Cglib动态代理案例:
/*出租房屋的业务接口*/
public interface CglibProxyRent {
//出租业务
public void renting();
}
/*房东的房子(被代理的类)*/
public class HouseOwner implements CglibProxyRent {
@Override
public void renting() {
System.out.println("二号房东的房子要出租");
}
}
/*房产中介(切面类/代理类)*/
public class ZhongJie {
//以下的是增强的功能
//出租前
public void before(){
System.out.println("中介带客户看房");
}
//出租后
public void after(){
System.out.println("中介完成房东出租房子之后的服务");
}
}
/*完成动态代理动作的类*/
public class CglibProxyFactory {
//target 传入被代理的对象(二号房东的房子 HouseOwner houseOwner)
public static Object getProxyBean(Object target) {
//创建代理对象(中介)
ZhongJie zhongJie = new ZhongJie();
//获取要被代理的对象(target)的字节码文件(c=HouseOwner)
Class c = target.getClass();
//Cglib提供的创建代理对象的模板对象
Enhancer enhancer = new Enhancer();
//把被代理对象(二号房东)设置为父类
enhancer.setSuperclass(c);
enhancer.setCallback(new MethodInterceptor() {
/*
* intercept方法是动态执行代理对象的方法
* o 代理对象
* method 目标方法的方法对象(renting方法的对象)
* args 是renting方法中的参数列表
* methodProxy 目标对象的方法对象的代理对象
* */
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//开始完成代理(中介ZhongJie)对被代理(房东的房子HouseOwner houseOwner)对象的增强功能织入操作
zhongJie.before();
// target=房东的房子 HouseOwner
Object obj = method.invoke(target, args);
zhongJie.after();
return obj;
}
});
return enhancer.create();//代理对象(是被代理对象的子类)
}
}
public class Demo {
public static void main(String[] args) {
CglibProxyRent cglibProxyRent=new HouseOwner();
//Cglib的代理对象
CglibProxyRent zhongjie =(CglibProxyRent) CglibProxyFactory.getProxyBean(cglibProxyRent);
zhongjie.renting();
}
}
Spring的AOP的编程案例
Spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--以下的标签是在当前文件被加载之后,扫描指定的包下所有的实体类-->
<context:component-scan base-package="com.aop.service"></context:component-scan>
<context:component-scan base-package="com.aop.aspect"></context:component-scan>
</beans>
aspect.xml
<?xml version="1.0" encoding="UTF-8"?>
<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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--当前的配置文件 是为了实现代理类与被代理类的织入操作-->
<aop:config>
<!--先获取被代理类中的方法save():这个方法有个专业的名字叫 切入点pointcut-->
<aop:pointcut id="p1" expression="execution(* com.aop.service.impl.UserServiceImpl.save(..))"/>
<!-- expression是表达式 *为通配符 通配返回值的任何类型 save(..)..代表save中的参数列表 无论有多少个-->
<!--切面:把(功能增强的类)中的check()方法织入在save()方法之前执行-->
<aop:aspect ref="myAspect">
<aop:after-returning method="check" pointcut-ref="p1"></aop:after-returning><!--p1代表save()-->
<!--before前置通知 表示check在 p1之前执行-->
<!--after-returning 表示check在 p1之后执行-->
</aop:aspect>
</aop:config>
</beans>
UserService
public interface UserService {
public void save();
}
UserServiceImpl
@Component//把当前类交给Spring容器管理
public class UserServiceImpl implements UserService {
//希望对当前的save()方法进行功能的增强(在这之前进行身份的校验check())
@Override
public void save() {
System.out.println("执行保存数据的操作逻辑。。。");
}
}
MyAspect
//当前类是一个功能增强类(切面类)
@Component
public class MyAspect {
//功能增强的方法
public void check(){
System.out.println("进行身份的校验工作。。。");
}
}
Demo
public class Demo {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("Spring.xml", "aspect.xml");
UserService userService =(UserService) ac.getBean("userServiceImpl");
userService.save();
}
}
Spring 的 AOP 开发的相关术语
1.通知(Advice)
就是你想要的功能,也就是上面说的 安全,事物,日志等。你给先定义好把,然后在想用的地方用一下。
2.连接点(yoinPoint)
spring 允许你使用通知的地方,基本每个方法的前,后(两者都有也行),或抛出异常时都可以是连接点,spring 只文持方法连接点.和方法有关的前后(抛出异常),都是连接点。
3.切入点(Pointcut)
上而说的连接点的基础上,来定义切入点,有5个方法,那就有5个连接占了,但是你并不想在所有方法附近都使用通知(使用叫织入),你只想让其中的几个,在调用这几个方法之前,之后或者抛出异常时干点什么,那么就用切点来定义这几个方法,让切点来筛选连接点,选中那几个你想要的方法。
4.切面(Aspect)
切面是通知和切入点的结合。通知说明了干什么和什么时候干(什么时候通过方法名中的 before,afer,around 等就能知道),而切入点说明了在哪干(指定到底是哪个方法),这就是一个完整的切面定义。
5.引入(introduction)
允许我们向现有的类添加新方法属性。就是把切面(也就是新方法属性:通知定义的)用到目标类中
6.目标(target)
引入中所提到的目标类,也就是要被通知的对象,也就是真正的业务逻辑,他可以在毫不知情的情况下,被咱们织入切面。而自己专注于业务本身的逻辑
7.代理(proxy)
怎么实现整套 aop,机制的.就是通过代理.织入增强功能后产生的代理对象
8.织入(weaving)
把切面应用到目标对象来创建新的代理对象的过程。有3种方式,spring采用的是运行时。
通知的类型
前置通知before:在目标方法执行之前的操作可以获取切入点的信息
后置通知after-returning:在目标方法执行之后的操作
带返回值的后置通知after-returning:在目标方法执行之后的操作,并获取目标方法的返回值
环绕通知 around:在目标方法执行之前和之后进行操作
扩展带参数:环绕通知around:在目标方法执行之前和之后进行操作
异常抛出通知 after-throwing: 在目标方法抛出异常时的操作(比如出现异常后进行回滚)。
最终通知 after:无论是否有异常都会执行的,相当于 try-catch-finally 中的 finally 块
基于xml通知的案例:
切入点接口
public interface UserService {
public void save();//前置通知
public void save2();//后置通知
public String saveBack();//后置通知带返回值
public void delete();//环绕通知
public void deleteParam(String name,String pwd);//环绕通知 带参数
public void update();//抛出异常通知
public void find();//最终通知
}
切入点
@Component//把当前类交给Spring容器管理
public class UserServiceImpl implements UserService {
//希望对当前的save()方法进行功能的增强(在这之前进行身份的校验check())
@Override
public void save() {
System.out.println("前置通知--执行保存数据的操作逻辑。。。");
}
@Override
public void save2(){
System.out.println("后置通知--执行保存数据的操作逻辑。。。");
}
@Override
public String saveBack(){
System.out.println("带返回值的后置通知--");
return "saveBack";//这个返回值能被MyAspect切面类中的backReturn方法通过obj接收到
}
@Override
public void delete() {
System.out.println("delete环绕通知");
}
@Override
public void deleteParam(String name, String pwd) {
System.out.println("测试带参数的环绕通知,接收参数");
}
@Override
public void update() {
System.out.println(1/0);
System.out.println("异常抛出通知:update操作");
}
@Override
public void find() {
System.out.println(1/0);//正常在没有try catch操作的情况下 在异常处会终止程序向下运行
System.out.println("最终通知----");
}
}
切面类(功能增强类)
//当前类是一个功能增强类(切面类)
@Component
public class MyAspect {
//功能增强的方法
//前置通知
public void check(){
System.out.println("1进行身份的校验工作。。。");
}
//后置通知
public void back(){
System.out.println("2进行身份的校验工作。。。");
}
//带返回值的后置通知
public void backReturn(Object obj){System.out.println("带返回值的后置通知接收到saveBack返回的:"+(String)obj);}
//环绕通知 ProceedingJoinPoint point是切入点(被增强的方法)
public void around(ProceedingJoinPoint point) throws Throwable {
//point接收切入点对象(delete方法)
System.out.println("环绕通知-----之前的操作");
point.proceed();//需要抛异常 运行delete方法
System.out.println("环绕通知-----之后的操作");
}
//带参数的环绕通知(应用场景:在转账之前判断账户是否有足够的余额)
public void aroundParam(ProceedingJoinPoint point) throws Throwable {
//point接收切入点对象(deleteParam方法)
System.out.println("环绕通知-----之前的操作");
point.proceed();//需要抛异常 运行deleteParam方法
//获取deleteParam的参数列表
Object[] args = point.getArgs();//[name,pwd]
System.out.println((String) args[0]);
System.out.println((String) args[1]);
System.out.println("环绕通知-----之后的操作");
}
//异常抛出通知 当切入点抛异常是通知才会触发 也可以接收异常 应用场景(例如数据库 有异常后回滚)
public void doException(Exception ex){
System.out.println("切入点抛异常了"+ex);
}
//最终通知 无论是否有异常都会执行
public void finallyDo(){
System.out.println("最终通知--无论是否有异常都会执行");
}
}
xml文件
<?xml version="1.0" encoding="UTF-8"?>
<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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--当前的配置文件 是为了实现代理类与被代理类的织入操作-->
<aop:config>
<!--先获取被代理类中的方法save():这个方法有个专业的名字叫 切入点pointcut-->
<aop:pointcut id="p1" expression="execution(* com.aop.service.impl.UserServiceImpl.save(..))"/>
<aop:pointcut id="p2" expression="execution(* com.aop.service.impl.UserServiceImpl.save2(..))"/>
<aop:pointcut id="p3" expression="execution(* com.aop.service.impl.UserServiceImpl.saveBack(..))"/>
<aop:pointcut id="p4" expression="execution(* com.aop.service.impl.UserServiceImpl.delete(..))"/>
<aop:pointcut id="p5" expression="execution(* com.aop.service.impl.UserServiceImpl.deleteParam(..))"/>
<aop:pointcut id="p6" expression="execution(* com.aop.service.impl.UserServiceImpl.update(..))"/>
<aop:pointcut id="p7" expression="execution(* com.aop.service.impl.UserServiceImpl.find(..))"/>
<aop:pointcut id="p8" expression="execution(* com.aop.service.impl.UserServiceImpl.*(..))"/>
<!--execution(* com.aop.service.impl.UserServiceImpl.*(..)) 以为最后以*通配符结尾 p8会应用到UserServiceImpl类下所有方法-->
<!-- expression是表达式 *为通配符 通配返回值的任何类型 save(..)..代表save中的参数列表 无论有多少个-->
<!--切面:把(功能增强的类)中的check()方法织入在save()方法之前执行-->
<aop:aspect ref="myAspect">
<!--before前置通知 表示check在 p1之前执行-->
<aop:before method="check" pointcut-ref="p1"></aop:before><!--p1代表save()-->
<!--after-returning后置通知 表示back在 p2之后执行-->
<aop:after-returning method="back" pointcut-ref="p2"></aop:after-returning>
<!--after-returning 带返回值的后置通知 同过returning="obj" obj名称取自backReturn形式参数名称 将p3的返回值传给backReturn-->
<aop:after-returning method="backReturn" pointcut-ref="p3" returning="obj"></aop:after-returning>
<!--around环绕通知 -->
<aop:around method="around" pointcut-ref="p4" ></aop:around>
<!--around环绕通知带参数:传递切入点的列表参数 -->
<aop:around method="aroundParam" pointcut-ref="p5" ></aop:around>
<!--after-throwing异常抛出通知 throwing="ex"用来接收异常 ex为形参名称 -->
<aop:after-throwing method="doException" pointcut-ref="p6" throwing="ex"></aop:after-throwing>
<!--最终通知-->
<aop:after method="finallyDo" pointcut-ref="p7"></aop:after>
<aop:after method="finallyDo" pointcut-ref="p8"></aop:after>
</aop:aspect>
</aop:config>
</beans>
基于注解通知的案例:
xml文件
<?xml version="1.0" encoding="UTF-8"?>
<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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启aop的注解开发 只需要加上该标签即可<aop:aspectj-autoproxy></aop:aspectj-autoproxy>-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
切入点接口
与基于xml通知的案例一致
切入点
与基于xml通知的案例一致
切面类(功能增强类)
//当前类是一个功能增强类(切面类)
@Component
@Aspect //该注解是告诉Spring容器 当前类是一个功能增强类(切面类)
public class MyAspect {
//功能增强的方法
//前置通知
@Before(value="execution(* com.aop.service.impl.UserServiceImpl.save(..))")
public void check(){
System.out.println("1进行身份的校验工作。。。");
}
//后置通知
@AfterReturning(value ="execution(* com.aop.service.impl.UserServiceImpl.save2(..))" )
public void back(){
System.out.println("2进行身份的校验工作。。。");
}
//带返回值的后置通知
@AfterReturning(value ="execution(* com.aop.service.impl.UserServiceImpl.saveBack(..))",returning="obj")
public void backReturn(Object obj){System.out.println("带返回值的后置通知接收到saveBack返回的:"+(String)obj);}
//环绕通知 ProceedingJoinPoint point是切入点(被增强的方法)
@Around(value ="execution(* com.aop.service.impl.UserServiceImpl.delete(..))")
public void around(ProceedingJoinPoint point) throws Throwable {
//point接收切入点对象(delete方法)
System.out.println("环绕通知-----之前的操作");
point.proceed();//需要抛异常 运行delete方法
System.out.println("环绕通知-----之后的操作");
}
//带参数的环绕通知(应用场景:在转账之前判断账户是否有足够的余额)
@Around(value ="execution(* com.aop.service.impl.UserServiceImpl.deleteParam(..))")
public void aroundParam(ProceedingJoinPoint point) throws Throwable {
//point接收切入点对象(deleteParam方法)
System.out.println("环绕通知-----之前的操作");
point.proceed();//需要抛异常 运行deleteParam方法
//获取deleteParam的参数列表
Object[] args = point.getArgs();//[name,pwd]
System.out.println((String) args[0]);
System.out.println((String) args[1]);
System.out.println("环绕通知-----之后的操作");
}
//异常抛出通知 当切入点抛异常是通知才会触发 也可以接收异常 应用场景(例如数据库 有异常后回滚)
@AfterThrowing(value ="execution(* com.aop.service.impl.UserServiceImpl.update(..))",throwing ="ex")
public void doException(Exception ex){
System.out.println("切入点抛异常了"+ex);
}
//最终通知 无论是否有异常都会执行
@After(value ="execution(* com.aop.service.impl.UserServiceImpl.find(..))")
public void finallyDo(){
System.out.println("最终通知--无论是否有异常都会执行");
}
//最终通知 无论是否有异常都会执行
@After(value ="execution(* com.aop.service.impl.UserServiceImpl.*(..))")
public void finallyAll(){
System.out.println("最终通知:UserServiceImpl下所有方法");
}
}
语法
execution([访问修饰符]方法返回值 包名.类名.方法名(参数))
com.spring.service.UserServiceImpl. add(..)
com.spring.service.UserServiceImpl.*(..) 开发中用的最多的是这种,对当前类下所有的方法做增强处理(场景:事务处理)
com.spring.service. impl.add(..)
com.spring.service.impl.*(..)表示:com.spring.service.impl 类下所有方法被增
* *.*.service.impl.add(..)没有包可以用*代替
*com.spring.*.*(..)表示 com.spring 包下所有的类,所有方法都被增强
Spring整合MyBatis实现登录与转账
Spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--引入含有数据库参数的外部文件-->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="${mysql.url}"></property>
<property name="username" value="${mysql.username}"></property>
<property name="password" value="${mysql.password}"></property>
<property name="driverClassName" value="${mysql.driver}"></property>
</bean>
<!--把mybatis交给spring管理-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--扫描mapper下的所有接口-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.spring.mapper"></property>
</bean>
<!--扫描service下所有的类-->
<context:component-scan base-package="com.spring.service"></context:component-scan>
<context:component-scan base-package="com.spring.aspect"></context:component-scan>
</beans>
aspect.xml
<?xml version="1.0" encoding="UTF-8"?>
<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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启aop的注解开发 只需要加上该标签即可<aop:aspectj-autoproxy></aop:aspectj-autoproxy>-->
<aop:config>
<!--获取切入点-->
<aop:pointcut id="p1" expression="execution(* com.spring.service.impl.AccountServiceImpl.transferAccount(..))"/>
<aop:aspect ref="myAspect">
<!--告诉spring将切入点p1交给checkMoney中的方法point接收-->
<aop:around method="checkMoney" pointcut-ref="p1"></aop:around>
</aop:aspect>
</aop:config>
<!--被增强类在有接口的情况下默认是jdk动态代理 也可以强制切换为cglib动态代理-->
<!--强制切换为cglib动态代理(代理对象是被代理对象的子类) 默认proxy-target-class="false"-->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>
Tuser
public class Tuser implements Serializable {
private int id;
private String uname;
private String pwd;
private int aid;
public Tuser() {
}
public Tuser(int id, String uname, String pwd, int aid) {
this.id = id;
this.uname = uname;
this.pwd = pwd;
this.aid = aid;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public int getAid() {
return aid;
}
public void setAid(int aid) {
this.aid = aid;
}
@Override
public String toString() {
return "Tuser{" +
"id=" + id +
", uname='" + uname + '\'' +
", pwd='" + pwd + '\'' +
", aid=" + aid +
'}';
}
}
Account
public class Account implements Serializable {
private int aid;
private int money;
public Account() {
}
public Account(int aid, int money) {
this.aid = aid;
this.money = money;
}
public int getAid() {
return aid;
}
public void setAid(int aid) {
this.aid = aid;
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"aid=" + aid +
", money=" + money +
'}';
}
}
UserService
public interface UserService {
public Tuser loginUser(String uname, String pwd);
}
AccountService
public interface AccountService {
/*
* out 转出账户id
* in 转入账户id
* money 转账金额
* */
public int transferAccount(int out,int in,int money);
}
UserMapper
@Repository//与@Component用处一致 都是把该类交给Spring容器管理
public interface UserMapper {
@Select("select * from t_user where uname=#{param1} and pwd=#{param2}")
public Tuser loginUser(String uname, String pwd);
}
AccountMapper
@Repository
public interface AccountMapper {
/*
* out 转出账户id
* in 转入账户id
* money 转账金额
* */
@Update("update account set money=money-#{param2} where aid=#{param1}")
public int transferOut(int out,int money);
@Update("update account set money=money+#{param2} where aid=#{param1}")
public int transferIn(int in,int money);
@Select("select money from account where aid=#{param1}")
public int getMoney(int aid);
}
UserServiceImpl
@Service//与@Component用处一致 都是把该类交给Spring容器管理
public class UserServiceImpl implements UserService {
//使用UserMapper接口对象
//@Autowired //根据类型注入 有spring提供
@Resource//有jdk提供
private UserMapper userMapper;
@Override
public Tuser loginUser(String uname, String pwd) {
Tuser tuser = userMapper.loginUser(uname, pwd);
return tuser;
}
}
AccountServiceImpl
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountMapper accountMapper;
@Override
public int transferAccount(int out, int in, int money) {
int n1= accountMapper.transferOut(out, money);
//模拟网络通信异常
System.out.println(1/0);//扣款成功 进账失败
//需要进行Spring事物处理
int n2= accountMapper.transferIn(in, money);
if ((n1+n2)==2){
return 1;
}
return 0;
}
//需求:当前需要对AccountServiceImpl类进行功能增强 提前做余额校验(out中的money>=转账额度)
}
MyAspect
/*
* 切面类(功能增强类)
* */
@Component
public class MyAspect {
//注入AccountMapper
@Autowired
private AccountMapper accountMapper;
public int checkMoney(ProceedingJoinPoint point){//point使用动态代理获取到AccountServiceImpl的transferAccount方法
Object[] args = point.getArgs();//获取AccountServiceImpl的参数列表 [out,in,money]
//获取转出人的id
Integer aid =(Integer)args[0];
//获取转账金额money
Integer money =(Integer)args[2];
//根据aid查询余额money
int money1 = accountMapper.getMoney(aid);
if (money1>=money){
//out中的money>=转账额度
try {
Integer proceed =(Integer)point.proceed();
return 1;
} catch (Throwable e) {
throw new RuntimeException();
}
}else {
//throw new RuntimeException("余额不足");//抛出异常的地方 程序会终止继续运行
//使用自定义异常
throw new MoneyException("余额不足");
}
}
}
MoneyException
public class MoneyException extends RuntimeException {
public MoneyException() {
super();
}
public MoneyException(String mag) {
super(mag);
}
}
Demo
public class Demo {
ApplicationContext ac;
public Demo(){
ac = new ClassPathXmlApplicationContext("Spring.xml","aspect.xml");
}
@Test
public void login(){
UserService userServiceImpl = (UserService)ac.getBean("userServiceImpl");
Tuser u= userServiceImpl.loginUser("张三", "333");
if (u!=null){
System.out.println("登录成功,欢迎"+u.getUname());
}else{
System.out.println("登录失败,用户名或密码有误");
}
}
//模拟转账处理
@Test
public void transfer(){
//一定要使用接口的引用指向接口的实现对象
AccountService t =(AccountService)ac.getBean("accountServiceImpl");
int i = t.transferAccount(2, 1, 10000);
System.out.println(i==1?"转账成功":"转账失败");
}
}
Spring的事物管理
什么是事物
事物:逻辑上的一组操作 组成这组操作的各个单元 要么全部成功 要么全部失败
Spring的事物管理的API
PlatformTransactionManager:平台事物管理器
平台事物管理器:接口 是spring用来管理事物的真正的对象
DataSourceTransactionManager:底层使用JDBC事物管理
TransactionDefinition:事物定义信息
事物定义:用于定义事物的相关的信息,隔离级别 超时信息 传播行为(DML) 是否只读(查询)
如果遇到特别复杂的业务逻辑,有可能出现业务层之间的方法互相调用造成事务嵌套事务的传播行为主要用来解决这个问题
Spring 中提供了七种事务的传播行为:
保证多个操作在同一个事务中
PROPAGATON_REQUIRED:默认值(一般都只要用这个),如果A中有事务,使用A中的事务,如果A没有,创建一个新的事务,将操作包含进来
PROPAGATON_SUPPORTS:支持事务,如果A中有事务,使用A中的事务。如果A没有事务,不使用事务。
PROPAGATON_MANDATORY:如果A中有事务,使用A中的事务。如果A没有事务,抛出异常。
保证多个操作不在同一个事务中
PROPAGATON_REQUIRES_NEW :如果A中有事务,将A的事务挂起(暂停),创建新事务,只包含自身
PROPAGATION_NOT_SUPPORTED:如果A中有事务,将A的事务挂起。不使用事务管理。
PROPAGATON_NEVER:如果A中有事务,报异常。
嵌套式事务
PROPAGATION_NESTED:嵌套事务,如果A中有事务,按照A的事务执行,执行完成后,设置
个保存点,执行B中的操作,如果没有异常,执行通过,如果有异常,可以选择回滚到最初始
置,也可以回滚到保存点。
Spring事物管理案例
注解方式的声明式事物管理
第一步:引入AOP的开发包
第二步:恢复转账环境
第三步:配置事物管理器
transc.xml
<?xml version="1.0" encoding="utf-8"?>
<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:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置事物管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启注解事物-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
第四步:在service层添加注解
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountMapper accountMapper;
@Override
//加入事物处理
@Transactional(propagation = Propagation.REQUIRED)//事物注释PROPAGATON_REQUIRED
public int transferAccount(int out, int in, int money) {
int n1= accountMapper.transferOut(out, money);//a操作 要保证ab操作在同一事物中
//模拟网络通信异常
/* try {
扣款成功 进账失败
spring的事物处理需要注意:
spring通过捕获异常来触发事物回滚
如果手动把异常catch到 那么spring的事物控制就失效了
}catch (Exception e) {
System.out.println(e);
}*/
System.out.println(1/0);//测试完一定要恢复转账环境
int n2= accountMapper.transferIn(in, money);//b操作
if ((n1+n2)==2){
return 1;
}
return 0;
}
//需求:当前需要对AccountServiceImpl类进行功能增强 提前做余额校验(out中的money>=转账额度)
}