Spring基础(五)

AOP(面向切面编程)

springAOP底层实现是使用代理模式!

静态代理模式的角色:

  • 抽象角色:一般使用接口或实现类类解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理角色会增加一些附属操作
  • 客户:访问代理对象的人

代理模式的好处:

  1. 使真实角色更加纯粹,专注自身的业务,而不同考虑一些公共的业务
  2. 公共业务交给代理对象!实现了业务的分工!
  3. 对于扩展业务使用扩展代理类中的业务即可!方便几种管理!

静态代理的缺点:

  • 一个真实对象就会产生一个代理对象;增加了代码量!

动态代理可以解决代码量增加的问题,因为动态代理使用了反射机制! 静态代理在编译时就代理了,动态代理在运行时才进行代理。

  • 动态代理的代理类是动态生成的,不是我们直接写的。

在这里插入图片描述

Spring中AOP大量使用了代理模式进行横向开发!

实现AOP需要拥有的元素

AOP的作用:提供声明式的事务,允许用户自定义切面!

  1. 横切关注点:要横切进入的功能,例如:日志、安全等等。
  2. 切面:横切关注点模块化成的类。(因为横切关注点可能有很多逻辑,放到一个类中好管理。其中放需要扩展的业务方法)
  3. 通知:横切关注点模块化成的类的方法。(就是一个方法就是要扩展业务的方法)
  4. 目标:真实对象。
  5. 代理:代理对象。
  6. 切入点:“切面通知”执行的地方的定义。(就是插入扩展业务的地方)
  7. 链接点:切入点执行的地方。(就是运行代理对象方法的那个地方)

SpringAOP中,通过Advice定义横切逻辑,Spring支持5中Advice:(也就是说用了spring提供的Advice就可以不改变原有代码增加新功能,在连接点增加功能

通知类型 连接点 实现接口
前置通知 方法前 org.springframework.aop.MethodBeforeAdvice
后置通知 方法后 org.springframework.aop.AfterReturningAdvice
环绕通知 方法前后 org.springframework.aop.MethodInterceptor
异常抛出通知 方法异常时 org.springframework.aop.ThorowAdvice
引介通知 类中增加新的方法属性时 org.springframework.aop.IntroductionInterceptor

使用spring实现AOP

使用spring的API接口实现切面类

使用spring的API接口实现切面类,从而达到AOP!

使用AOP之前需要导入AOP的包!

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>
public interface UserService {
    
    
    void add();
    void delete();
    void modify();
    Object select();
}
public class UserServiceImpl implements UserService {
    
    
    public void add() {
    
    
        System.out.println("增加\n");
    }
    public void delete() {
    
    
        System.out.println("删除\n");
    }
    public void modify() {
    
    
        System.out.println("修改\n");
    }
    public Object select() {
    
    
        System.out.println("查询\n");
        return null;
    }
}
// 实现spring提供的接口创建切面
public class Log implements MethodBeforeAdvice {
    
    
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
    
    
        System.out.println("在"+target.getClass().getName()+"中的方法"+method.getName()+"之前执行了!");
    }
}
<?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.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="userService" class="pers.qiu.service.UserServiceImpl"></bean>
    <bean id="log" class="pers.qiu.log.Log"></bean>

    <aop:config>
        <!--需要一个切入点  id 表达式=execution(返回值 类名.方法名(参数) )  表示执行的位置-->
        <!--.*代表所有方法,..代表接收一切参数-->
        <aop:pointcut id="pointcut" expression="execution(* pers.qiu.service.UserServiceImpl.*(..))"/>

        <!--执行环绕增加,就是将log这个切面增加到切入点方法中.它就自动的环绕在业务逻辑之前或之后.-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>

    </aop:config>
    
