Spring AOP

JAVA代理

代理模式(Proxy)是通过代理对象访问目标对象,这样可以在目标
对象基础上增强额外的功能,如添加权限,访问控制和审计等功能

代理对象:
1、代理对象存在的价值主要用于拦截对真实业务对象的访问。
2、代理对象应该具有和目标对象(真实业务对象)相同的方法。

代理分类:
1、静态代理
2、动态代理(jdk动态代理和Cglib动态代理)

静态代理实现原理

静态代理实现原理.png

实现
静态代理实现.png

springAOP实现方式

1.动态代理 使用 Proxy机制,使用反射技术,创建实际对象的代理对象,添加AOP的逻辑后执行。
2.动态字节码增强 ASM CGLIB的工具库实现

aop实现⽅式1:

interface接口

public interface IUserService<T> {

    /**
     * 获取所有用户列表
     * @return
     */
    List<T> getAllUser();

    /**
     * 保存用户
     * @param user
     * @return
     */
    boolean saveUser(T user);

    /**
     * 根据uid删除用户
     * @param uid
     * @return
     */
    boolean deleteUser(int uid);

    /**
     * 更新用户
     * @param obj
     * @return
     */
    boolean updateUser(T obj);
}

implements

public class UserServiceImpl implements IUserService<Object>{
    public List<Object> getAllUser() {
        System.out.println("-------getAllUser----------");
        return new ArrayList<Object>();
    }

    public boolean saveUser(Object user) {
        System.out.println("-------saveUser----------");
        return true;
    }

    public boolean deleteUser(int uid) {
        System.out.println("-------deleteUser----------");
        return true;
    }

