首页 > 技术文章 > Spring5:面向切面

applesnt 2020-04-01 16:20 原文

静态代理

缺点:一个真实角色就会产生一个代理角色,代码量会翻倍!

场景:要在写好的实现方法上加入日志功能(公共功能),不要修改原代码

1:原代码

业务接口:

package com.spring;

public interface UserService {

    public void addUser(String id);

    public void delUser(String id);

}

业务实现(真实角色):

package com.spring;

public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String id) {
        System.out.println("添加了一个用户");
    }

    @Override
    public void delUser(String id) {
        System.out.println("删除了一个用户");
    }

}

实例化对象:

<bean id="userService" class="com.spring.UserServiceImpl"></bean>

测试:

@Test
public void test04(){
    //获取容器
    ApplicationContext applicationContext =
            new ClassPathXmlApplicationContext("applicationContext.xml");
    //获取bean
    UserServiceImpl userService = applicationContext.getBean("userService", UserServiceImpl.class);
    userService.addUser("1001");
}

2:增加日志功能(代理实现)

增加一个代理类,实现业务接口:(因为代理角色要代理实现真实角色同样的业务)
1:要在代理类中,定义真实角色 并且注入真实角色
2:调用真实角色的业务,并插入代理角色自己的附属业务

package com.spring;

/*代理类*/
package com.spring;

/*代理类*/
public class UserServiceProxy implements UserService{
    /*代理角色要代理真实角色*/
    UserServiceImpl userService;

    /*给真实角色,添加set方法*/
    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }

    @Override
    public void addUser(String id) {
        this.log("add");
        userService.addUser(id);
    }

    @Override
    public void delUser(String id) {
        this.log("del");
        userService.delUser(id);
    }

    /*代理角色的附属业务 日志功能*/
    public void log(String msg){
        System.out.println("执行了"+msg+"操作");
    }
}

实例化对象:

<!--实例化真实角色-->
<bean id="userService" class="com.spring.UserServiceImpl"></bean>

<!--实例化代理角色-->
<bean id="userServiceProxy" class="com.spring.UserServiceProxy">
    <!--注入真实角色-->
    <property name="userService" ref="userService"></property>
</bean>

测试:

@Test
public void test05(){
    //获取容器
    ApplicationContext applicationContext =
            new ClassPathXmlApplicationContext("applicationContext.xml");
    //获取代理bean
    UserServiceProxy userServiceProxy = applicationContext.getBean("userServiceProxy", UserServiceProxy.class);
    userServiceProxy.addUser(“1001”);
}

打印结果:

执行了add操作
添加了一个用户

动态代理

通过反射机制,解决静态代理的缺点;动态代理的代理类是自动生成的;基于JDK的接口实现
invocationhandler:接口 实现invoke方法
proxy:动态代理类

package com.spring;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/*动态代理类要实现InvocationHandler接口*/
public class UserServiceDynamicProxy implements InvocationHandler {

    /*代理角色要代理真实角色*/
    UserServiceImpl userService;

    /*给真实角色,添加set方法*/
    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }

    /*生成并得到代理类
    * 1:classloader:
    * 2:真实角色:根据不同的真实角色 做修改
    * 3:InvocationHandler
    * return : 业务接口
    * */
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), userService.getClass().getInterfaces(), this);
    }
    /*自动处理代理实例 并返回结果*/
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        //调用附属业务
        this.log(method.getName());
        //获取参数
        String id = args[0].toString();
        System.out.println("参数为:"+ id);

        //相当于执行业务方法
        Object result = method.invoke(userService,args);
        //返回业务执行结果
        return result;
    }

    /*代理角色的附属业务 日志功能*/
    public void log(String msg){
        System.out.println("执行了"+msg+"操作");
    }
}

实例化对象:

<!--实例化真实角色-->
<bean id="userService" class="com.spring.UserServiceImpl"></bean>

<!--实例化代理角色-->
<bean id="userServiceDynamicProxy" class="com.spring.UserServiceDynamicProxy">
    <!--注入真实角色-->
    <property name="userService" ref="userService"></property>
</bean>

测试:

@Test
public void test06(){
    //获取容器
    ApplicationContext applicationContext =
            new ClassPathXmlApplicationContext("applicationContext.xml");
    //获取动态代理bean
    UserServiceDynamicProxy userServiceDynamicProxy = applicationContext.getBean("userServiceDynamicProxy", UserServiceDynamicProxy.class);
    /*得到这个代理类 返回业务接口*/
    UserService proxy = (UserService) userServiceDynamicProxy.getProxy();
    //调用业务方法
    proxy.addUser("1001");
}

