##代理模式 :提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。例如:你需要去报考驾校,虽然现在也支持自考了但是自考很麻烦,需要自己去预约训练场地、找教练等待,但是你可以有一个很好的选择,去报驾校,委托驾校帮忙处理那些事情,而驾校就充当了一个代理的作用。你无需关注其他,只需要关注科目一二三四考试即可。
代理模式主要有静态代理和动态代理方式:
- 静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;
- JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;
- CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理
1.创建业务接口
package com.dtree.proxy;
/**
* 〈一句话功能简述〉
* 业务接口
* @author
* @date 2019年03月28日 19:44
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public interface Welcomes {
void login();
}
2.创建业务接口实现类
package com.dtree.proxy;
/**
* 〈一句话功能简述〉
* 业务实现类
* @author forest
* @date 2019年03月28日 19:44
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class UserWelcome implements Welcomes {
@Override
public void login() {
System.out.println("欢迎回来");
}
}
3.静态代理模式
/**
* 〈一句话功能简述〉
* 静态代理
* @author forest
* @date 2019年03月28日 19:45
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class StaticProxy implements Welcomes {
private UserWelcome target;
private String name;
private String pwd;
public StaticProxy(UserWelcome target, String name, String pwd) {
this.target = target;
this.name = name;
this.pwd = pwd;
}
@Override
public void login() {
if (name.equals("admin") && pwd.equals("admin")) {
System.out.println(name+"登陆成功");
target.enter();
} else {
System.out.println("账号密码错误,登陆失败");
}
}
}
3.1静态代理测试类
package com.dtree.proxy;
import static org.testng.Assert.*;
/**
* 〈一句话功能简述〉
* 简单的静态代理的demo测试;
* @author forest
* @date 2019年03月28日 19:47
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class StaticProxyTest {
// 静态代理模式总结:
//
// 优点:在没有对目标对象进行侵入式修改的前提下,对用户的登陆进行了参数校验,也是对目标对象功能的扩展和拦截。
//
// 缺点:代理对象必须与目标对象实现同一个接口,这样会产生多个不同的代理类,不易维护,并且当接口增加方法,代理和目标对象都需要进行统一维护。
public static void main(String[] args) {
Welcomes welcome = new StaticProxy(new UserWelcome(), "admin", "admin");
welcome.login();
}
}
4.JDK动态代理
package com.dtree.proxy;
import org.omg.CORBA.PUBLIC_MEMBER;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 〈一句话功能简述〉
* JDK动态代理
*
* @author forest
* @date 2019年03月28日 17:31
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class DynamicProxy implements InvocationHandler {
/**
* 这其实业务实现类对象,用来调用具体的业务方法
*/
private Object target;
public Object bind(Object target) {
this.target = target;//接收业务实现类对象参数
//通过反射机制,创建一个代理类对象实例并返回。用户进行方法调用时使用
//创建代理对象时,需要传递该业务类的类加载器(用来获取业务实现类的元数据,
// 在包装方法是调用真正的业务方法)、接口、handler实现类
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.println("开始操作------");
//调用真正的业务方法
result = method.invoke(target, args);
System.out.println("结束后处理------");
return result;
}
}
4.1JDK动态代理测试类
package com.dtree.proxy;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
/**
* 〈一句话功能简述〉
* JDK动态代理
* @author forest
* @date 2019年03月28日 20:01
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class DynamicProxyTest {
@Test
public void functionDynamic() {
//1.实例化业务类
UserWelcome userWelcome = new UserWelcome();
//2.创建JDK代理类
DynamicProxy dynamicProxy = new DynamicProxy();
//3.将业务类绑定到代理类,针对接口
Welcomes bind = (Welcomes)dynamicProxy.bind(userWelcome);
//4.即可实现无侵入的执行业务类
bind.login();
}
}
5.Cglib动态代理
package com.dtree.proxy;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 〈一句话功能简述〉
* cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,
* 并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。
* @author forest
* @date 2019年03月28日 20:19
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class CGlibProxy implements MethodInterceptor {
private Object target;//业务类对象,供代理方法中进行真正的业务方法调用 //相当于JDK动态代理中的绑定
public Object getInstance(Object target) {
this.target = target; //给业务对象赋值
Enhancer enhancer = new Enhancer(); //创建加强器,用来创建动态代理类
enhancer.setSuperclass(this.target.getClass());
// 为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
//设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
enhancer.setCallback(this);
// 创建动态代理类对象并返回
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("cglib代理开始处理——————");
proxy.invokeSuper(obj, args); //调用业务类(父类中)的方法
System.out.println("cglib调用后处理——————");
return null;
}
}
5.1Cglib代理测试类
package com.dtree.proxy;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
/**
* 〈一句话功能简述〉
*
* @author forest
* @date 2019年03月28日 20:21
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class CGlibProxyTest {
@Test
public void functionDynamic() {
//1.实例化业务类
UserWelcome userWelcome = new UserWelcome();
//2.创建CGLIB代理类
CGlibProxy cGlibProxy = new CGlibProxy();
//3.将业务类绑定到代理类
// Welcomes bind = (Welcomes)cGlibProxy.getInstance(userWelcome);
UserWelcome bind = (UserWelcome)cGlibProxy.getInstance(userWelcome);
//4.即可实现无侵入的执行业务类
bind.login();
}
}
6.spring代理源代码:
如下:
- 如果目标对象实现了接口,默认情况下会采用JDK动态代理实现AOP
- 如果目标对象实现了接口,可以强制使用CGLIB实现AOP
- 如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换,而如何强制使用CGLIB实现AOP呢?在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>,默认false为jdk
package org.springframework.aop.framework;
import java.io.Serializable;
import java.lang.reflect.Proxy;
import org.springframework.aop.SpringProxy;
//spring默认代理类,实现了AopProxyFactory代理工厂
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
public DefaultAopProxyFactory() {
}
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
return new JdkDynamicAopProxy(config);
} else {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
} else {
return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
}
}
}
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
Class<?>[] ifcs = config.getProxiedInterfaces();
return ifcs.length == 0 || ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0]);
}
}