Spring-AOP

一、代理模式(GoF)

1.静态代理

 

一个原先已有的订单程序,客户想要看到每个订单步骤的耗时,第一种方法可以使用继承,再创建一个类继承原有的目标对象,但由于耦合度高,不采用这种方法。第二种方法采用代理模式(关联关系),耦合度低。创建一个代理类连接公共接口,定义一个接口的属性,若定义目标接口属性则耦合度高,应定义接口的属性,因为此接口还实现了目标对象,类似于多态,再调用方法。

代理模式:将目标对象作为代理对象的一个属性,这种关系叫做关联关系,比继承关系耦合度低,代理对象含有目标对象的引用。这里要写一个公共接口类型,因为公共接口耦合度低。

公共接口:

package com.hei;
public interface OrderService {//公共接口
    //生成订单
    void generate();
    //修改订单
    void modif();
    //查看订单详情
    void detail();
}

目标对象:

package com.hei;
public class OrderImp implements OrderService{//目标对象
    @Override
    public void generate() {
        try {
            Thread.sleep(2455);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("订单正在生成...");
    }

    @Override
    public void modif() {
        try {
            Thread.sleep(1655);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("订单正在修改...");
    }

    @Override
    public void detail() {
        try {
            Thread.sleep(1200);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("正在查看订单详情...");
    }
}

代理对象:

package com.hei;
public class OrderGof implements OrderService{//代理对象,增强功能
    private OrderService target;//传目标对象
    public OrderGof(OrderService target) {
        this.target = target;
    }

    @Override
    public void generate() {//代理方法
        long start=System.currentTimeMillis();
        target.generate();
        long end=System.currentTimeMillis();
        System.out.println("耗时时间:"+(end-start));
    }

    @Override
    public void modif() {
        long start=System.currentTimeMillis();
        target.modif();
        long end=System.currentTimeMillis();
        System.out.println("耗时时间:"+(end-start));
    }

    @Override
    public void detail() {
        long start=System.currentTimeMillis();
        target.detail();
        long end=System.currentTimeMillis();
        System.out.println("耗时时间:"+(end-start));
    }

}

主类:

public class Main {
    public static void main(String[] args) {
        //创建目标对象
        OrderService target=new OrderImp();
        //创建代理对象
        OrderService Gof=new OrderGof(target);
        //调用代理方法
        Gof.generate();
        Gof.modif();
        Gof.detail();
    }
}

2.动态代理

1)JDK动态代理

JDK动态代理只能代理接口。

JDK动态代理,在内存在自动生成代理类,需要在主类中创建代理对象即可,创建代理对象需注意三个参数:类加载器、接口、调用处理器。

创建代理对象:newProxyInstance.

 三个参数(类加载器、接口、调用处理器)

类加载器:

接口:

 调用处理器:

InvocatonHandler接口的方法的三个参数:

invoke方法执行,使用method来调用目标对象的目标接口。

具体代码

公共接口:

package com.hei;
public interface OrderService {//公共接口
    String getName();
    //生成订单
    void generate();
    //修改订单
    void modif();
    //查看订单详情
    void detail();
}

 目标对象类:

package com.hei;
public class OrderImp implements OrderService{//目标对象

    @Override
    public String getName() {
        System.out.println("获得名字");
        return "张三";
    }

    @Override
    public void generate() {
        try {
            Thread.sleep(2455);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("订单正在生成...");
    }

    @Override
    public void modif() {
        try {
            Thread.sleep(1655);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("订单正在修改...");
    }

    @Override
    public void detail() {
        try {
            Thread.sleep(1200);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("正在查看订单详情...");
    }
}

InvocatonHandler接口类:

package com.hei;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class TimeInvocationHandler implements InvocationHandler {
    private Object target;//目标对象

    public TimeInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start=System.currentTimeMillis();
        //调用目标对象的目标方法
       Object result= method.invoke(target,args);
       long end=System.currentTimeMillis();
        System.out.println("耗时"+(end-start)+"毫秒");
        return result;
    }
}

主类:

package com.hei;
import java.lang.reflect.Proxy;
public class Main {
    public static void main(String[] args) {
        //创建目标对象
        OrderService target=new OrderImp();
        //创建代理对象
       OrderService OS=(OrderService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),new TimeInvocationHandler(target));
       //调用代理对象的方法
       OS.generate();
       OS.modif();
       OS.detail();
      String name=OS.getName();
      System.out.println(name);

    }
}

2)CGLIB动态代理

 了解即可

二、面向切面编程(AOP)

把与业务逻辑没有关系的代码,提取出来叫做面向切面编程。

 1.七大术语

 连接点、切点、通知、切面。

2.切点表达式

3.Spring AOP基于注解

目标类:

package com.hei.service;
import org.springframework.stereotype.Service;
//纳入Spring容器进行管理,需要添加注解
@Service
public class UserService {//目标类
    public void login(){//目标方法
        System.out.println("系统正在进行身份验证");
    }
    public void logout(){
        System.out.println("退出系统");
    }
}

切面:

package com.hei.service;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component
@Aspect //切面是使用这个标注的
public class LogAspect{//切面
    //切面=切点+通知
    // 通知就是增强,就是具体要编写的增强代码
    //通知有五类通知,@通知(切点表达式)
    @Before("execution(* com.hei.service.UserService.*(..))")
    public void Strong(){
        System.out.println("这是一段增强代码,前置通知");
    }
}

spring.xml:

开启自动代理:Spring容器在扫描类的时候,查看该类是否有@Aspect注解,若有,则给这个类生成代理对象。

proxy-target-class="true"是强制使用CGLIB动态代理。

proxy-target-class="false"这是默认值,表示接口使用JDK动态代理。

<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 http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd

                          http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--组件扫描-->
    <context:component-scan base-package="com.hei.service"></context:component-scan>
<!--    开启aspectj-->
    <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>

测试类:

public class Test {
    @org.junit.Test
    public void test(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");
       UserService u= applicationContext.getBean("userService", UserService.class);
       u.login();
       u.logout();
    }
}

通知类型

环绕通知是最大的范围。

切面:

@Component
@Aspect //切面是使用这个标注的
public class LogAspect{//切面
    //切面=切点+通知
    // 通知就是增强,就是具体要编写的增强代码
    //通知有五类通知,@通知(切点表达式)
    @Before("execution(* com.hei.service.UserService.*(..))")
    public void Strong(){
        System.out.println("这是一段增强代码,前置通知");
    }
    @Around("execution(* com.hei.service.UserService.*(..))")
    public void rong(ProceedingJoinPoint p) throws Throwable {
        System.out.println("前环绕");
        //执行目标
        p.proceed();
        System.out.println("后环绕");
    }
}

切面顺序

利用@Order(数字),数字越小,越先执行。

通用切点

创建一个方法即可,方法里不需要写任何代码,只是作为一个标记。

@Component
@Aspect //切面是使用这个标注的
public class LogAspect{//切面
    //通用切点
    @Pointcut("execution(* com.hei.service.UserService.*(..))")
    public void Normal(){

    }
    //切面=切点+通知
    // 通知就是增强,就是具体要编写的增强代码
    //通知有五类通知,@通知(切点表达式)
    @Before("Normal()")
    public void Strong(){
        System.out.println("这是一段增强代码,前置通知");
    }
    @Around("Normal()")
    public void rong(ProceedingJoinPoint p) throws Throwable {
        System.out.println("前环绕");
        //执行目标
        p.proceed();
        System.out.println("后环绕");
    }
}

连接点

4.Spring AOP基于XML

目标类:

package com.hei.service;
public class UserService {//目标类
    public void login(){//目标方法
        System.out.println("系统正在登录");
    }
}

切面:

package com.hei.service;
import org.aspectj.lang.ProceedingJoinPoint;
public class TimeAspect {
    public void round(ProceedingJoinPoint p) throws Throwable {//环绕
        long start=System.currentTimeMillis();
        p.proceed();//执行目标
        long end=System.currentTimeMillis();
        System.out.println("耗时"+(end-start)+"毫秒");
    }
}

spring.xml(配置文件):

<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 http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                          http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userservice" class="com.hei.service.UserService"></bean>
<bean id="timeAspect" class="com.hei.service.TimeAspect"></bean>
<!--    aop配置-->
    <aop:config>
<!--        切点-->
        <aop:pointcut id="point" expression="execution(* com.hei.service.UserService.*(..))"/>
<!--        切面-->
        <aop:aspect ref="timeAspect">
            <aop:around method="round" pointcut-ref="point"></aop:around>
        </aop:aspect>
    </aop:config>
</beans>

测试类:

public class Test {
    @org.junit.Test
    public void test(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");
       UserService u= applicationContext.getBean("userservice", UserService.class);
       u.login();
    }
}

  • 14
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值