代理模式及案例

 *_* 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.

好处:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.

代理模型的三种方式
  1. 静态代理
  2. 动态代理  
   JDK动态代理
   CGLIB动态代理

一、静态代理

  在不修改目标对象方法的基础上,对目标对象方法进行扩展。

  

//Service接口
public interface UserService {

    void save();

    void delete();

    void update();

}
//Service实现
public class UserServiceImpl implements UserService {
    @Override
    public void save() {
        System.out.println("新增用户");
    }

    @Override
    public void delete() {
        System.out.println("删除用户");
    }

    @Override
    public void update() {
        System.out.println("修改用户");
    }
}
/**
 * 静态代理类
 * 实现日志扩展功能
 *要求:
 *1)和目标(类)实现同样的接口
 *2)在静态代理类中传入目标对象实例,以便调用目标对象的方法
 *3)可以在静态代理类的方法中添加代理逻辑代码
 */
public class LogProxy implements UserService{

    //接收目标对象实例
    private UserService userService;

    //使用构造方法传入目标对象实例
    public LogProxy(UserService userService){
        this.userService = userService;
    }

    @Override
    public void save() {
        System.out.println("before=====save");
        //调用目标对象的方法
        userService.save();
        System.out.println("after=====save");
    }

    @Override
    public void delete() {
        System.out.println("before=====save");
        //调用目标对象的方法
        userService.delete();
        System.out.println("after=====save");
    }

    @Override
    public void update() {
        System.out.println("before=====save");
        //调用目标对象的方法
        userService.update();
        System.out.println("after=====save");
    }
}
 
package com.lemon.a_staticproxy;

import com.lemon.service.UserService;
import com.lemon.service.impl.UserServiceImpl;

//测试 
public class Test {
    public static void main(String[] args) {
        //使用静态代理模式
        //1.创建目标对象
        UserService userService = new UserServiceImpl();
        //2.创建静态代理类对象
        UserService proxy = new LogProxy(userService);
        //3.调用代理类的方法
        proxy.save();
        proxy.update();
        proxy.delete();
    }
}

静态代理的缺点:

 1)一个静态代理类只能代理一个目标类

    2)静态代理类的每个方法都需要编写重复的代理逻辑,代码比较冗余

二、JDK动态代理

  前提:目标对象有接口的情况

/**
 * 用于生成JDK动态代理对象的工具类
 */
public class LogProxy {

    /**
     * 生成JDK动态代理对象的方法
     *
     * 返回值:生成的JDK动态代理对象
     * 参数:target, 传入目标对象
     */
    public static Object getProxy(Object target){
        /**
         * 参数一:类加载器,JDK动态代理的底层使用类加载器来生成的一个动态类的。通常传入当前类的类加载器即可!!!(LogProxy.class.getClassLoader())
         * 参数二:目标对象的接口列表(所有接口),通常使用目标对象获取接口列表(target.getClass().getInterfaces())
         * 参数三:接口。 该用于编写  代理类的代理逻辑代码。通常我们要提供InvocationHandler接口的实现类(匿名内部类的方式提供)
         */
        return Proxy.newProxyInstance(
                LogProxy.class.getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {

                    /**
                     * invoke方法:用于编写 代理类的代理逻辑代码。
                     *      invoke方法在什么时候会被调用?
                     *            该方法会在调用JDK代理对象的每个方法的时候被执行!!!!!
                     *
                     * @param proxy: 生成JDK动态代理对象
                     * @param method: 目标对象的执行方法的对象
                     * @param args: 目标对象的方法参数列表
                     * @return 返回值:目标对象方法执行后的返回结果
                     * @throws Throwable
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //获取目标对象的方法名称
                        String methodName = method.getName();

                        System.out.println("before======"+methodName);

                        //获取方法的参数列表
                       /* if(args!=null)
                        System.out.println(Arrays.asList(args));*/

                        //调用目标对象的方法
                        /**
                         * 参数一:执行的对象(必须传入目标对象,不能传入代理对象,否则会死循环)
                         * 参数二:方法的参数列表
                         */
                        Object result = method.invoke(target,args);

                        System.out.println("after======"+methodName);

                        return result;
                    }
                }
        );
    }
}

三、CGLIB动态代理

  目标对象可有可无

<!-- 导入spring-core(包含cglib依赖) -->
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
        </dependency>
    </dependencies>
/**
 * 演示JDK动态代理
 */
public class Demo {

    public static void main(String[] args) {
        //1.创建目标对象
        UserService userService = new UserServiceImpl();
        //2.创建静态代理类对象
        UserService proxy = (UserService) LogProxy.getProxy(userService);
        //3.调用代理类的方法
        proxy.save();
        proxy.delete();
        proxy.update();
    }
}
/**
 * 用于生成Cglib代理对象的工具类
 */
public class LogProxy {


