代理模式是对象的结构型模式,代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
代理模式的特点:
1、代理类和委托类有共同的父类或父接口
2、任何使用委托对象的地方都可以用代理对象替换
根据代理类的生成时间不同可以将代理分为静态代理和动态代理两种。
静态代理
静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
<span style="font-family:KaiTi_GB2312;font-size:18px;">package com.jmj.spring;
//接口
public interface UserManager {
public void addUser(String username, String password);
public void delUser(int userId);
public String findUserById(int userId);
public void modifyUser(int userId, String username, String password);
}
</span>
<span style="font-family:KaiTi_GB2312;font-size:18px;">package com.jmj.spring;
//真正得实现类
public class UserManagerImpl implements UserManager {
public void addUser(String username, String password) {
System.out.println("---------UserManagerImpl.add()--------");
}
public void delUser(int userId) {
System.out.println("---------UserManagerImpl.delUser()--------");
}
public String findUserById(int userId) {
System.out.println("---------UserManagerImpl.findUserById()--------");
return "张三";
}
public void modifyUser(int userId, String username, String password) {
System.out.println("---------UserManagerImpl.modifyUser()--------");
}
}
</span>
<span style="font-family:KaiTi_GB2312;font-size:18px;">package com.jmj.spring;
//代理类
public class UserManagerImplProxy implements UserManager {
private UserManager userManager;
public UserManagerImplProxy(UserManager userManager) {
this.userManager = userManager;
}
public void addUser(String username, String password) {
checkSecurity();
userManager.addUser(username, password);
}
public void delUser(int userId) {
checkSecurity();
userManager.delUser(userId);
}
public String findUserById(int userId) {
checkSecurity();
return userManager.findUserById(userId);
}
public void modifyUser(int userId, String username, String password) {
checkSecurity();
userManager.modifyUser(userId, username, password);
}
private void checkSecurity() {
System.out.println("-------checkSecurity-------");
}
}
</span>
<span style="font-family:KaiTi_GB2312;font-size:18px;">package com.jmj.spring;
//客户端
public class Client {
public static void main(String[] args){
UserManager userManager=new UserManagerImplProxy(new UserManagerImpl());
userManager.addUser("张三","123");
}
}
</span>
静态代理类优缺点
优点:
真正的实现类只需要关注业务逻辑本身,保证了真正的实现类的重用性。。
缺点:
1)每个代理类只能为一个接口服务:如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
2)增加了代码维护的复杂度,且会产生大量重复代码:如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。
由此,我们引入了动态代理,动态代理可以很好地解决静态代理的这些缺陷。
动态代理
动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。
JDK动态代理
UserManager接口和UserManagerImpl真正的实现类代码和静态代理中的代码一样,不再赘述!
UserManagerImplProxy代理类删除掉,改用下面的类实现代理
<span style="font-family:KaiTi_GB2312;font-size:18px;">package com.jmj.spring;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* ClassName: SecurityHandler
* @Description: 动态代理类对应的调用处理程序类
* @author meng
* @date 2016-4-26 下午8:20:05
*/
public class SecurityHandler implements InvocationHandler {
//代理类持有一个委托类的对象引用
private Object targetObject;
public Object createProxyInstance(Object targetObject){
//创建一个目标类
this.targetObject=targetObject;
//该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
//拿到目标类的装载器,目标接口,传递本类(代理类):实现了InvocationHandler类的invoke方法
//newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
//targetObject.getClass() 拿到目标类,就可以拿到目标类的所有东西
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(), this);
}
// 该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象
// 第三个是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//需要添加的内容
checkSecurity();
//得到方法名
System.out.println("start-->>"+method.getName());
//打印所有参数
for (int i = 0; i < args.length; i++)
{
System.out.println(args);
}
try
{
//调用目标方法,目标方法,
Object ret=method.invoke(targetObject, args);
return ret;
} catch (Exception e)
{
e.printStackTrace();
throw e;
}
}
private void checkSecurity() {
System.out.println("-------checkSecurity-------");
}
}
</span>
<span style="font-family:KaiTi_GB2312;font-size:18px;">package com.jmj.spring;
//客户端测试
public class Client {
public static void main(String[] args){
//拿到代理类(SecurityHandler)
SecurityHandler hander=new SecurityHandler();
//UserManager是被代理接口。绑定关系时,需要传递一个实现类的实例化对象
UserManager userManager=(UserManager)hander.createProxyInstance(new UserManagerImpl());
userManager.addUser("张三","123");
}
}
</span>
JDK动态代理类的优缺点
优点:
1)JDK动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。
2)简化了编程工作,提高了软件系统的可扩展性。
缺点:
JDK动态代理类只能代理接口。
CGLib动态代理
首先,需要引入cglib相应的jar包。CGLib动态代理的引入,可以解决JDK动态代理只能代理接口的问题。
1、具体实现类(UserManagerImpl)可以去掉实现接口部分的代码(implements UserManager)。
2、代理类,需要实现接口MethodInterceptor
/**
* @Title: CGLibDynamicProxy.java
* @Package com.jmj.spring
* @Description: TODO
* @author meng
* @date 2016-5-2 下午3:16:48
*/
package com.jmj.spring;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* ClassName: CGLibDynamicProxy
* @Description: 使用开源的 CGLib 类库可以代理没有接口的类,这样就弥补了 JDK 的不足。
* @author meng
* @date 2016-5-2 下午3:16:48
*/
public class CGLibDynamicProxy
implements
MethodInterceptor
{
private static CGLibDynamicProxy instance = new CGLibDynamicProxy();
public static CGLibDynamicProxy getInstance(){
return instance;
}
@SuppressWarnings("unchecked")
public <T> T getProxy(Class<T> cls) {
return (T) Enhancer.create(cls, this);
}
/* (non-Javadoc)
* @see net.sf.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], net.sf.cglib.proxy.MethodProxy)
*/
@Override
public Object intercept(
Object target,
Method method,
Object[] args,
MethodProxy proxy)
throws Throwable
{
checkSecurity();
Object result = proxy.invokeSuper(target, args);
return result;
}
private void checkSecurity() {
System.out.println("-------checkSecurity-------");
}
}
3、客户端可以通过动态代理得到具体的实现类
package com.jmj.spring;
//客户端测试
public class Client {
public static void main(String[] args){
//直接调用实现类
UserManagerImpl userManagerImpl=CGLibDynamicProxy.getInstance().getProxy(UserManagerImpl.class);
userManagerImpl.addUser("张三","123");
}
}
CGLib动态代理的优缺点: