1、代理模式

Snipaste_2021-07-20_07-33-31.jpg

1.1、静态代理

  • 抽象角色:一般会使用接口或者抽象类来解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理真实角色后,我们一般会做一些额外操作
  • 客户:访问代理对象的人!

代码步骤:

  1. 接口

    1. package demo01;
    2. public interface Rent {
    3. //租房
    4. public void rent();
    5. }
  2. 真实角色

    1. package demo01;
    2. //房东-->租房子
    3. public class Host implements Rent{
    4. @Override
    5. public void rent() {
    6. System.out.println("房东要出租房子!!");
    7. }
    8. }
  3. 代理角色 ```java package demo01;

public class Proxy implements Rent{ private Host host;

  1. //租房子
  2. @Override
  3. public void rent() {
  4. host.rent();
  5. SeeHouse();
  6. Money();
  7. }
  8. //看房
  9. public void SeeHouse(){
  10. System.out.println("中介带你看房!");
  11. }
  12. //收中介费
  13. public void Money(){
  14. System.out.println("中介要收中介费!");
  15. }
  16. public Proxy() {
  17. }
  18. public Proxy(Host host) {
  19. this.host = host;
  20. }

}


4. **客户端访问代理角色**
```java
package demo01;

public class Client {
    public static void main(String[] args) {
        Host host = new Host();
        Proxy proxy = new Proxy(host);
        proxy.rent();
    }
}

代理模式的好处:

  • 可以使真实角色的操作更加纯粹,不用再去关注一些公共的业务。
  • 公共业务就交给了代理角色,实现了业务的分工。
  • 公共业务发生扩展的时候,方便集中管理!

1.2、动态代理

  • 动态代理和静态代理的角色是一样的。

    • 抽象角色:一般会使用接口或者抽象类来解决
    • 真实角色:被代理的角色
    • 代理角色:代理真实角色,代理真实角色后,我们一般会做一些额外操作
    • 客户:访问代理对象的人!
  • 动态代理的代理类是动态生成的,不是我们直接写好的!

  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
    • 基于接口:JDK动态代理
    • 基于类:cglib
    • Java字节码实现:javasist

2、AOP

2.1、什么是AOP?

Aspect Oriented Programming 面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在程序运行期间,不修改源码的基础上,对我们的已有方法进行增强。
**

2.2、相关术语

在业务层接口中有被增强的方法和未被增强的方法。

  • 连接点(Joinpoint):业务层接口中所有的方法都是连接点
  • 切入点(Pointcut):业务层接口中要被增强的方法就是切入点
  • 通知(Advice):其实就是业务层方法的一些公共重复代码
  • 通知的类型:前置通知、后置通知、异常通知、最终通知、环绕通知
  • 目标对象(Target):指的是被代理对象
  • 织入(Weaving):被代理对象无法实现的功能,使用了动态代理技术创建了代理对象,在返回代理对象的时候在其中加入了事务的支持,而加入事务的支持的过程就叫做织入。
  • 代理(Proxy):指的是代理对象
  • 切面(Aspect):是切入点和通知的结合。建立切入点方法和通知方法在执行调用的对应关系就是切面。其实就是要将增强的内容配置进来。
  • 切入点方法:是指在业务层中要被增强的方法
  • 通知方法:通知类中的方法

Spring框架会监控切入点方法的执行。一旦监控到切入点方法被运行,使用动态代理机制,动态创建目标对象(被代理对象)的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整代码的逻辑运行。比如在业务层的转账方法一执行的时候,Spring框架就能知道你要进行转账操作了,接下来它就会根据你的配置,执行什么时候开启事务、什么时候提交事务、什么时候回滚等等,前提是你要配置清楚。

2.3、使用Spring实现AOP

【重点】使用AOP织入,需要导入一个依赖包

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

方式一:使用Spring的API接口

**

  • UserService接口 ```java package service;

public interface UserService { public void insert(); public void delete(); public void update(); public void select(); }


- UserServiceImpl实现类
```java
package service;

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

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

    @Override
    public void update() {
        System.out.println("更新了一个用户");
    }

    @Override
    public void select() {
        System.out.println("查询了一个用户");
    }
}
  • 前置日志 ```java package log; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method;

public class BeforeLog implements MethodBeforeAdvice { /**

 * @param method  要执行的目标对象的方法
 * @param objects  参数
 * @param o 目标对象
 * @throws Throwable
 */
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
    System.out.println(o.getClass().getName()+"的"+method.getName()+"执行了");
}

}


- 后置日志
```java
package log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;

