代理模式
1、什么是代理模式?
真實生活中有一種房屋中介是這樣的,租客根本就不知道房東是誰,一切簽合同、交租金、交鑰匙等操作都直接和中介公司發生。我們把這種模式稱之為代理模式。
代理模式:客戶端直接使用的都是代理對象,不知道目標對象是誰,此時代理對象可以在客戶端和目標對象之間起到中介的作用。
2、特點
代理對象完全包含目標對象,客戶端使用的都是代理對象的方法,和目標對象沒有直接關系
3、職責
把不是目標對象該做的事情從目標對象上撇開——職責清晰。
4、分類
靜態代理:在程序運行前就已經存在代理類的字節碼文件,代理對象和目標對象的關系在運行前就確定了。
動態代理:動態代理類是在程序運行期間由JVM通過反射等機制動態的生成的,所以不存在代理類的字節碼文件。代理對象和真實對象的關系是在程序運行事情才確定的。
靜態代理
1、在程序運行前就存在代理類的字節碼文件,代理對象和真實對象的關系在運行之前就確定了。
2、優點:
1.被代理的業務類只需要做好自己的業務,實現了責任分離,保證了業務類的重用性
2.將業務類隱藏起來,起到了保護作用
3、 缺點:
1.代理對象的某個接口只服務於某一個業務對象,每個真實對象都得創建一個代理對象
2.如果需要代理的方法很多,則要為每一種方法都進行處理
3.如果接口增加一個方法,除了所有實現類需要實現這個方法外,代理類也需要實現,增加了代碼的復雜度和成本
4、代碼示例
結構:
代碼:

