04-SpringMVC 编程方式实现 AOP

相关

  • Spring支持5种类型的增强或通知(advice)
    • before、around、after、afterReturnning

前提

  • Intellij
  • 创建Maven archtype webapp
  • SpringMVC 项目 而非 spring boot
  • 添加 spring-webmvn dependency

案例

service 业务逻辑层
public interface Service {

    void print(String str);
    //void say(String str);
}

public class ServiceImpl implements Service {

    public void print(String str) {
        System.out.println("我是业务方法"+str);
    }
    //public void say(String str) {
    //   System.out.println("222"+str);
    //}
}

aop 定义增强业务

** before 增强 **

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

/**
 * Created by weixin:javajidi_com.
 * 方法执行前的逻辑,称前置通知,
 * 通过编程实现接口
 */
public class BeforeAdvice implements MethodBeforeAdvice{
    /**
    * method: target 方法
    * objects: target 方法需要的参数
    * o:目的所要增强的 target 对象
    **/
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("BeforeAdvice方法执行前");
        System.out.println(method.getName()+";"+o.getClass());
    }
}

round 环绕增强

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/**
 * 实现接口为 MethodInterceptor extends Advice
 */
public class RoundAdvice implements MethodInterceptor {

    //注意参数为:MethodInvocation
    //通过 它可以拿到参数信息
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("Roundadvice方法执行前");
        System.out.println(methodInvocation.getArguments()[0]);//可以获取目标方法的参数值
        Object result=methodInvocation.proceed();//调用目标对象的方法
        System.out.println("RoundAdvice方法执行完成了");
        return result;
    }
}

afterreturnning 增强

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

//实现 接口
public class AfterAdvice implements AfterReturningAdvice {

    /**
    * o: target 方法执行返回结果
    * method: target 方法
    * objects: target 方法参数
    * o1: 要增强 的 target 对象
    **/
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("AfterAdvice方法执行完成了");
        System.out.println(method.getName()+";"+o1.getClass());
    }
}

Test1

import org.springframework.aop.framework.ProxyFactory;
import spring4.Service;
import spring4.ServiceImpl;

/**
 * test1: 不通过 spring bean 配置文件
 */
public class Test {

    public static void main(String[] arg){
        
        //非 Spring上下文 控制 bean, 全部自定义(new)
        
        Service service=new ServiceImpl();//
        //使用代理工厂为目标对象创建代理,
        //我们自己的advice逻辑
        ProxyFactory proxyFactoryBean=new ProxyFactory();
        proxyFactoryBean.setTarget(service);//设置目标对象
        
        BeforeAdvice beforeAdvice = new BeforeAdvice();
        proxyFactoryBean.addAdvice(beforeAdvice);//为目标对象织入增强
        
        AfterAdvice afterAdvice = new AfterAdvice();
        proxyFactoryBean.addAdvice(afterAdvice);
        
        
        RoundAdvice roundAdvice = new RoundAdvice();
        proxyFactoryBean.addAdvice(roundAdvice);
        
        Service proxy=(Service)proxyFactoryBean.getProxy();
        proxy.print("test");
    }
}

classpath 路径下配置 spring.xml 实现

//在resources 目录 创建spring.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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 由Spring 上下文 创建并装配 所有的对象-->
    <bean id="beforeAdvice" class="spring4.aop.BeforeAdvice"/>
    <bean id="afterAdvice" class="spring4.aop.AfterAdvice"/>
    <bean id="roundAdvice" class="spring4.aop.RoundAdvice"/>
    <bean id="service" class="spring4.ServiceImpl"/>
    <bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">

        <property name="interceptorNames">
            <list>
                <value>beforeAdvice</value>

                <value>afterAdvice</value>

                <value>roundAdvice</value>
            </list>
        </property>
        <property name="target" ref="service"></property>
    </bean>

</beans>

test2 通过bean 配置文件和Spring上下文

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import spring4.Service;

public class Test {

    public static void main(String[] arg){
//        Service service=new ServiceImpl();
//        //使用代理工厂为目标对象创建代理,并织入我们自己的advice逻辑
//        ProxyFactory proxyFactoryBean=new ProxyFactory();
//        proxyFactoryBean.setTarget(service);//设置目标对象
//        proxyFactoryBean.addAdvice(new BeforeAdvice());//为目标对象织入增强
//        proxyFactoryBean.addAdvice(new AfterAdvice());
//        proxyFactoryBean.addAdvice(new RoundAdvice());
//        Service proxy=(Service)proxyFactoryBean.getProxy();
//        proxy.print("test");
        
        //获取上下文中的bean
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:spring.xml");
        Service service=applicationContext.getBean("serviceProxy",Service.class);
        service.print("test");
    }
}

**运行结果-增强被织入到目标类的所有方法中 **

BeforeAdvice方法执行前
print;class spring4.ServiceImpl
Roundadvice方法执行前
test
我是业务方法test
RoundAdvice方法执行完成了
AfterAdvice方法执行完成了
print;class spring4.ServiceImpl

更细粒度-指定不同方法的增强
  • spring 通过org.springframework.aop.Pointcut接口描述切点,Pointcut由ClassFilter和MethodMatcher构成。
  • 通过ClassFilter定位到某些特定类上,通过MethodMatcher定位到某些特定方法上
  • 这样Pointcut就拥有了描述某些类的某些特定方法的能力。
