`

设计模式之代理模式

 
阅读更多

设计模式之代理模式

代理模式

代理模式是常用的java设计模式,他的特征是代理类与委托类具有相同的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类对象的相关方法,来提供特定的服务;
静态代理类实例代码:
public interface Subject {
     void operation();
}
 
public class RealSubject implements Subject {
    @Override
    public void operation() {
        System.out.println("real subject operate started......");
    }
}
 
public class Proxy implements Subject {
    //对象的组合可以在运行时动态改变行为
    private Subject subject;  

    public Proxy(Subject subject) {
        this.subject = subject;
    }

    @Override
    public void operation() {
        System.out.println("before operate......");
        subject.operation();
        System.out.println("after operate......");
    }

}
 
public class Client {

    public static void main(String[] args) {
        Subject subject = new RealSubject();
        Proxy proxy = new Proxy(subject);
        proxy.operation();
    }
}
动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码,动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java反射机制可以生成任意类型的动态代理类。java.lang.reflect包中的Proxy类和InvocationHandler接口提供了生成动态代理类的能力
动态代理可以提供对另一个对象的访问,同时隐藏实际对象的具体时实,代理一般会实现它所表示的实际对象的接口,代理可以访问实际对象,但是延迟实现实际对象的部分功能,时间对象实现系统的实际功能,代理对象对客户隐藏了实际对象,客户端不知道 它是与代理打交道还是与实际对象打交道
好处:可以对请求进行任何处理,在不允许直接访问某些类的情况下对访问要做做特殊处理等

1.JDK动态代理:利用java反射机制实现,必须有对应的接口

JDK动态代理中包含一个类和一个接口

1.1 InvocationHandler接口:

public interface InvocationHandler {
   // proxy:在其上调用方法的代理实例,method:要调用的方法;args:方法调用所需要的参数
public Object invoke(Object proxy, Method method, Object[] args)
                                                         throws Throwable;
}

1.2 Proxy类:

Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法
 public static Object newProxyInstance(ClassLoader loader,  Class<?>[] inter                                                 faces, InvocationHandler h)
loader:类加载器
interfaces:类全部的接口
h:得到InvocationHandler接口的子类实现
 实例部分:
public interface StudentDAO {
    //保存学生
    void saveStudent(Integer i);
    //查询学生
    void query();
}
public class StudentDAOImpl implements StudentDAO {
    @Override
    public void saveStudent(Integer i) {
        System.out.println("save student material..."+i.toString());
    }

    @Override
    public void query() {
        System.out.println("query student info");
    }
}
 
public class StudentDAOProxy implements InvocationHandler {

    private Object orginalObject;

    public Object bind(Object obj){
        this.orginalObject = obj;
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);
    }

    private void preMethod(){
        System.out.println("------------方法执行之前--------------");
    }

    private void afterMethod(){
        System.out.println("------------方法执行之后--------------");
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result ;
        preMethod();
        result = method.invoke(this.orginalObject,args);
        afterMethod();
        return result;
    }
}
 
public class TestStudentDAOTest {

    public static void main(String[] args){
        StudentDAO dao = new StudentDAOImpl();
        StudentDAOProxy proxy = new StudentDAOProxy();
        dao = (StudentDAO)proxy.bind(dao);
        dao.saveStudent(12);
        dao.query();
    }
}
JDK的动态代理依靠接口实现,如果有些类并没有接口,则不能使用JDK代理,这就要使用Cglib动态代理了
2.Cglib动态代理:不依赖接口
CGLIB包的底层是通过使用一个小而快的字节码处理矿建ASM,来转换字节码并生成新的类,除了CGLIB包,脚本语言例如Groovy和BeanShell也是使用ASM来生成字节码,不推荐直接使用ASM,因为它要求对JVM内部结构包括class文件的格式和指令集都很熟悉 ;CGlib是一个强大的高性能的代码生成包,被许多AOP框架使用,如Spring AOP,为他们提供方法的interception拦截

 
public interface MethodInterceptor extends Callback {
    Object intercept(Object obj, Method method, Object[] args, 
                                     MethodProxy proxy) throws Throwable;
}
  • obj:代理对象
  • method:拦截方法
  • args:方法参数
  • proxy:拦截器 

实例:

public abstract class AbstractStudentDAO {
    public abstract void  saveStudent();
    public abstract void queryStudent();
}

 

public class CglibProxy implements MethodInterceptor {

    private Object originalObj;

    public Object bind(Object obj){
        this.originalObj = obj;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(obj.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    private void preMethod(){
        System.out.println("------------方法执行之前--------------");
    }

    private void afterMethod(){
        System.out.println("------------方法执行之后--------------");
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        preMethod();
        methodProxy.invokeSuper(obj,objects);
        afterMethod();
        return null;
    }
}

 

 

public class TestStudentDAOTest {

    public static void main(String[] args){
        StudentDAO dao = new StudentDAOImpl();
        StudentDAOProxy proxy = new StudentDAOProxy();
        dao = (StudentDAO)proxy.bind(dao);
        dao.saveStudent(12);
        dao.query();
    }
}

 

spring缺省是支持Proxy动态代理,如何强制使用CGLIB生成代理
<aop:aspectj-autoproxy proxy-target-class=“true”/>
Transaction:
<tx: annotation-driven transaction-manager=“teManager” proxy-target-class=“true” /> 


 

AOP(Aspect Oriented Programming)它是一种设计模式,用于实现一个系统中的应用
  • 日志拦截
  • 授权认证
  • 事务拦截
  • 方法缓存
  • 数据审计
  1. Aspect(切面):指横切性关注点的抽象即为切面,它与类相似,只是两者的关注点不一样,类是对物体特征的抽象,而切面横切性关注点的抽象
  2. JoinPoint(连接点):所谓连接点是指那些被拦截到的点,在Spring中,这些点指的是方法,因为spring只是支持方法类型的连接点,实际上joinpoint还可以是field或类构造器
  3. PointCut切入点:所谓切入点是指我们要对那些Joinpoint进行拦截的定义
  4. Advice(通知):所谓通知是指拦截到joinpoint之后所要做的事情就是通知,通知分为前置通知、后置通知、异常通知、最终通知、环绕通知
  5. Target(目标对象):代理的目标对象
  6. Weave(织入):指将aspects应用到target对象并导致Proxy对象创建的过程称为ahi入
  7.  Introduction(引入):在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一些方法或Field 
2.1 基于代理的AOP
  1. 创建bean                                                                                                                                                           
    <bean id=“studentDaoImpl”  class=“com.student.impl.StudentDaoImpl”/>
     
  2. 定义切入点                                                                                                                                                        
    <bean id=“studentDaoPointCut” class=“org.springframework.aop.support.Jdk                                               RegexMethodPointcut”>
          <property name=“pattern” value=“.*dao”>                                //指定正则表达式,匹配以dao结尾的方法
    </bean>
     
  3. 定义通知                                                                                                                                                          
    <bean id=“studentDaoAdvisor”   
             class=“org.springframework.aop.support.DefaultPointcutAdvisor”>
       <property name=“advice”  ref=“studentdDaoProxy”/>
       <property name=“pointcut” ret=“studentDaoPointCut”/>
    <bean>
     
  4. 定义代理工厂                                                                                                                                                  
    <bean id=“studnetProxyFactory” class=“org.springframework.aop.framework.                                                  ProxyFactoryBean”>
      <property name=“interceptorNames” value=“studentDaoAdvisor”/>
      <property name=“proxyInterfaces” value=“com.student.StudentDAO”/>
    <bean>
     
  5. 测试
ApplicationContext ctx = new ClassPathXmlApplicationContext(“application.xml");
StudentDao dao = (StudnetDao) ctx.getBean(“studentProxyFactory);
dao.saveStudent();
2.2 基于CGLIB的AOP
proxy动态代理只能对实现了接口的类生成代理,而不能针对类,CGLIB是针对类生成代理,主要是对用户类生成一个子类
基于代理的AOP实现过程比较复杂,可使用Spring提供的自动代理,让切入点和通知自动匹配
<bean id=“studentDaoAdvice” class=“org.springframework.aop.support.RegexdMet                                                      hodePointcutAdvisor”>
   <property name=“advice” ref=“studentDaoProxy”/>
   <property  name=“pattern” value=“.*student”/>
</bean>
//声明使用自动代理
<bean class=“org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator”/> 
 
2.3基于AspectJ 的AOP
<context:annotation-config/>
<context:component-scan base-package=“com.student”/>
<aop:aspectj-autoproxy/>
 
@Aspect  //声明为切面
@Component  //声明为组件
public class StudentAspectJProxy{
      @Before(“execution(public void com.student.StudentDao.*student())")
      public void beforeMethod(){
           System.out.println(“方法执行之前.....");
     }
   @Before(“execution(public void com.student.StudentDao.*student())")
      public void afterMethod(){
           System.out.println(“方法执行之前.....");
     }
}
 
public class StudentAspectJProxy{
     @PointCut(“execution(public void com.student.StudentDao.*student())")
      public void doFilter(){ }

      @Before(“doFilter()")
      public void beforeMethod(){
           System.out.println(“方法执行之前.....");
     }

   @AfterReturning(“doFilter()")
      public void afterMethod(){
           System.out.println(“方法执行之前.....");
     }
}
  
  • 大小: 9.1 KB
  • 大小: 256.6 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics