代理模式
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.对接口创建代理优于对类创建代理,因为会产生更加松耦合的系统,也更符合面向接口编程规范。