代理(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); }