代理模式

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("回滚事务....");
    }
}
txManager
<?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>
pom.xml
<?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>
App-Context.xml
public class UserDaoImpl implements IUserDao {
    
    public void save() {
        System.out.println("保存成功!!!!!!!!!!");
    }

    
    public void update() {
        System.out.println("更新成功~~~~~~~~~");
    }
}
UserDaoImpl
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();
        }
    }
}
UserServiceImplProxy
public class UserServiceImpl implements IUserService {
    @Setter
    private IUserDao userDao;

    public void save() {
        userDao.save();
    }


    public void update() {
        userDao.update();
    }
}
UserServiceImpl
@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();
    }
}
App

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>
App-Context.xml

  其余的代码和上面一样

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

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;
    }
}
CGLib

8、CGLib代理总结

  1.CGLIB可以生成目标类的子类,并重写父类非final修饰符的方法。

  2.要求类不能是final的,要拦截的方法要是非final、非static、非private的。

  3.动态代理的最小单位是类(所有类中的方法都会被处理);

9、在Spring中:

 

  1.若目标对象实现了若干接口,Spring就会使用JDK动态代理。

 

  2.若目标对象没有实现任何接口,Spring就使用CGLIB库生成目标对象的子类。

 

  3.对接口创建代理优于对类创建代理,因为会产生更加松耦合的系统,也更符合面向接口编程规范。

 

posted on 2019-09-08 19:36  幸福的小耗子  阅读(1073)  评论(0编辑  收藏  举报