代理(Proxy)是一种设计模式, 提供了对目标对象另外的访问方式;即经过代理访问目标对象。 这样好处: 能够在目标对象实现的基础上,加强额外的功能操做。(扩展目标对象的功能)。html
能够作到在不修改目标对象的功能前提下,对目标对象功能扩展。java
很简单举个例子:设计模式
如今我是一个明星,拥有不少粉丝。粉丝但愿我唱歌给他们听,可是若是都是我来接应他们,我岂不是很忙….因而乎,我就去找了个经纪人。这个经纪人就表明了我。当粉丝想要我唱歌的时候,应该是找经纪人,告诉经纪人想让我唱歌。markdown
如今我愈来愈红了,不是粉丝想要我唱歌,我就唱了。我要收费了。可是呢,做为一个公众人物,不多是我本身说:我要收10000万,我才会去唱歌。因而这就让经纪人对粉丝说:只有10000万,我才会唱歌。ide
不管外界是想要我干什么,都要通过个人经纪人。个人经纪人也会在其中考虑收费、推脱它们的请求。学习
经纪人就是代理,实际上台唱歌、表演的仍是我测试
直接使用例子来讲明吧…如今我有一个IUserDao的接口,拥有save方法()this
// 接口 public interface IUserDao { void save(); }
public class UserDao implements IUserDao{ @Override public void save() { System.out.println("-----已经保存数据!!!------"); } }
如今,我想要在save()方法保存数据前开启事务、保存数据以后关闭事务…(固然啦,直接再上面写不就好了吗…业务方法少的时候,确实没毛病…)spa
public void save() { System.out.println("开启事务"); System.out.println("-----已经保存数据!!!------"); System.out.println("关闭事务"); }
可是呢,如今若是我有好多好多个业务方法都须要开启事务、关闭事务呢?设计
public void save() { System.out.println("开启事务"); System.out.println("-----已经保存数据!!!------"); System.out.println("关闭事务"); } public void delete() { System.out.println("开启事务"); System.out.println("-----已经保存数据!!!------"); System.out.println("关闭事务"); } public void update() { System.out.println("开启事务"); System.out.println("-----已经保存数据!!!------"); System.out.println("关闭事务"); } public void login() { System.out.println("开启事务"); System.out.println("-----已经保存数据!!!------"); System.out.println("关闭事务"); }
…..咱们发现就有了不少不少的重复代码了…咱们要作的就是:当用户调用UserDao方法的时候,找的是代理对象、而代理帮我在解决这么繁琐的代码
因而呢,咱们就请了一个代理了
所以,咱们的代理就要实现IUserDao接口,这样的话,代理就跟userDao有相同的方法了。
public class UserDaoProxy implements IUserDao{ // 接收保存目标对象【真正作事的仍是UserDao】,所以须要维护userDao的引用 private IUserDao target; public UserDaoProxy(IUserDao target) { this.target = target; } @Override public void save() { System.out.println("开始事务..."); target.save(); // 执行目标对象的方法 System.out.println("提交事务..."); } }
外界并非直接去找UserDao,而是要经过代理才能找到userDao
public static void main(String[] args) { // 目标对象 IUserDao target = new UserDao(); // 代理 IUserDao proxy = new UserDaoProxy(target); proxy.save(); // 执行的是,代理的方法 }
这样一来,咱们在UserDao中就不用写那么傻逼的代码了…傻逼的事情都交给代理去干了…
咱们首先来看一下静态代理的不足:
动态代理比静态代理好的地方:
Java提供了一个Proxy类,调用它的newInstance方法能够生成某个对象的代理对象,该方法须要三个参数:
在编写动态代理以前,要明确两个概念:
小明是一个明星,拥有唱歌和跳舞的方法。实现了人的接口
public class XiaoMing implements Person { @Override public void sing(String name) { System.out.println("小明唱" + name); } @Override public void dance(String name) { System.out.println("小明跳" + name); } }
public interface Person { void sing(String name); void dance(String name); }
public class XiaoMingProxy { //代理只是一个中介,实际干活的仍是小明,因而须要在代理类上维护小明这个变量 XiaoMing xiaoMing = new XiaoMing(); //返回代理对象 public Person getProxy() { /** * 参数一:代理类的类加载器 * 参数二:被代理对象的接口 * 参数三:InvocationHandler实现类 */ return (Person)Proxy.newProxyInstance(XiaoMingProxy.class.getClassLoader(), xiaoMing.getClass().getInterfaces(), new InvocationHandler() { /** * proxy : 把代理对象本身传递进来 * method:把代理对象当前调用的方法传递进来 * args:把方法参数传递进来 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //若是别人想要让小明唱歌 if (method.getName().equals("sing")) { System.out.println("给1000万来再唱"); //实际上唱歌的仍是小明 method.invoke(xiaoMing, args); } return null; } }); } }
public static void main(String[] args) { //外界经过代理才能让小明唱歌 XiaoMingProxy xiaoMingProxy = new XiaoMingProxy(); Person proxy = xiaoMingProxy.getProxy(); proxy.sing("我爱你"); }
咱们以前写中文过滤器的时候,须要使用包装设计模式来设计一个request类。若是不是Servlet提供了实现类给咱们,咱们使用包装设计模式会出现麻烦
如今咱们学习了动态代理了,动态代理就是拦截直接访问对象,能够给对象进行加强的一项技能
public void doFilter(final ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { final HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; response.setContentType("text/html;charset=UTF-8"); request.setCharacterEncoding("UTF-8"); //放出去的是代理对象 chain.doFilter((ServletRequest) Proxy.newProxyInstance(CharacterEncodingFilter.class.getClassLoader(), request.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //判断是否是getParameter方法 if (!method.getName().equals("getParameter")) { //不是就使用request调用 return method.invoke(request, args); } //判断是不是get类型的 if (!request.getMethod().equalsIgnoreCase("get")) { return method.invoke(request, args); } //执行到这里,只能是get类型的getParameter方法了。 String value = (String) method.invoke(request, args); if (value == null) { return null; } return new String(value.getBytes("ISO8859-1"), "UTF-8"); } }), response); }