java 代理到动态代理演变及详解

1.在我们开发中代理设计其实无处不在,那么怎么实现代理呢?其实说白了就是我们调用一个类的时候,可以通过一个公共的类来实现不同方法的调用,也就是说完成代理之后的对象具有与目标类相同的属性及动作(方法)。话不多说,下面看一个简单的代理实现:

1)假设场景,我们最熟悉的用户信息操作,有新增,修改,删除,查询。此时要求我们要对这些操作进行日志记录信息以便我们进行项目定位与维护。首先创建一个u简单user对象

package com.szpl.cai.proxy.dao;
public class User {
 private String id;
 private String name;
 public User(String id,String name){
  this.id=id;
  this.name=name;
 }
 public String getId() {
  return id;
 }
 public void setId(String id) {
  this.id = id;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 @Override
 public String toString() {
  return "User [id=" + id + ", name=" + name + "]";
 }
 
}

2)在这里我们模拟一个伪功能,没有3层结构只有一个简单的dao层,创建userdao及userdaoimpl实现类

package com.szpl.cai.proxy.dao;
/**
 * 用户接口
 * @author caizl
 *
 */
public interface UserDao {
 //新增用户
 public void insertUser(User user);
 //修改用户
 public void updateUser(String id);
 //删除用户
 public void deleteUser(String id);
 //查询用户
 public void findUser(String id);
}

//实现类

package com.szpl.cai.proxy.dao;
public class UserDaoImpl implements UserDao{
 public void insertUser(User user) {
  System.out.println("新增用户"+user.toString());  
 }
 public void updateUser(String id) {  
  System.out.println("修改用户:"+id+"信息");
 }
 public void deleteUser(String id) {
  
  System.out.println("删除用户:"+id+"信息");
 }
 public void findUser(String id) {
  System.out.println("查找用户"+id+"信息");
  
 }
}

//此时基本工作已经满足,我们接下来创建一个user的动态代理类

package com.szpl.cai.proxy.dao;
/**
 * 用户代理类
 * @author caizl
 *
 */
public class UserProxy implements UserDao{
 private UserDao userDao;
    public UserDao bind(UserDao userDao){
     this.userDao=userDao;
     return this;
    }
 public void insertUser(User user) {
  log("插入之前查询用户表没有此用户信息,可以新增");  
  this.userDao.insertUser(user);
  log("提交事物,新增结束");
  
 }
 public void updateUser(String id) {
  log("更新之前查询用户表查到用户信息,可以修改");
  this.userDao.updateUser(id);
  log("用户:"+id+"修改成功,提交事物,修改结束");
 }
 public void deleteUser(String id) {
  log("删除之前查询用户表查到用户信息,可以删除");
  this.userDao.deleteUser(id);
  log("用户:"+id+"删除成功,提交事物,删除结束");  
 }
 public void findUser(String id) {
  log("查询用户表查到用户信息:id="+id);
  this.userDao.findUser(id);
  log("用户:"+id+"查询成功");  
 }
 //记录日志方法
 public void log(String message){
  System.err.println("日志记录:"+message);
 }
}

3)测试案例

package com.szpl.cai.proxy.dao;
public class Test {
 public static void main(String[] args) throws Exception{
  UserDao userDao = new UserProxy().bind(new UserDaoImpl());
  User user = new User("1", "java");
  userDao.insertUser(user);
  Thread.sleep(1000);
  userDao.updateUser(user.getId());
  /*Thread.sleep(1000);
  userDao.deleteUser(user.getId());
  Thread.sleep(1000);
  userDao.findUser(user.getId());*/
 }
}

由以上代码可以测试我们在进行用户实际操作同时进行了日志记录。就好像我们执行用户操作时,日志记录并不影响我们实际操作,并且没有相关耦合,就像操作在执行过程中,当满足某一条件时就会执行我们的日志记录,但是不影响我们的业务(这个就是有关切面,在这里不做过多解读)但是有一个确定我们此代理类只能生成用户代理类,我们项目肯定不知用户这一个,那么此时就需要借助我们java自带的代理类来进行实现满足,如下:

利用我们的java自带类实现一个动态代理:

package com.szpl.cai.proxy.dao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * 通过java包的方式获取代理对象
 * @author caizl
 *
 */
public class JavaProxy implements InvocationHandler{
    private Object target;//需代理的类对象
    //获取与代理对象结构相同的一个代理类对象;需要由系统生成指定接口的代理类
    public Object bind(Object target){
     this.target=target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
 public Object invoke(Object proxy, Method method, Object[] args)
   throws Throwable {
  //调用日志信息
  logMessage(method.getName());
  //调用目标方法
  Object retObj =  method.invoke(this.target, args);
  //判断如果有修改等操作则进行事物提交
  if(method.getName().matches("do[a-zA-Z0-9]+")){
      this.commit(); 
  }
  return retObj;
 }
 //日志方法
 public void logMessage(String methodName){
  System.out.println("调用了方法名为:"+methodName);
 }
 //事物提交
 public void commit(){
  System.out.println("事物提交");
 }
}

通过以上代码我们可以知道,在写我们代理类时需要将我们的目标类target进行绑定,这样我们才能通过反射进行类中方法调用及实现。个人理解:创建一个代理类步骤:

1)创建一个代理类实现InvocationHandler接口,通过覆写invoke反射方法来进行实体类操作。

2)编写绑定方法将我们目标类绑定到我们的代理对象中,此动作利用我们java代理类进行目标类的结构复制。

3)覆写我们的invoke方法,通过绑定之后代理对象就具有我们目标类的属性及方法,然后经过反射机制就可以调用我们的目标方法。

但是此种方式虽然实现了动态代理及代码重用,但是有一定的局限性,那就是必须实现一个接口(红色部分),如果没有接口传入则无法进行代理使用。此时我们可以借助三方工具cglib方式进行进一步的代理实现(资源分享我们需要的jar包可以通过http://findjar.com此网站进行下载)。

3.使用cglib进行代理实现(基于我们类实现动态代理)

使用这个工具我们需要了解enhancer(返回代理类对象),Methodinteceptor:实现我们处理代理操作的一个接口MethdProxy类相当于我们Method类

package com.szpl.cai.proxy.dao;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibProxy implements MethodInterceptor{  
 public Object intercept(Object obj, Method method, Object[] args,
   MethodProxy methodProxy) throws Throwable {
  System.err.println("被代理方法:"+method.getName());
  Object retObj = methodProxy.invokeSuper(obj, args);//通过反射将目标类复制我们目标类结构及属性生成代理对象
  return retObj;
 } 
}
//测试类
class haha{
 public void say(){
  System.err.println("哥们代理成功了");
 }
}

//测试方法类
public static void main(String[] args) {
  Enhancer enhancer = new Enhancer();//字节码增强
  enhancer.setSuperclass(haha.class);//设置字节码增强父类
  enhancer.setCallback(new CglibProxy());//通过反射生成目标代理类
  haha testClass = (haha)enhancer.create();//强转为目标类对象
  testClass.say();
 }
总结:以上代理可以汇总一下优缺点:


 

















  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值