public class TransactionManager { public void begin(){ System.out.println("開啟事務###"); } public void commit(){ System.out.println("提交事務++++++"); } public void rollback(){ System.out.println("回滾事務...."); } }

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.test</groupId> <artifactId>spring03</artifactId> <version>1.0.0</version> <properties> <!-- 定義全局變量:變量名為project.spring.version --> <project.spring.version>5.0.0.RELEASE</project.spring.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${project.spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${project.spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${project.spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>${project.spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${project.spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${project.spring.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.20</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.10</version> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.xml</include> </includes> </resource> </resources> </build> </project>

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--事務管理器--> <bean id="txManager" class="com.test.class01_static.tx.TransactionManager"/> <bean id="userDao" class="com.test.class01_static.dao.impl.UserDaoImpl"/> <!--代理對象,屬性中的userService和Service為同一個實現類,使用id區分--> <bean id="proxy" class="com.test.class01_static.proxy.UserServiceImplProxy"> <property name="userService" ref="userService"/> <property name="txManager" ref="txManager"/> </bean> <!--被代理對象--> <bean id="userService" class="com.test.class01_static.service.impl.UserServiceImpl"> <property name="userDao" ref="userDao"/> </bean> </beans>

public class UserDaoImpl implements IUserDao { public void save() { System.out.println("保存成功!!!!!!!!!!"); } public void update() { System.out.println("更新成功~~~~~~~~~"); } }

public class UserServiceImplProxy implements IUserService { /** * 靜態代理的特點: * 指在程序運行之前就存在代理對象的字節碼文件(本文件) * 一個代理類只能代理一種類型 * 實現了責任分離的目標(事務的開啟、提交、回滾與業務分離) * 靜態代理因為多了代理層,從而提升了維護成本 */ @Setter private IUserService userService; @Setter private TransactionManager txManager; public void save() { try{ //開啟事務 txManager.begin(); //處理事務 userService.save(); //提交事務 txManager.commit(); }catch (Exception e){ //回滾事務 txManager.rollback(); } } public void update() { try{ //開啟事務 txManager.begin(); //處理事務 userService.update(); //提交事務 txManager.commit(); }catch (Exception e){ //回滾事務 txManager.rollback(); } } }

public class UserServiceImpl implements IUserService { @Setter private IUserDao userDao; public void save() { userDao.save(); } public void update() { userDao.update(); } }

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration public class App { @Autowired @Qualifier("proxy") //因為bean中有兩個服務層對象,所以利用別名的方式區分代理和服務層對象 //也可直接命名IUserService proxy 效果相同 private IUserService userService; @Test public void testSave() throws Exception { //因為使用的是proxy代理對象,所以使用的是代理類的方法 userService.save(); } @Test public void testUpdate() throws Exception { userService.update(); } }
JDK動態代理
1、在程序運行之前是沒有字節碼文件的,在程序運行時由JVM通過反射機制動態的創建出代理對象的字節碼。代理對象和真實對象的關系是在程序運行時才確定的。
2、JDK動態代理API分析:
1、java.lang.reflect.Proxy 類: Java 動態代理機制生成的所有動態代理類的父類,它提供了一組靜態方法來為一組接口動態地生成代理類及其對象。 主要方法: public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler hanlder) 方法職責:為指定類加載器、一組接口及調用處理器生成動態代理類實例 參數: loader :類加載器 interfaces :目標對象實現的接口 hanlder :代理執行處理器 返回:動態生成的代理對象 2、java.lang.reflect.InvocationHandler接口: public Object invoke(Object proxy, Method method, Object[] args) 方法職責:負責集中處理動態代理類上的所有方法調用 參數: proxy :生成的代理對象 method :當前調用的真實方法對象 args :當前調用方法的實參 返回: 真實方法的返回結果 ------------------------------------------------------------------------------------ jdk動態代理操作步驟 ① 實現InvocationHandler接口,創建自己增強代碼的處理器。 ② 給Proxy類提供ClassLoader對象和代理接口類型數組,創建動態代理對象。 ③ 在處理器中實現增強操作。
3.步驟
1、配置文件

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--事務管理器--> <bean id="manager" class="com.test.class02_JDKProxy.tx.TransactionManager"/> <!--JDK動態代理--> <bean id="proxy" class="com.test.class02_JDKProxy.proxy.JDKProxy"> <property name="txManager" ref="manager"/> <property name="target" ref="userService"/> </bean> <!--CGlib動態代理--> <bean id="cglibProxy" class="com.test.class02_JDKProxy.cglib.CglibProxy"> <property name="target" ref="userService"/> <property name="txManager" ref="manager"/> </bean> <bean id="userService" class="com.test.class02_JDKProxy.service.impl.UserServiceImpl"> <property name="userDao" ref="userDao"/> </bean> <bean id="userDao" class="com.test.class02_JDKProxy.dao.impl.UserDaoImpl"/> </beans>
其余的代碼和上面一樣
2、獲取代理對象,實現接口成為代理類,實現接口的方法
public class JDKProxy implements InvocationHandler { @Setter private Object target; @Setter private TransactionManager txManager; //java.lang.reflect.Proxy是java所有動態代理類的父類 public Object getProxy(){ /**Proxy.newProxyInstance提供了一組靜態方法為一組接口動態的生成代理類及對象 * 第一個參數:類加載器 * 第二個參數:目標對象實現的接口的字節碼數據對象 * 第三個參數:實現了InvocationHandler接口的類的對象(代理類) * (本類可實現此接口,成為代理類) */ return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /** * proxy:代理對象 * method:當前調用的真實方法,利用反射使用方法 * 方法的使用,方法.invoke(對象名,參數) * args:當前調用方法的實參 */ try { //開啟事務 txManager.begin(); //處理事務 method.invoke(target,args); //提交事務 txManager.commit(); }catch (Exception e){ //回滾事務 txManager.rollback(); }finally { //釋放資源 txManager.destroy(); } return null; } }
3、測試類

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration public class App { @Autowired private JDKProxy proxy; @Autowired private CglibProxy cglibProxy; @Test public void testSave() throws Exception { IUserService service = (IUserService)this.proxy.getProxy(); System.out.println(service.getClass()); service.save(); } @Test public void testUpdate() throws Exception { IUserService service = (IUserService)this.proxy.getProxy(); service.update(); } @Test public void testSave2() throws Exception { UserServiceImpl service = (UserServiceImpl)cglibProxy.getProxy(); service.save(); } @Test public void testUpdate2() throws Exception { UserServiceImpl service = (UserServiceImpl)cglibProxy.getProxy(); service.update(); } }
4、原理
5、JDK動態代理:
1.代理的對象必須要實現接口
2.需要為每個對象創建代理對象;
3.動態代理的最小單位是類(類中所有的方法都會被代理);
6、JDK動態代理總結:
1.JAVA動態代理是使用java.lang.reflect包中的Proxy類與InvocationHandler接口這兩個來完成的。
2.要使用JDK動態代理,必須要定義接口。
3.JDK動態代理將會攔截所有public的方法(因為只能調用接口中定義的方法),這樣即使在接口中增加了新的方法,不用修改代碼也會被攔截。
4.如果只想攔截一部分方法,可以在invoke方法中對要執行的方法名進行判斷
7、CGLib針對沒有接口的類的代理,和動態代理的區別是獲取代理對象的方法不一樣,其余一樣

public class CglibProxy implements InvocationHandler { @Setter private Object target; @Setter private TransactionManager txManager; public Object getProxy(){ Enhancer enhancer = new Enhancer();//增強類 enhancer.setSuperclass(UserServiceImpl.class);//對哪個父類增強 //設置如何增強(寫實現了InvocationHandler的接口的類,也就是代理類) //這里本類實現了此接口,為代理類 enhancer.setCallback(this); return enhancer.create();//創建並返回代理對象 } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { //開啟事務 txManager.begin(); //處理事務 method.invoke(target,args); //提交事務 txManager.commit(); }catch (Exception e){ //回滾事務 txManager.rollback(); }finally { //釋放資源 txManager.destroy(); } return null; } }
8、CGLib代理總結
1.CGLIB可以生成目標類的子類,並重寫父類非final修飾符的方法。
2.要求類不能是final的,要攔截的方法要是非final、非static、非private的。
3.動態代理的最小單位是類(所有類中的方法都會被處理);
9、在Spring中:
1.若目標對象實現了若干接口,Spring就會使用JDK動態代理。
2.若目標對象沒有實現任何接口,Spring就使用CGLIB庫生成目標對象的子類。
3.對接口創建代理優於對類創建代理,因為會產生更加松耦合的系統,也更符合面向接口編程規范。