• Spring静态代理与动态代理


    代理模式

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

  • 相关阅读:
    ChineseAlphabetUtil获取汉字首字母工具类
    RandomCodeUtil随机数工具类,随机生成数字、字母、数字字母组合、中文姓名
    ValidateUtil常用验证工具类,如手机、密码、邮箱等
    聊天项目
    日期
    字符串
    java中属性,set get 以及如何学习类的一些用法
    继承 多态 封装
    方法 属性 构造方法和包
    面向对象知识
  • 原文地址:https://www.cnblogs.com/xfdhh/p/11488014.html
Copyright © 2020-2023  润新知