    public boolean updateUser(Object obj) {
        System.out.println("-------updateUser----------");
        return true;
    }

Aspect

public class MyAspect {
    public void before(){
        System.out.println("-----------before---------------");
    }
    public void after(){
        System.out.println("-----------after---------------");
    }
}

factory

public class UserFactory {
    public static IUserService getUserService(){
        final IUserService userService=new UserServiceImpl();
        final MyAspect ma=new MyAspect();
        IUserService ius= (IUserService) Proxy.newProxyInstance(UserFactory.class.getClassLoader(), userService.getClass().getInterfaces(), new InvocationHandler() {
            /**
             * 代理对象调用的回调方法
             * @param proxy 代理对象
             * @param method 被代理的方法
             * @param args 被代理的方法的参数列表对象
             * @return 每个方法的最终返回值
             * @throws Throwable
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                ma.before();
                System.out.println("method:"+method);
                System.out.println("args:"+args);
                Object obj = method.invoke(userService,args);
                ma.after();
                System.out.println("obj:"+obj);
                return obj;
            }
        });
        return ius;
    }

AOP实现方式2

public class UserFactory {

    /**
     * 使用spring中的一个增强类实现aop方式
     *      1、创建Enhancer对象
     *      2、设置增强类Enhancer的superClass
     *      3、设置Enhancer对象的回调
     *      4、通过eh对象的create()方法来指定对象
     * @return
     */
    public static IUserService getUserService(){
        final MyAspect ma = new MyAspect();
        final IUserService<Object> userService=new UserServiceImpl();
        //1、创建Enhancer对象
        Enhancer eh=new Enhancer();
        //2、设置增强类Enhancer的superClass
        eh.setSuperclass(IUserService.class);
        //3、设置Enhancer对象的回调
        eh.setCallback(new MethodInterceptor() {
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                ma.before();
                System.out.println("method:"+method);
                //System.out.println("objects:"+objects);
                Object obj=method.invoke(userService,objects);
                ma.after();
                System.out.println("obj:"+obj);
                return obj;
            }
        });
        //4、通过eh对象的create()方法来指定对象
        IUserService<Object> iUserService= (IUserService<Object>) eh.create();
        return iUserService;
    }

AOP实现方式3


    <bean id="us" class="com.qfedu.aop3.UserServiceImpl"/>
    <bean id="my" class="com.qfedu.aop3.MyAspect"/>
    <!--
        ProxyFactoryBean代理的FactoryBean对象,我们现在要代理的是us
            包含四个属性注入:
                1.  interfaces: 接口对象们
                    <list>
                        <value>com.qfedu.aop03.IUserService</value>
                        <value>com.qfedu.aop03.IUserService</value>
                        <value>com.qfedu.aop03.IUserService</value>
                    </list>
                2.  target:目标对象,哪个对象将被以代理的方式创建
                3.  interceptorNames:拦截对象的名称,自定义的MethodInterceptor对象,注意它的包结构组成
                4.  optimize:boolean类型的值:
                        true:强制使用cglib的动态代理方式
                        false:使用jdk自带的动态代理

                        cglib:code generation library,代码生成库,性能更高

    -->
    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="interfaces" value="com.qfedu.aop3.IUserService"/>
        <property name="target" ref="us"/>
        <property name="interceptorNames" value="my"/>
        <property name="optimize" value="true"/>
    </bean>

MyAscept

public void before(){
        System.out.println("-----------before---------------");
    }
    public void after(){
        System.out.println("-----------after---------------");
    }

    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        before();
        //处理业务方法的调用
        Object obj = methodInvocation.proceed();
        after();
        return obj;
    }

Test类

public class TestUser {
    @Test
    public void testUser(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("applications.xml");
        IUserService us=ac.getBean("proxy",IUserService.class);
        System.out.println(us.getAllUser());
        System.out.println(us.saveUser(new Object()));
        System.out.println(us.deleteUser(1));
        System.out.println(us.updateUser(new Object()));
    }
}

简单AOP实现

Spring 方面可以使用下面提到的五种通知工作:


5种通知.png

先添加aspectj依赖

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

AOP实现方式4

<bean id="us" class="com.qfedu.aop4.UserServiceImpl"/>
    <bean id="ma" class="com.qfedu.aop4.MyAspect"/>
    <!--
        proxy-target-class设置为true,强制使用cglib方式来实现动态代理
    -->
    <aop:config proxy-target-class="true">
        <!--
            定义一个aop的切点
            该包com.qfedu.aop04下所有的任意类,类下的任意含参不含参数的方法,返回值必须为List的方法将会被代理
            <aop:pointcut id="pt" expression="execution(java.util.List com.qfedu.aop04.*.*(..))" />
            该包com.qfedu.aop04下所有的任意类,类下的任意含参不含参数的方法,返回值必须为boolean的方法将会被代理
            <aop:pointcut id="pt" expression="execution(boolean com.qfedu.aop04.*.*(..))" />

            com.qfedu.aop04.UserServiceImpl.deleteUser(int)具体的方法了,参数为int,返回值任意
            <aop:pointcut id="pt" expression="execution(* com.qfedu.aop04.UserServiceImpl.deleteUser(int))" />
        -->
        <aop:pointcut id="pt" expression="execution(* com.qfedu.aop4.*.*(..))"/>
        <!--
            通知,将MyAspect与切点关联起来
        -->
        <aop:advisor advice-ref="ma" pointcut-ref="pt"/>
    </aop:config>

AOP实现方式5

MyAspect

public class MyAspect {
/**
 * 五种通知方式来实现aop
 *  1. 前置通知,在业务方法之前执行
 *  2. 后置通知,在业务方法之后执行
 *  3. 环绕通知,同时在业务方法的前后执行
 *  4. 带有返回值通知,可以拿到业务方法的返回值
 *  5. 异常通知,可以捕获业务方法中的异常对象
 *
 *      注意:如果同时配置来所有的通知方式,则执行顺序依次为:
 *          before>around before>业务方法 >after returning > around after > after
 *          before>around before>业务方法 >after throwing >  after
 *
 *          ps. 使用注解的话是环绕通知proceed方法之前部分先执行,使用xml配置的话取决于aop:before和aop:around的配置顺序
 */
    public void myBefore(JoinPoint jp){
        System.out.println("this is myBefore");
    }
    public void myAfter(JoinPoint jp){
        System.out.println("this is myAfter");
    }
    public Object myAround(ProceedingJoinPoint pjp){
        try {
            System.out.println("this is myAroundBefore");
            Object obj = pjp.proceed();
            System.out.println("this is myAroundAfter:"+obj);
            return obj;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }
    public void myReturn(JoinPoint jp,Object obj){
        System.out.println("this is returning:"+obj);
    }
    public void myThrowing(JoinPoint jp,Throwable e){
        System.out.println("this is throwing:"+e.getMessage());
    }
}

<bean id="us" class="com.qfedu.aop5.UserServiceImpl"/>
    <bean id="ma" class="com.qfedu.aop5.MyAspect"/>
    <aop:config expose-proxy="true">
        <aop:aspect ref="ma">
            <aop:pointcut id="mpc" expression="execution(* com.qfedu.aop5.*.*(..))"/>
            <aop:before method="myBefore" pointcut-ref="mpc"/>
            <aop:after method="myAfter" pointcut-ref="mpc"/>
            <aop:around method="myAround" pointcut-ref="mpc"/><!--环绕通知-->
            <aop:after-returning method="myReturn" pointcut-ref="mpc" returning="obj"/>
            <aop:after-throwing method="myThrowing" pointcut-ref="mpc" throwing="e"/>
        </aop:aspect>
    </aop:config>

AOP实现方式6

<!--
    context:component-scan 组件扫描
        base-package指定要扫描的包的路径
    -->
    <context:component-scan base-package="com.qfedu.aop6"/>
    <!--aop:aspectj-autoproxy标签实现自动代理-->
    <aop:aspectj-autoproxy/>

要在实现类中加入注解@Component并命名

@Component("us")
public class UserServiceImpl implements IUserService {
    public List<Object> getAllUser() {
        System.out.println("----------getAllUser-------------");
        return new ArrayList<Object>();
    }

    public boolean saveUser(Object user) {
        System.out.println("----------saveUser-------------");
        return true;
    }

    public boolean deleteUser(int uid) {
        System.out.println("----------deleteUser-------------");
        return true;
    }

    public boolean updateUser(Object obj) {
        System.out.println("----------updateUser-------------");
        return false;
    }
}

在MyAspect中加入注解

/**
 *  以注解的方式实现的切面类MyAspect
 *
 *      当前类中的五种通知方式均以注解方式完成
 */
@Component         //  标注当前类为一个组件
@Aspect                //  标注当前类为一个切面类
public class MyAspect {
/**
     * @Pointcut 注解为了避免相同的匹配规则被定义多处,专门定义该方法设置执行的匹配规则,各个自行调用即可
     *    write once, only once
     */
    @Pointcut(value = "execution(* com.qfedu.aop6.*.*(..))")
    public void setAll(){}
/**
     * @Before 表示该方法为一个前置通知
     * @param jp 连接点
     */
    @Before("setAll()")
    public void myBefore(JoinPoint jp){
        System.out.println("this is myBefore");
    }
/**
     * @After 表示该方法为一个后置通知
     * @param jp 连接点
     */
    @After("setAll()")
    public void myAfter(JoinPoint jp){
        System.out.println("this is myAfter");
    }
/**
     * @Around 表示该方法为一个环绕通知
     * @param pjp 处理连接点
     * @return 返回每个业务方法的返回值
     */
    @Around("setAll()")
    public Object myAround(ProceedingJoinPoint pjp){
        try {
            System.out.println("this is myAroundBefore");
            Object obj = pjp.proceed();
            System.out.println("this is myAroundAfter:"+obj);
            return obj;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }
/**
     * @AfterReturning 表示该方法为一个带有返回值的通知
     * @param jp 连接点
     * @param obj 业务方法的返回值
     */
    @AfterReturning(value = "setAll()",returning = "obj")
    public void myReturn(JoinPoint jp,Object obj){
        System.out.println("this is returning:"+obj);
    }
/**
     * @AfterThrowing 表示该方法为一个带有异常的通知
     * @param jp 连接点
     * @param e Throwable对象
     */
    @AfterThrowing(value = "setAll()",throwing = "e")
    public void myThrowing(JoinPoint jp,Throwable e){
        System.out.println("this is throwing:"+e.getMessage());
    }
}

Test

@Test
    public void testUser(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("beans3.xml");
//此处的us就是实现类中加入注解@Component并命名的us
        IUserService us = ac.getBean("us", IUserService.class);
        us.getAllUser();
        us.deleteUser(1);
        us.saveUser("ysx");
        us.updateUser("yanshixian");
    }

AOP实现方式7(使用BeanPostProcessor方式实现Spring的AOP)

<!--
        context:component-scan上下文的组件扫描
            base-package指定要扫描的包为com.qfedu.aop07
    -->
    <context:component-scan base-package="com.qfedu.aop7"/>
    <!--
       指定BeanPostProcessor的Factory hook,让每个bean对象初始化是自动回调该对象中的回调方法
    -->
    <bean class="com.qfedu.aop7.MyBeanPostProcessor"/>

MyBeanPostProcessor

**
 * 配置了包扫描之后,该类会初始化两个对象EventListenerMethodProcessor和DefaultEventListenerFactory,再外加我们自己的组件对象
 *
 * 所以会发现有三个before打印
 *
 * 我还专门加了一个UserServiceImpl2,你会发现将有四个before的打印(注意看看该类上的Component注解是否启用)
 *
 */
public class MyBeanPostProcessor implements BeanPostProcessor {
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        //System.out.println("this is before");
        return bean;
    }

    public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
        //System.out.println("this is after");
        return Proxy.newProxyInstance(MyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                MyAspect ma = new MyAspect();
                ma.before();
                Object obj = method.invoke(bean, args);
                ma.after();
                return obj;
            }
        });
    }
}

UserServiceImpl

@Component("us")
public class UserServiceImpl implements IUserService {
    public List<Object> getAllUser() {
        System.out.println("----------getAllUser-------------");
        return new ArrayList<Object>();
    }

    public boolean saveUser(Object user) {
        System.out.println("----------saveUser-------------");
        return true;
    }

    public boolean deleteUser(int uid) {
        System.out.println("----------deleteUser-------------");
        return true;
    }

    public boolean updateUser(Object obj) {
        System.out.println("----------updateUser-------------");
        return false;
    }
}

BeanFactory 与 AppliacationContext 有什么区别

  1. BeanFactory
    基础类型的 IOC 容器,提供完成的 IOC 服务支持。如果没有特殊指定,默认采用延迟初始化策略。相对来说,容
    器启动初期速度较快,所需资源有限。
    2.ApplicationContext
    ApplicationContext 是在 BeanFactory 的基础上构建,是相对比较高级的容器实现,除了 BeanFactory 的所有
    支持外,ApplicationContext 还提供了事件发布、国际化支持等功能。ApplicationContext 管理的对象,在容器启动
    后默认全部初始化并且绑定完成。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352