    /**
     * 生成Cglib代理对象
     * 返回值:生成的Cglib子类代理对象
     * 参数:目标对象(目标对象没有接口)
     */
    public static Object getProxy(Object target){

        /**
         * 方法返回值:生成的Cglib子类代理对象
         * 参数一:目标对象的类型(target.getClass()) (其实目标对象的类型就是Cglib代理对象 的  父类)
         * 参数二:MethodInterceptor接口,用于编写 代理对象的代理逻辑代码。通常提供MethodInterceptor接口的匿名内部即可
         */
        return Enhancer.create(
                target.getClass(),
                new MethodInterceptor() {
                    /**
                     * intercept方法:在调用代理对象的每个方法的时候会执行
                     * @param proxy: 生成的代理对象
                     * @param method: 目标对象的方法对象
                     * @param args: 目标对象的方法参数列表
                     * @param methodProxy: 代理对象的方法对象
                     * @return
                     * @throws Throwable
                     */
                    @Override
                    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

                        //获取目标对象的方法名称
                        String methodName = method.getName();

                        System.out.println("before======"+methodName);

                        /**
                         * 调用目标对象的方法
                         */
                        //方式一:直接使用目标对象 调用 目标对象的方法
                        Object result = method.invoke(target,args);
                        //方式二:使用代理类(子类)调用 目标对象(父类)的方法
                        //invokeSuper: 调用父类的方法
                        //Object result = methodProxy.invokeSuper(proxy,args);

                        System.out.println("after====="+methodName);

                        return result;
                    }
                }
        );
    }

}
/**
 * 演示CJLIB动态代理
 */
public class Demo2 {

    public static void main(String[] args) {
        //1.创建目标对象
        UserService userService = new UserServiceImpl();
        //2.创建静态代理类对象
        UserService proxy = (UserService) LogProxy.getProxy(userService);
        //3.调用代理类的方法
        proxy.save();
        proxy.delete();
        proxy.update();
    }
}

 *_* Spring aop 案例

1 、创建service接口和实现

public interface LemonService {

    void save();
    void delete();
    void update();

}
public class LemonServiceImpl implements LemonService {
    @Override
    public void save() {
        System.out.println("新增");
    }

    @Override
    public void delete() {
        System.out.println("删除");
    }

    @Override
    public void update() {
        System.out.println("修改");
    }
}

2、创建切面类

/**
 * 日志切面类
 */
public class LogAspect {

    /**
     * 通知方法(插入到目标方法的前面)
     */
    public void writeLog(){
        System.out.println("before=======");
    }

}

3、配置切面类

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 1.创建目标对象 -->
    <bean id="lemonService" class="com.lemon.service.impl.LemonServiceImpl"/>

    <!-- 2.创建切面对象 -->
    <bean id="logAspect" class="com.lemon.log.LogAspect"/>

    <!-- 3.切面配置 -->
    <aop:config>
        <!-- 切面配置 = 通知(advice)+切入点(pointcut)-->
        <!--
            ref: 引用切面类对象
         -->
        <aop:aspect ref="logAspect">
            <!-- 定义切入点 -->
            <!--
                id: 定义切入点的别名
                expression: 切入点表达式(用于定义需要切入的方法)
             -->
            <aop:pointcut id="pt" expression="execution(* com.lemon.service.impl.LemonServiceImpl.*(..))"/>

            <!-- 定义通知 -->
            <!--
               method: 使用切面类的哪个方法作为通知方法
               pointcut-ref: 关联切入点
             -->
            <!-- 前置通知 -->
            <aop:before method="writeLog" pointcut-ref="pt"/>
        </aop:aspect>
    </aop:config>
</beans>

  *_* Spring boot 自定义拦截器

package com.example.demo.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class MyInterceptor implements HandlerInterceptor{

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        System.out.println(">>>MyInterceptor1>>>>>>>在请求处理之前进行调用(Controller方法调用之前)");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        System.out.println(">>>MyInterceptor1>>>>>>>请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)");
        
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println(">>>MyInterceptor1>>>>>>>在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)");
        
    }

}

 package com.example.demo.configure;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import com.example.demo.interceptor.MyInterceptor;
@Configuration
public class MyWebAppConfigurer extends WebMvcConfigurerAdapter{
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 第一个参数指定自定义拦截器
        // addPathPatterns 用于添加拦截规则
        // excludePathPatterns 用户排除拦截
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
     // 多个拦截器可以组成一个拦截器链
     //registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**");
        super.addInterceptors(registry);
    }

}

  *_* Spring boot 自定义过滤器

package com.example.demo.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class MyFilter implements Filter{

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        long start = System.currentTimeMillis();
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("时间间隔:"+(System.currentTimeMillis()-start));
        
    }

    @Override
    public void destroy() {
        // TODO Auto-generated method stub
        
    }

}
package com.example.demo.configure;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.example.demo.filter.MyFilter;
@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean registFilter() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new MyFilter());
        registration.addUrlPatterns("/*");
        registration.setName("MyFilter");
        registration.setOrder(1);
        return registration;
    }
}
原文地址:https://www.cnblogs.com/hnzkljq/p/12009882.html