springAOP
前言:
静态代理
静态代理类:
1、原则:和目标方法类功能一致且和目标类实现同样的接口。
2、缺点:- 往往在开发我们自己写的不仅仅是一个业务,两个业务,而我们的业务会有很多,如果每一个业务开发一个静态代理类,不仅没有减少我们的工作量,而且让我们的工作量多了很多。
- 如何解决这个问题?能不能为我们现有的业务层动态的创建代理类,通过动态代理去解决我们现有的业务层中的业务代码冗余问题。
动态代理
通过jdk提供的Proxy类,动态为现有的业务生成代理类。
代码示例:
/**
* JDK中的动态代理
* @author MOTUI
*
*/
public class TestUserServiceImplDynamicProxy {
public static void main(String[] args) {
final UserService userService = new UserServiceImpl();
//使用JDK提供的Proxy对象,用来产生动态代理类
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class<?>[] interfaces = {UserService.class};
InvocationHandler h = new InvocationHandler() {
//参数1:生成的代理对象
//参数2:当前调用的方法对象
//参数3:当前带哦啊用方法参数
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
try {
System.out.println("开启事务");
//调用真正的业务逻辑
Object invoke = method.invoke(userService, args);
System.out.println("提交事务");
return invoke;
} catch (Exception e) {
System.out.println("回滚事务");
e.printStackTrace();
}
return null;
}
};
//参数1:当前线程的类加载器 加载.class文件
//参数2:生成代理的对象的目标类的接口类型
//参数3:当通过代理对象去调方法时,会优先进入InvocationHander类的invoke方法
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(loader, interfaces, h);//返回静态代理对象
//userServiceProxy.save();
String query = userServiceProxy.query("小王");
System.out.println(query);
}
}
学习AOP需要知道几个重要的概念:
- 通知(Advice):处理目标方法以外的操作都称之为通知
- 切入点(PointCut):要为哪些类中的哪些方法加入通知
切面(Aspect): 通知 + 切入点
前置通知:
代码:public class RecordMethodNameBeforeAdvice implements MethodBeforeAdvice { /** * 参数1:当前目标类中调用的方法对象 * 参数2:当前目标类中调用的方法的参数 * 参数3:目标对象 */ @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("当前执行的方法:"+method.getName()); } }
配置文件:
<!-- 管理Bean --> <bean id="userService" class="com.motui.first.UserServiceImpl"/> <!-- 配置通知 --> <bean id="methodBeforeAdvice" class="com.motui.first.RecordMethodNameBeforeAdvice"></bean> <!-- 配置切面 --> <aop:config> <!-- 配置切入点 expression:指定哪个类的那个方法需要加入通知--> <aop:pointcut expression="execution(* com.motui.first.UserServiceImpl.*(..))" id="pc"/> <!-- 组合切入点和通知 --> <!-- advice-ref:告诉当前切面使用的是哪个通知 书写通知bean的id pointcut-ref:当前通知加入哪个切入点 --> <aop:advisor advice-ref="methodBeforeAdvice" pointcut-ref="pc"/> </aop:config>
后置通知:
代码:public class MyAfter implements AfterReturningAdvice ,ThrowsAdvice { //参数1: 目标类中当前执行的方法的返回值 //参数2: 目标类中当前执行的方法对象 //参数3: 目标类中当前执行的方法的参数 //参数4: 目标对象 @Override public void afterReturning(Object result, Method m, Object[] args, Object traget) throws Throwable { System.out.println("返回值:"+result); System.out.println("当前目标类的目标方法"+m.getName()); System.out.print("参数列表:"); if (args.length>0) { for (Object object : args) { System.out.println(object); } }else{ System.out.println("参数为空"); } System.out.println("目标:"+traget); } //出现异常时进入的通知方法 public void afterThrowing(Method method, Object[] args, Object target,Exception ex){ System.out.println("异常通知的方法的名称: "+method.getName()); System.out.println("异常通知的方法的参数: "+args); System.out.println("异常通知的目标对象"+target); System.out.println("异常通知的异常的信息 :" +ex.getMessage()); } }
配置文件:
<!-- 管理bean --> <bean id="deptService" class="com.motui.after.DeptServiceImpl"/> <bean id="myAfter" class="com.motui.after.MyAfter"/> <!-- 配置切入点 --> <aop:config> <aop:pointcut expression="execution(* com.motui.after.DeptServiceImpl.*(..))" id="pc"/> <aop:advisor advice-ref="myAfter" pointcut-ref="pc"/> </aop:config>
环绕通知:
代码:/** * 记录运行时长 * @author MOTUI * */ public class TestAroundAOP implements MethodInterceptor{ @Override public Object invoke(MethodInvocation mi) throws Throwable { System.out.println("记录运行时长"); long start = new Date().getTime(); Object proceed = mi.proceed();//返回值为目标方法的返回值,这个返回值在通知类中返回 long end = new Date().getTime(); System.out.println("运行时长:"+(end-start)); return proceed; } }
配置文件:
<!-- 管理Service --> <bean id="addressService" class="com.motui.around.AddressServiceImpl"/> <bean id="testAroundAOP" class="com.motui.around.TestAroundAOP"/> <!-- 配置切入点 --> <aop:config> <aop:pointcut expression="execution(* com.motui.around.AddressServiceImpl.*(..))" id="pc"/> <aop:advisor advice-ref="testAroundAOP" pointcut-ref="pc"/> </aop:config>
异常通知:
代码在后置通知中。
execution表达式:
execution(返回值类型 包名.类名.方法名(参数列表)) 示例: execution(* com.motui.service.impl.UserServiceImpl.save(..)) 返回值:任意 包:com.motui.service.impl 类:UserServiceImpl 方法:save 参数:任意 execution(* com.motui.service.impl.UserServiceImpl.*(..)) 返回值:任意 包:com.motui.service.impl 类:UserServiceImpl 方法:任意方法 参数:任意
在spring中有两种代理模式(JDK中的proxy和CGLIB):
在spring配置文件中可以通过proxy-target-class属性指定,true是CGLIB代理;false是Proxy代理
总结:
Spring的核心思想
IOC:控制反转 DI:依赖注入
AOP:面向切面的编程思想
AOP:通过动态为现有项目中的目标类创建代理对象,解决项目中的一些通用问题。
AOP几个重要概念:
通知(Advice):除了目标方法以外的操作称之为通知
切入点(Pointcut):项目中的哪些类的哪些方法需要加入通知
切面(Aspect):通知 + 切入点
AOP的底层实现
1、JDK的动态代理 Proxy 基于接口生成的代理
2、Spring CGBIL (CGLIB用来生成动态代理) 基于实现类生成的代理
3、JDK Proxy和CGLIB生成代理的区别?
Proxy根据接口生成动态代理对象,产生对象的返回值是接口类型
CGLIB根据实现类生成代理对象,产生对象的返回值是实现类类型(目标类型)