Spring
第四章
1、aop
2、spring与mybatis集成
3、spring中的事务处理
1、Aop—面向切面编程
通知类型
1、前置通知: 它是在业务逻辑代码执行之前由系统自动调用
2、后置通知: 它是在业务逻辑代码执行之后由系统自动调用
3、异常通知: 它是在业务逻辑代码执行过程中,产生异常时由系统自动调用
(它与后置通知属于平级关系,二者只执行其一)
4、环绕通知: 它相当于前置+后置通知,它会把业务代码的执行过程包含在通知中
1、前置通知
特点:前置通知中的代码,将会自动在业务逻辑代码之前自动调用
前置通知的要求:
1、通知类需要实现接口MethodBeforeAdvice
public class BeforeAdvice implements MethodBeforeAdvice {
}
2、重写接口的before方法
public void before(Method method, Object[] args, Object targetClass) throws Throwable {
}
2、后置通知
特点:前置通知中的代码,将会自动在业务逻辑代码之后自动调用
后置通知的要求:
1、通知类需要实现接口AfterReturningAdvice
public class AfterAdvice implements AfterReturningAdvice {
}
2、重写接口的afterReturning方法
public void afterReturning(Object returnValue, Method method, Object[] args, Object targetClass) throws Throwable {
}
//后置通知多了一个参数(返回的数据)
3、异常通知
(异常通知与后置通知属于平级,二者只会触发一个)
特点:异常通知中的代码,它会在业务逻辑类执行过程中产生异常时自动调用(其他类产生的异常,异常通知并不触发)
异常通知的要求:
1、通知类需要实现接口ThrowsAdvice
注意:这个接口比较特殊,接口中并没有方法,它是一个标识性接口,标识当前是一个异常通知
public class MyThrowsAdvice implements ThrowsAdvice {
}
2、编写方法(格式不能随便写)
public void afterThrowing(Method method, Object[] args, Object targetClass,Exception ex){
}
【实际应用】
1、编写MyThrowsAdvice类实现ThrowsAdvice接口
2、编写方法afterThrowing
public void afterThrowing(Method method, Object[] args, Object targetClass, Exception ex){
System.out.println("---------------------进入异常通知--------------------");
System.out.println("产生异常的业务逻辑类:"+targetClass.getClass());
System.out.println("产生异常的方法:"+method.getName());
System.out.println("产生异常的原因:"+ex.getMessage());
System.out.println("---------------------离开异常通知--------------------");
}
3、注册后置通知
<!--注册异常通知-->
<bean id="throws" class="org.java.advice.myThrowsAdvice"/>
4、在代理模式中配置异常
<!-- 2、代理附加的额外功能-->
<property name="interceptorNames">
<list>
<value>before</value>
<value>after</value>
<value>throws</value>
</list>
</property>
4、环绕通知
特点:环绕通知是把业务逻辑代码运行的整个过程,包含在通知中
环绕通知的要求:
1、通知类需要实现接口 MethodInterceptor
public class RoundAdvice implements MethodInterceptor {
}
2、重写接口的方法
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
return null;
}
【实际应用】
1、编写RoundAdvice类实现MethodInterceptor接口
2、重写接口的方法invoke
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println();
System.out.println("--------------------进入环绕通知-----------------------");
System.out.println("即将访问的业务类:"+methodInvocation.getThis().getClass());
System.out.println("即将访问的业务方法:"+methodInvocation.getMethod().getName());
System.out.println("即将传递的参数:"+ Arrays.toString(methodInvocation.getArguments()));
System.out.println("--------------------准备离开环绕通知,进入业务逻辑类-------");
System.out.println();
//此方法执行后,就会进入业务类逻辑类,返回的obj即为业务方法发返回结果
Object obj = methodInvocation.proceed();
System.out.println();
System.out.println("--------------------回到环绕通知,继续环绕-----------------------");
System.out.println("业务类返回的结果是:"+obj);
System.out.println("--------------------离开环绕通知-----------------------");
return obj;
}
3、注册后置通知
<!--注册环绕通知-->
<bean id="round" class="org.java.advice.roundAdvice"/>
4、在代理模式中配置环绕通知
//由于环绕通知就是前置通知+后置通知,因此,在附加功能上只需要配置环绕通知即可
<!--配置代理-->
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 1、指定代理的业务接口-->
<property name="proxyInterfaces">
<value>org.java.service.buyBookService</value>
</property>
<!-- 2、代理附加的额外功能-->
<property name="interceptorNames">
<list>
<value>throws</value>
<value>round</value>
</list>
</property>
<!--3、功能附加完成后要访问的业务类-->
<property name="target" ref="buyBookService"/>
</bean>
目前存在的问题
【问题】:
1、当前代理类只能对1个业务类进行代理,如果有1000个业务类,就需要配置1000个代理类
2、配置了代理类以后,只有访问代理类才能附加功能,如果直接访问业务类的名称,AOP的功能无法附加
解决方案
【解决方案1:配置自动代理】
特点:
1、不用单独的配置代理,系统会根据访问的业务类的名称自动生成代理
2、访问时,直接访问业务类的名称即可,不用访问代理的名称
缺点:
value里面的业务类还是需要写一千次
a、在applicationContext.xml文件配置自动代理
<!-- 配置自动代理-->
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<!--1、指定访问哪些业务类的时候,自动创建代理-->
<property name="beanNames">
<list>
<value>buyBookService</value>
<value>buyBookService2</value>
<value>buyBookService3</value>
</list>
</property>
<!--2、指定要代理要附加的功能-->
<property name="interceptorNames">
<list>
<value>before</value>
<value>after</value>
<value>mythrows</value>
</list>
</property>
</bean>
b、访问时,直接访问业务类的名称即可,不用访问代理类
BuyBookService bookService = (BuyBookService) cxt.getBean("buyBookService");
【解决方案2:配置声明式代理】
@@@特点:对指定包下面的所有业务类自动形成代理
1、编写通知类(注意:这种通知类不需要实现任何接口)
public class MyAdvice {
/**
* 前置通知
* JoinPoint:通过它可以获得当前要访问的业务类的名称 ,方法名,参数
*/
public void before(JoinPoint joinPoint){
System.out.println("-------进入前置通知------------");
System.out.println("即将访问的业务类是:"+joinPoint.getTarget().getClass());
System.out.println("即将访问的业务方法:"+joinPoint.getSignature().getName());
System.out.println("传递的参数有:"+ Arrays.toString(joinPoint.getArgs()));
System.out.println("-------离开前置通知------------");
}
//后置通知类
public void after(JoinPoint joinPoint, Object obj) {
//obj:返回值
System.out.println();
System.out.println("------------进入后置通知--------------------------");
System.out.println("访问的业务逻辑类:" + joinPoint.getTarget().getClass());
System.out.println("调用业务逻辑类中的哪一个方法:" + joinPoint.getSignature().getClass());
System.out.println("传递给方法的参数:" + Arrays.toString(joinPoint.getArgs