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

原文地址:https://www.cnblogs.com/xfdhh/p/11488014.html

时间: 2024-11-09 03:53:05

Spring静态代理与动态代理的相关文章

spring静态代理和动态代理

本节要点: Java静态代理 Jdk动态代理 1 面向对象设计思想遇到的问题 在传统OOP编程里以对象为核心,并通过对象之间的协作来形成一个完整的软件功能,由于对象可以继承,因此我们可以把具有相同功能或相同特征的属性抽象到一个层次分明的类结构体系中.随着软件规范的不断扩大,专业化分工越来越系列,以及OOP应用实践的不断增多,随之也暴露了一些OOP无法很好解决的问题. 现在假设系统中有三段完全相似的代码,这些代码通常会采用"复制"."粘贴"方式来完成,通过这种方式开发

【设计模式】代理模式:静态代理,动态代理,spring aop

代理模式分为静态代理和动态代理.我们拿链家来举例子,我们本人是真实的对象,有真实的业务需求:需要去找房子:链家是中介,是代理类,他来帮我执行找房子的这个操作. 静态代理: 1.实现一个接口 public interface SearchHome { public void search(); } 2.构建实现接口的委托类 public class Master implements SearchHome { @Override public void search() { System.out.

Atitit 代理CGLIB&#160;动态代理&#160;AspectJ静态代理区别

Atitit 代理CGLIB 动态代理 AspectJ静态代理区别 1.1. AOP 代理主要分为静态代理和动态代理两大类,静态代理以 AspectJ 为代表:而动态代理则以 spring AOP 为代表 1 1.2. JDK动态代理是模拟接口实现的方式,cglib是模拟子类继承的方式1 1.3. CGLIB代理模式的缺点 在static和final方法上应用横切关注点也是无法做到的.2 1.1. AOP 代理主要分为静态代理和动态代理两大类,静态代理以 AspectJ 为代表:而动态代理则以 

Java:静态代理and动态代理

代理模式是常用的设计模式,其特征是代理类与委托类具有相同的接口,在具体实现上,有静态代理和动态代理之分.代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务,也就是说代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等. 静态代理和动态代理的一个显著区别: 静态代理:由程序员创建或特定工具自动生成源代码,再对其编译.在程序运行前,代理类的.class文件就

java 代理模式(静态代理、动态代理、Cglib代理) 转载

Java的三种代理模式 1.代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法 代理模式最大的特点就是代理类和实际业务类实现同一个接口(或继承同一父类),代理对象持有一个实际对象的引用,外部调用时操作的是代理对象,而在代理对象的内部实现中又会去调

静态代理、动态代理

代理模式最大的优势就是能够解耦,在spring中也是广泛使用.spring中一个重要的特性就是aop,aop是个啥东西呢?其实很简单,比如现在有个业务方法,那这个业务方法很重要,涉及到非常重要的业务数据,那对于广大企业应用来说,为了以后能够及时的定位问题,需要记录相关入参以及出参到日志表. 但是对于企业应用来说,需要记录日志的地方应该是蛮多的,如果每个方法中都手动的去写这些记录日志的东西,就会特别的冗余,那使用代理模式就可以解决. 一.静态代理 1.User接口 package com.ty.s

zbb20180930 代理模式 -静态代理-jdk动态代理-cglib动态代理

CGLIB与JDK动态代理区别 区别: java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理.而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理. 1.如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP 2.如果目标对象实现了接口,可以强制使用CGLIB实现AOP 3.如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB

java中静态代理和动态代理

一.概述 代理是一种模式,提供了对目标对象的间接访问方式,即通过代理访问目标对象.如此便于在目标实现的基础上增加额外的功能操作,前拦截,后拦截等,以满足自身的业务需求,同时代理模式便于扩展目标对象功能的特点也为多人所用. 按照代理的创建时期,代理类可以分为两种: 静态:由程序员创建代理类或特定工具自动生成源代码再对其编译.在程序运行前代理类的.class文件就已经存在了. 动态:在程序运行时运用反射机制动态创建而成. 我们根据加载被代理类的时机不同,将代理分为静态代理和动态代理.如果我们在代码编

静态代理与动态代理

记得刚接触代理,是在大话设计模式,最近在技术总结和学些java的过程又接触到静态代理和动态代理,尤其是动态代理,在学习AOP时,用到了动态代理,下面我用一些例子来对静态代理和动态代理做个总结. 其实用了代理之后最大的好处就是隐藏了真实类(委托类),这样更加安全,而静态代理和动态代理最大的区别就是,静态代理的代理类是程序员自己写的,在程序运行之前就已经存在的,而动态代理则是在程序运行时动态生成的,而且因为动态代理更加灵活,也常被应用. 首先先用一张UML图来理解一下代理是怎么回事? 其实静态代理就