spring学习记录(九)

Spring的动态代理开发

说起代理设计模式,大家应该都不陌生:一个原始类,一个代理类,都实现相同的接口,扩展类依赖原始类,并在接口的实现中,调用原始类的方法,并且在调用原始类中方法的前后,进行扩展功能的注入,这就是静态代理模式。
而java中的动态代理,则是试用Proxy类和InvocationHandler接口,来实现了代理的功能,并且不用写很多的代理类。
下面我们来依次看一下

静态代理:

//接口
public interface UserService {

    public void register(Person person) ;

    public void login(String username, String password) ;
}

//实现类
public class UserServiceImpl implements UserService {
    @Override
    public void register(Person person) {
        System.out.println("UserServiceImpl.register");
    }

    @Override
    public void login(String username, String password) {
        System.out.println("UserServiceImpl.login");
    }
}

//代理类
public class UserServiceImplProxy implements UserService {

    private UserService userService = new UserServiceImpl();

    @Override
    public void register(Person person) {
        System.out.println("UserServiceImplProxy.register before");
        userService.register(person);
        System.out.println("UserServiceImplProxy.register after");
    }

    @Override
    public void login(String username, String password) {
        System.out.println("UserServiceImplProxy.login before");
        userService.login(username, password);
        System.out.println("UserServiceImplProxy.login after");
    }
}

测试方法:

@Test
public void test3(){
    UserService userService = new UserServiceImplProxy();
    userService.login("huwenchao", "password");
    userService.register(new Person());
}

执行结果:
在这里插入图片描述
上面的代码和执行结果,可以看出静态代理的方式

动态代理

@Test
public void test5(){
    UserService userService = new UserServiceImpl();
    UserService serviceProxy = (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println(method.getName() + ": before");
            Object ret = method.invoke(userService, args);
            System.out.println(method.getName() + ": after");
            return ret;
        }
    });
    serviceProxy.register(new Person());
    serviceProxy.login("huwenchao", "password");
}

执行结果:
在这里插入图片描述

以上,java的动态代理,就不需要再出现UserServiceImplProxy这种代理类了,而是试用Proxy和InvocationHandler动态生成了UserServiceImpl的代理类,扩展了原始类中的功能。

spring的动态代理

Spring中,有一套自己的动态代理的开发方式,编写起来会更加的优雅,使用aop的方式来实现,其中最主要的接口是MethodBeforeAdvise和MethodInterceptor

  1. MethodBeforeAdvise:顾名思义,实现了这个接口,会在原始类的原始方法执行之前,进行功能的扩展
  2. MethodInterceptor:将原始类的原始方法进行拦截,可以在原始方法的前后、报错、返回等位置,进行拦截,甚至可以改变返回值。

实现spring的动态代理,主要分为四个步骤:

  1. 原始类的编写
  2. 扩展类的编写,实现上述两个接口
  3. pointcut-切点的声明
  4. 将扩展类和切点进行组装

首先,需要引入spring-aop相关的jar包:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.3.15</version>
</dependency>

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.8.8</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.8</version>
</dependency>
  • 编写原始类
    我们就使用上述的UserService接口和UserServiceImpl实现类
  • 编写扩展类,我们使用MethodInterceptor来说明
public class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("MyMethodInterceptor.invoke  before");
        Object ret = invocation.proceed();
        System.out.println("MyMethodInterceptor.invoke  after");
        return ret ;
    }
}
  • 配置文件中进行声名,包括切点和切点的组装
<bean id="arround" class="com.huwc.dynamic.MyMethodInterceptor"></bean>
<bean id="userService" class="com.huwc.dynamic.UserServiceImpl"></bean>
<aop:config>
   <aop:pointcut id="pc" expression="execution(* login(String, ..))"/>
   <aop:advisor advice-ref="arround" pointcut-ref="pc"></aop:advisor>
</aop:config>

其中,涉及到了切点表达式的编写,我们就不展开了

测试方法:

@Test
public void test2(){
    ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
    UserService service = (UserService) ctx.getBean("userService");
    System.out.println(service.getClass().getName());
    service.register(new Person());
    service.login("huwenchao", "password");
}

执行结果:
在这里插入图片描述
我们可以看到,实现类中的register方法,并没有被拦截,而login方法则被拦截了,这个是和我们指定的切点表达式相关。从上面的执行结果中,我们可以看到从IOC容器中拿到的UserService的bean,其实是一个代理对象,并且成功拦截了login方法,在前后进行了功能的扩展。

其中切点(pointcut)的表达式主要又分为:

  • execution的表达式
  • args表达式
  • within表达式
  • @annotation表达式
    每个表达式,都有自己的含义和作用,并且多个表达之间可以进行and和or的逻辑运行,具体的细节,感兴趣的可以自己查找和学习。

基于注解的spring的动态代理

spring的动态代理,也可以通过注解的方式来编写,会更加的简单和方便,步骤如下

  • xml配置:
<bean id="personService" class="com.huwc.proxy.PersonServiceImpl"></bean>
<bean id="myAspect" class="com.huwc.aspect.MyAspect"></bean>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
  • Aspect–切面类的编写
@Aspect
public class MyAspect {

    @Pointcut("execution(* login(..))")
    public void myPointCut(){}

    @Around(value = "myPointCut()")
    public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("------aspect log --------------");
        Object ret = joinPoint.proceed();

        return ret ;
    }

    @Around(value = "myPointCut()")
    public Object arround2(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("------aspect tx --------------");
        Object ret = joinPoint.proceed();

        return ret ;
    }
}

测试代码:

@Test
public void testAspect(){
    ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext2.xml");
    PersonService service = (PersonService) ctx.getBean("personService");
    service.register(new com.huwc.bean.Person());
    service.login("huwenchao", "password");
}

执行结果:
在这里插入图片描述

注意:

spring的动态代理,有两种类型,一种是java动态代理,基于接口实现,另一种是cglib代理,基于类的继承来实现,spring中默认的代理类型为java动态代理,如果需要修改为cglib代理,则需要修改spring中的配置文件

  • 传统配置
<aop:config proxy-target-class="true">
    <!--   定义切入点:所有的方法都进行拦截     -->
    <aop:pointcut id="pc" expression="execution(* *(..))"/>
    <!--   组装:把切入点和额外的功能,进行组装     -->
    <aop:advisor advice-ref="myMethodInterceptor" pointcut-ref="pc"></aop:advisor>
</aop:config>
  • 注解模式配置
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值