Spring AOP使用的核心技术是动态代理,说到动态代理就不得不和设计模式中的代理模式联系起来,通过代理模式我们可以对目标类进行功能增强,在某个方法的执行前后增加一些操作,例如计算方法执行效率、打印日志等。

看下面的例子,我们有一个目标类Target,我们需要在目标类的test方法中增加日志打印功能,这时候我们就可以通过代理模式来实现:

package com.proxy.test;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; class Target {
public void test(){
System.out.println("Target test");
}
} class TargetProxy
{
private Target target;
private Log logger = LogFactory.getLog(TargetProxy.class);
public TargetProxy(Target target) {
this.target = target;
}
public void test()
{
logger.info("*****方法执行前**********");
target.test();
logger.info("*****方法执行后**********");
}
} public class ProxyTest{
public static void main(String[] args) {
Target target = new Target();
TargetProxy proxy = new TargetProxy(target);
proxy.test(); }
}

在上面的例子中我们通过代理类TargetProxy对目标类进行的功能增强,所谓的动态代理就是指在程序运行期间,在内存中动态的生成代理类的字节码并实例化代理对象。

实现方式有两种,一种是通过JDK自带的动态代理,另外一种则是使用Cglib实现。

1.使用JDk实现动态代理

例如在Service层,我们有两个业务逻辑类LoginServiceImpl和UserServiceImpl:

interface LoginService{
public boolean checkUser();
} class LoginServiceImpl implements LoginService{
@Override
public boolean checkUser() {
System.out.println("LoginServiceImpl checkUser");
return false;
}
} interface UserService{
public String getUserName();
} class UserServiceImpl implements UserService{ @Override
public String getUserName() {
System.out.println("UserServiceImpl getUserName");
return null;
} }

我们要对LoginServiceImpl和UserServiceImpl中的方法增加日志打印功能,可以通过Jdk动态代理实现,案例代码如下:

package com.proxy.test.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; interface LoginService{
public boolean checkUser();
} class LoginServiceImpl implements LoginService{
@Override
public boolean checkUser() {
System.out.println("LoginServiceImpl checkUser");
return false;
}
} interface UserService{
public String getUserName();
} class UserServiceImpl implements UserService{ @Override
public String getUserName() {
System.out.println("UserServiceImpl getUserName");
return null;
} } class ProxyHandler implements InvocationHandler{
private Object target;
private Log logger = LogFactory.getLog(ProxyHandler.class); public void setTarget(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] param)
throws Throwable {
logger.info("*********代理方法执行前************");
Object retObj = method.invoke(target, param);
logger.info("*********代理方法执行后************");
return retObj;
} } public class ProxyTestJDK {
public static void main(String[] args) {
//创建目标对象
LoginService loninService = new LoginServiceImpl();
UserService userService = new UserServiceImpl(); ProxyHandler proxyHandler = new ProxyHandler();
//创建LoginService代理对象
proxyHandler.setTarget(loninService);
LoginService loninService$Proxy = (LoginService) Proxy.newProxyInstance(loninService.getClass().getClassLoader(),
loninService.getClass().getInterfaces(), proxyHandler);
loninService$Proxy.checkUser(); //创建UserService代理对象
proxyHandler.setTarget(userService);
UserService userService$Proxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(), proxyHandler);
userService$Proxy.getUserName();
}
}

运行程序输出:

十月 17, 2015 1:37:21 下午 com.proxy.test.cglib.CglibProxy intercept
信息: *********代理方法执行前************
LoginServiceImpl checkUser
十月 17, 2015 1:37:21 下午 com.proxy.test.cglib.CglibProxy intercept
信息: *********代理方法执行后************
十月 17, 2015 1:37:21 下午 com.proxy.test.cglib.CglibProxy intercept
信息: *********代理方法执行前************
UserServiceImpl getUserName
十月 17, 2015 1:37:21 下午 com.proxy.test.cglib.CglibProxy intercept
信息: *********代理方法执行后************

2.使用Cglib动态代理实现

对于上面的需求我们也可以通过Cglib实现,具体代码如下:

package com.proxy.test.cglib;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
interface LoginService{
public boolean checkUser();
} class LoginServiceImpl implements LoginService{
@Override
public boolean checkUser() {
System.out.println("LoginServiceImpl checkUser");
return false;
}
} interface UserService{
public String getUserName();
} class UserServiceImpl implements UserService{ @Override
public String getUserName() {
System.out.println("UserServiceImpl getUserName");
return null;
} } class CglibProxy implements MethodInterceptor
{
private Log logger = LogFactory.getLog(CglibProxy.class); @Override
public Object intercept(Object proxy, Method method, Object[] params,
MethodProxy methodProxy) throws Throwable {
logger.info("*********代理方法执行前************");
Object retObj = methodProxy.invokeSuper(proxy, params);
logger.info("*********代理方法执行后************");
return retObj;
}
//返回目标对象的代理对象
public Object newProxy(Object target)
{
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
enhancer.setClassLoader(target.getClass().getClassLoader());
return enhancer.create();
}
}
public class ProxyTestCglib { public static void main(String[] args) {
//创建目标对象
LoginService loninService = new LoginServiceImpl();
UserService userService = new UserServiceImpl();
CglibProxy proxy = new CglibProxy();
//创建代理对象
LoginService loninService$Proxy = (LoginService)proxy.newProxy(loninService);
UserService userService$Proxy = (UserService)proxy.newProxy(userService);
loninService$Proxy.checkUser();
userService$Proxy.getUserName(); }
}

3.二者优缺点分析

使用JDK动态代理,目标类必须实现的某个接口,如果某个类没有实现接口则不能生成代理对象。

Cglib原理是针对目标类生成一个子类,覆盖其中的所有方法,所以目标类和方法不能声明为final类型。

从执行效率上看,Cglib动态代理效率较高。

最新文章

  1. Redis简单案例(四) Session的管理
  2. [ 转载 ] kernel32.BaseThreadInitThunk
  3. [后端人员耍前端系列]KnockoutJs篇:快速掌握KnockoutJs
  4. yii2 如何在页面底部加载css和js
  5. HTML5的数据自动补齐功能
  6. Structured Streaming Programming Guide
  7. Win10下IIS配置图解、MVC项目发布图解、IIS添加网站图解
  8. PHP扩展开发01:第一个扩展【转】
  9. TinyXml快速入门(二)
  10. 渲染器 Shader BitmapShader
  11. c#语句:分支
  12. Storm On YARN带来的好处
  13. UVa 481 - What Goes Up
  14. php+redis 简易的实现文章发布系统(用户投票系统)
  15. js excel 列表导出
  16. C# 设置Excel数据自适应行高、列宽的2种情况
  17. fastjson JSON 对象为空保留null
  18. Random()种子数
  19. (转)web.xml中的contextConfigLocation在spring中的作用
  20. [BZOJ4857][JSOI2016]反质数序列[最大点独立集]

热门文章

  1. json简介及josn数组中取字符
  2. day34-3 类和对象小知识
  3. day001 Python 计算机基础(2019年5月16日)
  4. PHP并发IO编程实践
  5. kernel学习单
  6. USACO 4.1 Fence Rails
  7. mysql字符集和排序规则
  8. 45.mapping建立、修改
  9. Netty学习总结(3)——Netty百万级推送服务
  10. Eclipse在Project Explorer项目归组及分模块显示