打印结果:

执行了addUser操作
参数为:1001
添加了一个用户

面向切面

面向切面主要应用于日志、安全、缓存、事务管理
切面:切入的类,比如 log类
通知:切入的具体方法,比如 log里面的一个方法
目标:在哪切入
切入点:什么时候执行,比如业务方法执行前或者执行后

面向切面环境配置:
1:导入AOP命名空间:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
    
</beans>

2:导入依赖包:

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.5</version>
</dependency>

一、Spring API实现AOP

定义业务接口:

package com.spring;

public interface UserService {

    public void addUser(String id);

    public void delUser(String id);
}

实现业务接口:

package com.spring;

public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String id) {
        System.out.println("添加了一个用户");
    }

    @Override
    public void delUser(String id) {
        System.out.println("删除了一个用户");
    }

}

定义切面和通知(类和方法):
前置通知:MethodBeforeAdvice

package com.spring.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class BeforeLog implements MethodBeforeAdvice {

    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("业务前日志被调用 信息为"+o.getClass().getName()+"类的"+method.getName()+"的方法");
    }
}

后置通知:AfterReturningAdvice

package com.spring.log;

import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;

public class AfterLog implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("业务后日志被调用 信息为:执行了"+method.getName()+" 返回信息为"+o);
    }
}

实例化对象以及配置AOP:

<bean id="userService" class="com.spring.UserServiceImpl"></bean>

<bean id="beforeLog" class="com.spring.log.BeforeLog"></bean>
<bean id="afterLog" class="com.spring.log.AfterLog"></bean>

<!--配置AOP面向切面-->
<aop:config>
    <!--定义切入点-->
    <aop:pointcut id="point" expression="execution(* com.spring.UserServiceImpl.*(..))"></aop:pointcut>
    <!--执行切入 以及关联切入点-->
    <aop:advisor advice-ref="beforeLog" pointcut-ref="point"></aop:advisor>
    <aop:advisor advice-ref="afterLog" pointcut-ref="point"></aop:advisor>
</aop:config>

测试:

@Test
public void test04(){
    //获取容器
    ApplicationContext applicationContext =
            new ClassPathXmlApplicationContext("applicationContext.xml");
    //动态代理 代理的是接口 所以要返回一个接口
    UserService userService = applicationContext.getBean("userService", UserService.class);
    userService.addUser("1001");
}

打印结果:

业务前日志被调用 信息为com.spring.UserServiceImpl类的addUser的方法
添加了一个用户
业务后日志被调用 信息为:执行了addUser 返回信息为null

二、自定义类实现AOP

业务接口和实现类基于【Spring API实现AOP】
定义一个自定也切面和通知:

package com.spring.log;

public class MyLog {

    public void before(){
        System.out.println("业务前调用");
    }

    public void after(){
        System.out.println("业务后调用");
    }
}

实例化对象以及配置AOP:

<!--配置AOP面向切面-->
<aop:config>
    <!--自定义切面-->
    <aop:aspect ref="myLog">
        <!--定义切入点-->
        <aop:pointcut id="point" expression="execution(* com.spring.UserServiceImpl.*(..))"></aop:pointcut>
        <!--执行切入 以及关联切入点-->
        <aop:before method="before" pointcut-ref="point"></aop:before>
        <aop:after method="after" pointcut-ref="point"></aop:after>
    </aop:aspect>
</aop:config>

测试:
测试方法基于【Spring API实现AOP】

打印结果:

业务前调用
添加了一个用户
业务后调用

三、自定义类实现AOP

业务接口和实现类基于【Spring API实现AOP】
定义一个自定也切面和通知:

package com.spring.log;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect/*标记这是一个切面*/
public class MyLog {

    /*定义切入点*/
    @Before("execution(* com.spring.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("业务前调用--注解版");
    }

    @After("execution(* com.spring.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("业务后调用---注解版");
    }
}

实例化对象以及配置AOP: 要开启AOP注解支持

<bean id="userService" class="com.spring.UserServiceImpl"></bean>

<bean id="myLog" class="com.spring.log.MyLog"></bean>

<!--开启AOP的注解支持-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

测试:
测试方法基于【Spring API实现AOP】

打印结果:

业务前调用--注解版
添加了一个用户
业务后调用---注解版

推荐阅读