<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="beforeAdvice" class="spring4.aop.BeforeAdvice"/>
    <bean id="afterAdvice" class="spring4.aop.AfterAdvice"/>
    <bean id="roundAdvice" class="spring4.aop.RoundAdvice"/>
    <bean id="service" class="spring4.ServiceImpl"/>

    <!--通过正则表达定义增强具体的执行位置-->
    <bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="advice" ref="beforeAdvice"/>
        <!--正则表达式用来表示增加添加到哪些类的哪些方法-->
        <property name="pattern" value="spring4\..*.print.*"/><!--表示应用到spring4包下所有类中的所有print开头的方法上-->
    </bean>
    <bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">

        <property name="interceptorNames">
            <list>
                <value>advisor</value><!--连接到具体执行位置-->

            </list>
        </property>
        <property name="target" ref="service"></property>
    </bean>

</beans>

test3
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import spring4.Service;

/**
 * Created by weixin:javajidi_com.
 */
public class Test {

    public static void main(String[] arg){
//        Service service=new ServiceImpl();
//        //使用代理工厂为目标对象创建代理,并织入我们自己的advice逻辑
//        ProxyFactory proxyFactoryBean=new ProxyFactory();
//        proxyFactoryBean.setTarget(service);//设置目标对象
//        proxyFactoryBean.addAdvice(new BeforeAdvice());//为目标对象织入增强
//        proxyFactoryBean.addAdvice(new AfterAdvice());
//        proxyFactoryBean.addAdvice(new RoundAdvice());
//        Service proxy=(Service)proxyFactoryBean.getProxy();
//        proxy.print("test");

        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:spring.xml");
        //直接从上下文获取 proxy
        Service service=applicationContext.getBean("serviceProxy",Service.class);
        service.print("print");
        //添加多余验证代码,验证其不会被增强
        service.say("say");
    }
}

结果

BeforeAdvice方法执行前
print;class spring4.ServiceImpl
我是业务方法print
say:say

动态生成 proxy

  • 当有很多 目的target 类需要被增强时,需要手动配置很多 proxy
  • Spring 提供自动代理机制
  • 在内部Spring 使用 BeanPostProcessor 自动完成此任务
  • BeanPostProcessor 的实现类: 根据 某些规则 自动在容器实例化 bean时为符合规则条件的bean 生成代理
    • BeanNameAutoProxyCreator
      • 允许为一组特定配置名的Bean自动创建代理
    • DefaultAdvisorAutoProxyCreator
      • 它会对容器中所有的Advisor进行扫描,自动将这些切面应用到匹配的Bean中(即为目标Bean创建代理实例)
    • AnnotationAwareAspectjAutoProxyCreator
      • 为包含AspectJ注解的Bean自动创建代理实例
<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="beforeAdvice" class="spring4.aop.BeforeAdvice"/>
    <bean id="afterAdvice" class="spring4.aop.AfterAdvice"/>
    <bean id="roundAdvice" class="spring4.aop.RoundAdvice"/>
    <bean id="service" class="spring4.ServiceImpl"/>

    <bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="advice" ref="beforeAdvice"/>
        <!--正则表达式用来表示增加添加到哪些类的哪些方法-->
        <property name="pattern" value="spring4\..*.print.*"/><!--表示应用到spring4包下所有类中的所有print开头的方法上-->
    </bean>
    
    <!--会扫描所有容器中的advisor, 如上面定义的 advisor, 这个advisor 可以匹配到 若干个类
    若出现 advisor2, advisor3, advisor4 得写 相应个数 proxyFactoryBean
    然后自动为这些advisor要应用的bean生成代理-->
    <!--bean 即要被增强的 target 类-->
  <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

</beans>

test 4

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import spring4.Service;

/**
 * Created by weixin:javajidi_com.
 */
public class Test {

    public static void main(String[] arg){
//        Service service=new ServiceImpl();
//        //使用代理工厂为目标对象创建代理,并织入我们自己的advice逻辑
//        ProxyFactory proxyFactoryBean=new ProxyFactory();
//        proxyFactoryBean.setTarget(service);//设置目标对象
//        proxyFactoryBean.addAdvice(new BeforeAdvice());//为目标对象织入增强
//        proxyFactoryBean.addAdvice(new AfterAdvice());
//        proxyFactoryBean.addAdvice(new RoundAdvice());
//        Service proxy=(Service)proxyFactoryBean.getProxy();
//        proxy.print("test");

        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:spring.xml");
        //要注意这里 bean name,可以debug,  
        //applicationContext.getBeanNamesForType(Service.class) 拿到 bean 名字
        Service service=applicationContext.getBean("service",Service.class);
        service.print("print");
        service.say("say");
    }
}

参考: https://www.jianshu.com/p/1dd6a26c881b

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,701评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,649评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,037评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,994评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,018评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,796评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,481评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,370评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,868评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,014评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,153评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,832评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,494评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,039评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,437评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,131评论 2 356

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,673评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,827评论 6 342
  • 如下是整篇文章的结构,所需阅读时间大约20min Spring简介 Spring框架由Rod Johnson开发,...
    李序锴阅读 897评论 0 15
  • 什么是Spring Spring是一个开源的Java EE开发框架。Spring框架的核心功能可以应用在任何Jav...
    jemmm阅读 16,468评论 1 133
  • 最近好像陷入一场远距离恋爱,连笑容都很甜蜜。时间也随着渐渐远去,好像闲暇的时光稍纵即逝,原本存了好多电视和电影的...
    yumiruirui阅读 155评论 0 0