<!-- 也可以使用多个切入点
	<aop:config>
		<aop:pointcut id="addpointcut" expression="execution(* pers.qiu.service.UserServiceImpl.add(..))"/>

		<aop:pointcut id="deletepointcut" expression="execution(* pers.qiu.service.UserServiceImpl.add(..))"/>

		<aop:advisor advice-ref="log" pointcut-ref="addpointcut"/>
		<aop:advisor advice-ref="log" pointcut-ref="deletepointcut"/>
	</aop:config>
 -->
</beans>

使用了aop配置后,在getBean时获取到的是user Service的代理对象.获取在生成代理对象到spring容器中的时候就已经插入完成了.

public class MyTest {
    
    
    @Test
    public void text01(){
    
    
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) context.getBean("userService");
        userService.add();
    }
}

在这里插入图片描述

有了AOP之后,可以看到:真实类中只写它的业务逻辑;切面类中只写扩展的逻辑;通过xml进行连接配置即可生成连接两者功能的代理类!

使用自定义类实现切面类

使用自定义的类作为切面类,而不使用spring的包中提供的那四种类作为切面类!

// 自定义切面
public class MyCutFact {
    
    
    public void before(){
    
    
        System.out.println("在切点前面执行!");
    }

    public void after(){
    
    
        System.out.println("在切点后面执行!");
    }
}
<?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.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="userService" class="pers.qiu.service.UserServiceImpl"></bean>
    <bean id="log" class="pers.qiu.log.Log"></bean>

    <bean id="cutFact" class="pers.qiu.log.MyCutFact"></bean>
    <aop:config>
        <!--切面-->
        <aop:aspect ref="cutFact">
            <!--切入点-->
            <aop:pointcut id="pointcut" expression="execution(* pers.qiu.service.UserServiceImpl.*(..))"/>
			<!--通知-->
            <aop:before method="before" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>
</beans>

可以看到:使用AOP需要:

  • 切面类
  • 切入点
  • 切面类使用的通知

自定义切面与使用spring接口实现切面类的区别:

  1. 使用spring接口实现切面类,它已经将切面类功能特向化了。也就是实现指定的接口只能在切入点前面或者后面或者环绕进行。而自定义切面类并没有指定的重写方法,所以只能将方法命名为before、after进行通知的设定。
    • 所以在xml中需要将切面显示声明出来(实现spring接口的方式spring已经封装了所以不需要做)
    • 使用的通知也需要显示声明出来(实现spring接口的方式spring封装了,并且spring特向化了所以不需要做)
    • 都需要声明一个切入点!

在这里插入图片描述

使用注解实现AOP

@Aspect
@Component
public class AnnotationCutFect {
    
    
    // 直接在通知上定义切入点,创建AnnotationCutFect对象时直接将切入点内置到里面。getBean时直接得到组合后的代理对象。
    @Before("execution(* pers.qiu.service.UserServiceImpl.*(..))")
    public void before(){
    
    
        System.out.println("方法执行前!");
    }
}
@Configuration
@ComponentScan({
    
    "pers.qiu.log", "pers.qiu.service"})
@Component
// 开启自动代理(必开,否则不会代理它)
@EnableAspectJAutoProxy
public class MyApplicationContext {
    
    
    @Bean
    public UserServiceImpl userService(){
    
    
        return new UserServiceImpl();
    }

    @Bean
    public AnnotationCutFect annotationCutFect(){
    
    
        return new AnnotationCutFect();
    }
}
public class MyTest {
    
    
    @Test
    public void test02(){
    
    
        ApplicationContext context = new AnnotationConfigApplicationContext(MyApplicationContext.class);
        UserService userService = (UserService) context.getBean("userService");
        AnnotationCutFect annotationCutFect = (AnnotationCutFect) context.getBean("annotationCutFect");
        userService.add();
    }
}

如果不在MyApplicationContext中开启自动代理,则需要使用xml进行开启配置并且使用XMLApplicationContext获取spring容器。(太麻烦,要不就全注解,要不就全配置文件,混搭八太行!)

<?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.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <aop:aspectj-autoproxy/>
</beans>

猜你喜欢

转载自blog.csdn.net/qq_43477218/article/details/113836563