Spring AOP
IT小郭. 人气:0一、什么是代理?
指为一个目标对象提供一个代理对象, 并由代理对象控制对目标对象的引用. 使用代理对象, 是为了在不修改目标对象的基础上,增强目标对象的业务逻辑.
1、静态代理
静态代理的特点是, 为每一个业务增强都提供一个代理类, 由代理类来创建代理对象. 下面我们通过静态代理来实现对转账业务进行身份验证.
(1) 转账业务
public interface IAccountService { //主业务逻辑: 转账 void transfer(); } public class AccountServiceImpl implements IAccountService { @Override public void transfer() { System.out.println("调用dao层,完成转账主业务."); } }
(2) 代理类
public class AccountProxy implements IAccountService { //目标对象 private IAccountService target; public AccountProxy(IAccountService target) { this.target = target; } /** * 代理方法,实现对目标方法的功能增强 */ @Override public void transfer() { before(); target.transfer(); } /** * 前置增强 */ private void before() { System.out.println("对转账人身份进行验证."); } }
(3) 测试
public class Client { public static void main(String[] args) { //创建目标对象 IAccountService target = new AccountServiceImpl(); //创建代理对象 AccountProxy proxy = new AccountProxy(target); proxy.transfer(); } }
结果: 对转账人身份进行验证.调用dao层,完成转账主业务.
2、动态代理
静态代理会为每一个业务增强都提供一个代理类, 由代理类来创建代理对象, 而动态代理并不存在代理类, 代理对象直接由代理生成工具动态生成.
2.1、JDK动态代理
JDK动态代理是使用 java.lang.reflect 包下的代理类来实现. JDK动态代理动态代理必须要有接口.
(1) 转账业务
public interface IAccountService { //主业务逻辑: 转账 void transfer(); } public class AccountServiceImpl implements IAccountService { @Override public void transfer() { System.out.println("调用dao层,完成转账主业务."); } }
(2) 增强
因为这里没有配置切入点, 称为切面会有点奇怪, 所以称为增强.
public class AccountAdvice implements InvocationHandler { //目标对象 private IAccountService target; public AccountAdvice(IAccountService target) { this.target = target; } /** * 代理方法, 每次调用目标方法时都会进到这里 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); return method.invoke(target, args); } /** * 前置增强 */ private void before() { System.out.println("对转账人身份进行验证."); } }
(3) 测试
public class Client { public static void main(String[] args) { //创建目标对象 IAccountService target = new AccountServiceImpl(); //创建代理对象 IAccountService proxy = (IAccountService) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new AccountAdvice(target) ); proxy.transfer(); } }
结果: 对转账人身份进行验证.调用dao层,完成转账主业务.
2.2、 CGLIB
动态代理
JDK动态代理必须要有接口, 但如果要代理一个没有接口的类该怎么办呢? 这时我们可以使用CGLIB动态代理. CGLIB动态代理的原理是生成目标类的子类, 这个子类对象就是代理对象, 代理对象是被增强过的.
注意: 不管有没有接口都可以使用CGLIB动态代理, 而不是只有在无接口的情况下才能使用
(1) 转账业务
public class AccountService { public void transfer() { System.out.println("调用dao层,完成转账主业务."); } }
(2) 增强
因为这里没有配置切入点, 称为切面会有点奇怪, 所以称为增强.
public class AccountAdvice implements MethodInterceptor { /** * 代理方法, 每次调用目标方法时都会进到这里 */ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { before(); return methodProxy.invokeSuper(obj, args); // return method.invoke(obj, args); 这种也行 } /** * 前置增强 */ private void before() { System.out.println("对转账人身份进行验证."); } }
(3) 测试
public class Client { public static void main(String[] args) { //创建目标对象 AccountService target = new AccountService(); // //创建代理对象 AccountService proxy = (AccountService) Enhancer.create(target.getClass(), new AccountAdvice()); proxy.transfer(); } }
结果: 对转账人身份进行验证.调用dao层,完成转账主业务.
二、模拟Spring AOP场景
了解了动态代理后, 我们就可以自己来实现Spring AOP功能了, 所以下面我们来模拟下Spring AOP场景.
(1) 转账业务
public interface IAccountService { //主业务逻辑: 转账 void transfer(); } public class AccountServiceImpl implements IAccountService { @Override public void transfer() { System.out.println("调用dao层,完成转账主业务."); } }
(2) 切面抽象类
定义一个切面抽象类, 该类使用了模板方法的设计模式, 为开始, 结束, 异常, 前置增强, 后置增强提供了默认实现, 当我们定义切面类时只需要按需重写它们就行. isIntercept() 方法用来判断切入点是否正确, 切面类需要重写这个方法.
public abstract class BaseAspect implements MethodInterceptor { private static final Logger logger = LoggerFactory.getLogger(BaseAspect.class); @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object result = null; begin(); try { if (isIntercept(method, args)) { before(); result = methodProxy.invokeSuper(obj, args); after(); } else { result = methodProxy.invokeSuper(obj,args); } } catch (Exception e) { logger.error("proxy failure", e); error(e); throw e; } finally { end(); } return result; } /** * 开始增强 */ public void begin() { } /** * 切入点判断 */ public boolean isIntercept(Method method, Object[] args) throws Throwable { return true; } /** * 前置增强 */ public void before() throws Throwable { } /** * 后置增强 */ public void after() throws Throwable { } /** * 异常增强 */ public void error(Throwable e) { } /** * 最终增强 */ public void end() { } }
(3) 切面类
创建一个切面类, 类中配置切入点和增强.
public class AccountAspect extends BaseAspect { /** * 切入点 */ public boolean isIntercept(Method method, Object[] args) throws Throwable { return method.getName().equals("transfer"); } /** * 前置增强 */ public void before() throws Throwable { System.out.println("对转账人身份进行验证."); } }
(4) 代理工厂类
定义一个工厂类来创建代理, 其实不创建这个类也行, 但为了模仿Spring还是创建了. @SuppressWarnings是为了抑制警告, 就是编译器上面的黄线.
public class ProxyFactory { @SuppressWarnings("unchecked") public static <T> T createProxy(final Class<?> targetClass, final MethodInterceptor methodInterceptor) { return (T) Enhancer.create(targetClass,methodInterceptor); } }
(5) 测试
public class Client { public static void main(String[] args) { //创建目标对象 IAccountService target = new AccountServiceImpl(); //切面 BaseAspect accountAspect = new AccountAspect(); //创建代理对象 IAccountService proxy = (IAccountService) ProxyFactory.createProxy(target.getClass(), accountAspect); proxy.transfer(); } }
结果:对转账人身份进行验证.调用dao层,完成转账主业务.
加载全部内容