一:代理模式
代理模式简介:
代理模式是GoF23种常用设计模式之一,可以使用代理模式创建代理对象,让代理对象控制目标对象的访问,并且可以在不改变目标对象原有的逻辑功能之下添加一些额外的功能。
案例分析:
实现一个用户注册功能,现在要在原有注册功能之上添加一个打印日志的功能。为了不破坏原有的业务逻辑,我们可以使用代理模式。
1.静态代理实现:
代理对象与被代理对象必须实现同一接口,在代理对象中实现日志打印服务。
- UserSevice接口
public interface UserService { public boolean login(String userName,String password); }
- UserService实现类:
public class UserServiceImpl implements UserService{ @Override public boolean login(String userName, String password) { // TODO Auto-generated method stub boolean result="zs".equals(userName)&&"123".equals(password); System.out.println("原有业务执行结果是:"+result); return result; } }
- 打印日志工具类
public class LogUtil { public static void log() { System.out.println("日志:"+new Date().toLocaleString()); } }
- UserService代理类
public class UserProxy implements UserService{ private UserService userService; @Override public boolean login(String userName, String password) { userService=new UserServiceImpl(); boolean result=userService.login(userName, password); if(result) { LogUtil.log(); } return result; } }
- 测试类
public class Test { public static void main(String[] args) { UserProxy userProxy=new UserProxy(); System.out.println(userProxy.login("zs", "123")); } }
- 缺点:代理对象的接口只服务于一种类型的对象,有局限性
2.动态代理实现:
JDK1.3之后加入了实现动态代理的API,代理类实现InvocationHandler的接口就行。
- 代理类:
public class LoggerHandler implements InvocationHandler{ private Object delegate; public Object bind(Object delegate) { this.delegate=delegate; return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(), this); } @Override public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable { System.out.println(arg0.getClass().getName());//代理对象 System.out.println(arg1.getName());//代理对象的方法 System.out.println(arg2.toString());//方法中的参数 Object result=null; result=arg1.invoke(delegate, arg2); LogUtil.log(); return result; } }
- 测试类:
public class Test { public static void main(String[] args) { UserService us=new UserServiceImpl(); UserService proxy=(UserService) new LoggerHandler().bind(us); proxy.login("zs", "123"); } }
二:AOP概述
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
1.SpringAPI传统方式实现AOP
切面通过实现一些接口,来达到通知的作用。分为前置通知,后置通知,环绕通知,异常通知,最终通知。
业务类接口:
package com.user.service; public interface UserService { public boolean login(String userName, String pwd); public boolean regist(String userName, String pwd); }
业务实现类:
public class UserServiceImpl implements UserService { @Override public boolean login(String userName, String pwd) { System.out.println("目标对象中,账号密码是:" + userName + ":" + pwd); boolean result = "zs".equals(userName) && "202cb962ac59075b964b07152d234b70".equals(pwd); System.out.println("用户登录"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } int n = 10 / 0; return result; } @Override public boolean regist(String userName, String pwd) { System.out.println("目标对象中,账号密码是:" + userName + ":" + pwd); System.out.println("用户注册成功"); return true; } }
前置通知:
public class Md5Advice implements MethodBeforeAdvice { @Override public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { // TODO Auto-generated method stub System.out.println(arg0.getName());// 方法名称 System.out.println(arg1.toString());// 方法参数 System.out.println(arg2);// 被代理对象 String newPwd = Md5Encode.getMD5(arg1[1].toString().getBytes()); arg1[1] = newPwd; } }
后置通知:
public class ScoreAdvice implements AfterReturningAdvice { @Override public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable { System.out.println(arg0);// 方法返回的结果 System.out.println(arg1.getName());// 方法名 System.out.println(arg3);// 被代理对象 System.out.println("后置通知:用户登录成功,增加10个积分"); } }
环绕通知:
public class TimeAdvice implements MethodInterceptor { /** * 环绕通知 */ @Override public Object invoke(MethodInvocation arg0) throws Throwable { System.out.println("methodbefore:" + new Date().toLocaleString()); Object result = arg0.proceed();// 目标方法是否执行 System.out.println("methodafter:" + new Date().toLocaleString()); return result; } }
异常通知:
public class LoginExceptionAdvice implements ThrowsAdvice { public void afterThrowing(Exception e) { e.printStackTrace(); System.out.println("异常通知"); } }
配置文件:
<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 http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <bean id="UserServiceImpl" class="com.user.service.UserServiceImpl"></bean> <bean id="Md5Advice" class="com.advice.Md5Advice"></bean> <bean id="ScoreAdvice" class="com.advice.ScoreAdvice"></bean> <bean id="TimeAdvice" class="com.advice.TimeAdvice"></bean> <bean id="LoginExceptionAdvice" class="com.advice.LoginExceptionAdvice"></bean> <bean id="UserServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces" value="com.user.service.UserService"></property> <property name="target" ref="UserServiceImpl"></property> <property name="interceptorNames"> <list> <!-- <value>Md5Advice</value> <value>ScoreAdvice</value> --> <value>TimeAdvice</value> <value>LoginExceptionAdvice</value> </list> </property> </bean> </beans>
测试类:
public class Test { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); UserService us = (UserService) ctx.getBean("UserServiceProxy"); us.login("zs", "123"); // us.regist("zs", "123"); } }
2.Spring基于xml方式实现AOP
业务类:
public class Test { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); UserService us = (UserService) ctx.getBean("UserServiceProxy"); us.login("zs", "123"); // us.regist("zs", "123"); } }
通知类:
public class BeforeAdvice { /** * 切点 前置通知 * * @param joinPoint */ public void methodBefore(JoinPoint joinPoint) { System.out.println("前置通知"); System.out.println(joinPoint.getSignature().getName());// 代理方法名 System.out.println(joinPoint.getArgs());// 参数数组 System.out.println(joinPoint.getTarget());// 被代理对象 System.out.println("================"); } /** * 后置通知 * * @param joinPoint * @param result */ public void afterReturning(JoinPoint joinPoint, Object result) { System.out.println("后置通知"); System.out.println("================"); } /** * 环绕通知 * * @param joinPoint * @return */ public Object aroundMethod(ProceedingJoinPoint joinPoint) { System.out.println("环绕通知开始:"); System.out.println("================"); Object[] args = joinPoint.getArgs();// 方法参数 args[0] = args[0] + "456"; Object result = null; try { result = joinPoint.proceed(args);// 原方法是否执行 } catch (Throwable e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("环绕通知结束"); System.out.println("================"); return result; } /** * 异常通知 * * @param e */ public void throwExMethod(Exception e) { System.out.println("异常通知开始"); System.out.println("异常信息:" + e.getMessage()); System.out.println("================"); } /** * 最终通知 */ public void afterMethod(JoinPoint jointPoint) { System.out.println("最终通知"); System.out.println("================"); } }
配置文件:
<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" 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.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <bean id="UserService" class="com.UserService"></bean> <bean id="MyAdvice" class="Advice.BeforeAdvice"></bean> <aop:config> <aop:aspect id="MyAspect" ref="MyAdvice"> <aop:before method="methodBefore" pointcut="execution(* com.*.*(..))" /> <!-- com下的所有类型,所有参数 --> <aop:after-returning method="afterReturning" pointcut="execution(* com.*.*(..))" returning="result" /> <aop:around method="aroundMethod" pointcut="execution(* com.*.*(..))" /> <aop:after-throwing method="throwExMethod" pointcut="execution(* com.*.*(..))" throwing="e" /> <aop:after method="afterMethod" pointcut="execution(* com.*.*(..))" /> </aop:aspect> </aop:config> </beans>
测试类:
public class Test { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); UserService us = (UserService) ctx.getBean("UserService"); us.login("zs", "123"); } }
3.Spring基于注解方式实现AOP
业务类:
@Component("UserService") public class UserService { public boolean login(String userName, String passWord) { System.out.println("登录名是:" + userName); System.out.println("密码是:" + passWord); System.out.println("=============="); int n = 10 / 0; return true; } }
通知类:
@Component @Aspect public class BeforeAdvice { /** * 切点 前置通知 * * @param joinPoint */ @Before("execution(* com.*.*(..))") public void methodBefore(JoinPoint joinPoint) { System.out.println("前置通知"); System.out.println(joinPoint.getSignature().getName());// 代理方法名 System.out.println(joinPoint.getArgs());// 参数数组 System.out.println(joinPoint.getTarget());// 被代理对象 System.out.println("================"); } /** * 后置通知 * * @param joinPoint * @param result */ @AfterReturning(pointcut = "execution(* com.*.*(..))", returning = "result") public void afterReturning(JoinPoint joinPoint, Object result) { System.out.println("后置通知"); System.out.println("================"); } /** * 环绕通知 * * @param joinPoint * @return */ @Around("execution(* com.*.*(..))") public Object aroundMethod(ProceedingJoinPoint joinPoint) { System.out.println("环绕通知开始:"); System.out.println("================"); Object[] args = joinPoint.getArgs();// 方法参数 args[0] = args[0] + "456"; Object result = null; try { result = joinPoint.proceed(args);// 原方法是否执行 } catch (Throwable e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("环绕通知结束"); System.out.println("================"); return result; } /** * 异常通知 * * @param e */ @AfterThrowing(pointcut = "execution(* com.*.*(..))", throwing = "e") public void throwExMethod(Exception e) { System.out.println("异常通知开始"); System.out.println("异常信息:" + e.getMessage()); System.out.println("================"); } /** * 最终通知 */ @After("execution(* com.*.*(..))") public void afterMethod(JoinPoint jointPoint) { System.out.println("最终通知"); System.out.println("================"); } }
配置文件:
<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" 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.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <bean id="UserService" class="com.UserService"></bean> <bean id="MyAdvice" class="Advice.BeforeAdvice"></bean> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> <context:component-scan base-package="com"></context:component-scan> </beans>
测试类:
public class Test { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); UserService us = (UserService) ctx.getBean("UserService"); us.login("zs", "123"); } }