public class AfterLog implements AfterReturningAdvice {
    /**
     * @param o 返回值
     * @param method    要执行的目标对象的方法
     * @param objects   参数
     * @param o1   目标对象
     * @throws Throwable
     */
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("执行了"+method.getName()+",返回值为:"+o);
    }
}
  • applicationContext.xml配置文件 ```xml <?xml version=”1.0” encoding=”UTF-8”?>

<!--配置aop,导入aop的约束-->
<aop:config>
    <!--切入点:expression表达式:execution()-->
    <aop:pointcut id="pointcut" expression="execution(* service.UserServiceImpl.*(..))"/>
    <!--执行环绕增加!-->
    <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
</aop:config>


- 测试
```java
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.UserService;
import service.UserServiceImpl;

public class Junit {
    @Test
    public void test(){
        ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
        //注意!动态代理,代理的是接口
        UserService userService = (UserService) context.getBean("userService");
        userService.delete();
        userService.insert();
        userService.select();
        userService.update();
    }
}
  • 控制台

Snipaste_2021-07-20_18-24-29.jpg

方式二:使用自定义类来实现AOP

  • UserService接口 ```java package service;

public interface UserService { public void insert(); public void delete(); public void update(); public void select(); }


- UserServiceImpl实现类
```java
package service;

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

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

    @Override
    public void update() {
        System.out.println("更新了一个用户");
    }

    @Override
    public void select() {
        System.out.println("查询了一个用户");
    }
}
  • 自定义类 ```java package diy;

public class DiyPointCut { public void before(){ System.out.println(“=====方法执行前====”); }

public void after(){
    System.out.println("====方法执行后=-===");
}

}


- applicationContext.xml配置文件
```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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--注册Bean-->
    <bean id="userService" class="service.UserServiceImpl"/>
    <bean id="afterLog" class="log.AfterLog"/>
    <bean id="beforeLog" class="log.BeforeLog"/>

<!--方式二:使用自定义类来实现AOP-->
      <!--注册Bean-->
    <bean id="diy" class="diy.DiyPointCut"/>
    <!--自定义切面,ref 要引用的类-->
    <aop:config>
        <aop:aspect ref="diy">
            <!--切入点-->
            <aop:pointcut id="point" expression="execution(* service.UserServiceImpl.*(..))"/>
            <!--通知  pointcut-ref 切入点-->
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>
</beans>
  • 测试

Snipaste_2021-07-20_18-24-29.jpg

方式三:使用注解实现AOP

  • UserService接口 ```java package service;

public interface UserService { public void insert(); public void delete(); public void update(); public void select(); }


- UserServiceImpl实现类
```java
package service;

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

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

    @Override
    public void update() {
        System.out.println("更新了一个用户");
    }

    @Override
    public void select() {
        System.out.println("查询了一个用户");
    }
}
  • 切面类

使用@Aspect注解标注这是一个切面类

package diy;

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

//标注这个类是一个切面
@Aspect
public class AnnotationPointCut {
    @Before("execution(* service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("----方法执行前----");
    }

    @After("execution(* service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("---方法执行后---");
    }

    @Around("execution(* service.UserServiceImpl.*(..))")
    public void around(){
        System.out.println("环绕通知");
    }
}
  • applicationContext.xml配置文件 ```xml <?xml version=”1.0” encoding=”UTF-8”?>

<!--注册Bean-->
<bean id="annotationPointCut" class="diy.AnnotationPointCut"/>
<!--开启注解支持-->
<aop:aspectj-autoproxy/>


- 测试
```java
package diy;

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

//标注这个类是一个切面
@Aspect
public class AnnotationPointCut {
    @Before("execution(* service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("----方法执行前----");
    }

    @After("execution(* service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("---方法执行后---");
    }

    @Around("execution(* service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("============环绕通知===========");
        Object[] args = pjp.getArgs(); //得到方法执行所需的参数
        pjp.proceed(args); //明确切入点方法的调用
    }
}
  • 控制台

Snipaste_2021-07-20_18-24-29.jpg

切入点表达式说明

1,访问修饰符可以省略
void com.itheima.service.UserServiceImpl.add()

2,返回值可以使用通配符,表示任意返回值
`
com.itheima.service.UserServiceImpl.add()`

3,包名可以使用通配符表示任意包。但是有几级包就要写几个
`
....UserServiceImpl.add()`

4,包名可以使用..表示当前包及其子包
* *..*.UserServiceImpl.add()

5,类名和方法名都可以实现通配符
* *..*.*()

6,参数列表:

  • 可以直接写数据类型:
  • 基本数据类型写名称
  • 引用类型写包名.类名的方式 java.lang.String
  • 可以使用通配符写任意类型,但是必须有参数
  • 可以使用..表示有无参数均可,有参数可以是任意类型

在实际开发中,我们都是对业务层的方法进行增强,所以切入点表达式都是切到业务层的实现类

execution(* com.itheima.service.UserServiceImpl.*(..))