4-5 spring framwork

IOC 控制反转

  • IOC 控制反转,指的是将对象的创建权交给 Spring容器。
  • Spring Bean就是Spring容器创建管理的Java 对象;
  • Spring 通过读取 XML 或 Java 注解中的信息(bean)来获取哪些对象需要实例化

bean的作用域

  • Spring 容器在初始化一个 Bean 实例时,同时会指定该实例的作用域。
    在这里插入图片描述

注解

  • 注解是代码的特殊标记,格式:@注解名(属性名=属性值,属性名=属性值,属性名=属性值…)
  • 注解可作用在类上面、属性上面、方法上面。
  • 使用注解的目的:简化xml配置。
  • IOC容器会自动扫描包路径下的所有被@Controller、@Service、 @Repository、@Component四个注解声明的类,并为其创建对象。
  • 类名上被这四个注解修饰,就等价于在xml文件中配置了这个类的bean。
@Controller一般声明controller层的类
@Service一般声明Service层的类
@Repository一般声明dao层的类
@Component其他没有特殊要求的层用该注解修饰

以@Component为例,其他三个注解同理。

/*
	1.只有一个属性值时默认是value的值,可以省略 value= 
	2.如果不指定value,默认为匈牙利命名的类名或大驼峰命名的类名的小驼峰。
	3.此处的value值即xml配置文件中bean的id
*/
@Component(value="student")	//同@Component("student"),同@Component
public class Student {
	@Autowired	//byType
    private Class class;
    @Autowired
    @Qualifier(value="group")	//byName
    private Group group;
    @Resource(name="time")	//byName、byType
    private Time time;
    @Value(value="wang")	//普通数据类型注入
    private String name;

    public Student() {
    }
}
  • 注入属性注解有四个:
    @Autowired、@Qualifier、@Resource、@Value
  • 加了注入属性注解后就不需要提供setter方法了。
  • @Autowired用在接口类型的成员变量上时会查找接口的实现类来注入,如果该接口有多个实现类则报错。该注解不设置属性。
  • @Resource找不到会再按类型找,@Qualifier找不到会报异常且要与@Autowired联用。这两个一般用在有多个实现类的情况。
  • @Qualifier使用属性value,可以省略 “value=”,@Resource使用属性name,但不可省略 “name=”,注解有value属性时才可以简写省略。
依赖注入
  • DI(Dependency Injection) 依赖注入,从容器中获取指定类的对象,主要指属性注入。
    @Autowired //根据类型从容器中注入对象
    @Resource //默认根据变量名称(对象名)注入,当找不到与名称匹配的bean时才会按类型注入

AOP 面向切面编程

  • AOP将应用中的一些非业务的通用功能封装起来单独维护,然后通过xml配置或者注解的方式来定义这些功能的使用方式,而不是在业务逻辑代码中直接调用。
  • 应用场景:日志记录,事务管理,权限验证,效率检查。

常用概念

连接点:程序执行期间的某个点,类里面可以被增强的方法。
切入点:一个匹配连接点(Join point)的谓词表达式,指定实际真正被增强的方法。
通知:指一个切面在特定的连接点要做的事情。
切面:它是通知和切点合起来的抽象。
织入:把通知应用到切入点的过程。

实现代码

  • 导入依赖 pom.xml
<!-- spring-aop -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>${spring.version}</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-expression</artifactId>
  <version>${spring.version}</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>${spring.version}</version>
</dependency>

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

<dependency>
  <groupId>commons-logging</groupId>
  <artifactId>commons-logging</artifactId>
  <version>1.2</version>
</dependency>

xml配置实现

  • web.xml配置
<!--指定Spring配置文件的位置,有多个配置文件时,以逗号分隔-->
<context-param>
  <param-name>contextConfigLocation</param-name>
  <!--spring将加载spring目录下的applicationContext.xml文件-->
  <param-value>classpath:spring.xml</param-value>
</context-param>

<!--指定以ContextLoaderListener方式启动Spring容器-->
<listener>
  <listener-class>
    org.springframework.web.context.ContextLoaderListener
  </listener-class>
</listener>
  • spring配置 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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.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">

    <!-- IOC 自动扫描service层,所有被@Service声明的类将会被扫描到-->
    <context:component-scan base-package="com.st.service" />
    <!-- IOC 自动扫描dao层,所有被@Repository声明的类将会被扫描到-->
    <context:component-scan base-package="com.st.dao" />
    <!-- IOC 自动扫描其他业务层,所有被@Component注解声明的类将会被扫描到-->
    <context:component-scan base-package="com.st.model" />


    <!-- AOP配置 -->
    <!-- 用bean配置-->
    <bean name="aopUtil" class="com.st.util.AopUtil"></bean>
    <aop:config>
        <!-- 1 定义切面(类) -->
        <!-- ref 关联jave bean的name属性-->
        <aop:aspect id="myAspect" ref="aopUtil">
            <!-- 2 定义切入点
        execution(* com.st.service.*.get*(..)
        第一个* 表示任意返回值类型等
        第二个* 表示com.st.service包下任意类
        第三个* 表示该类的任意以get开头的方法
        (..) 表示任意个数的参数列表;
            (*)代表只有一个参数,参数类型为任意类型;
            (*,String)代表有两个参数,第一个参数可以为任何值,第二个为 String 类型的值
        -->
            <aop:pointcut id="myPointCut" expression="execution(* com.st.service.*.get*(..))"/>
            <!-- 3 定义通知  把通知切入到切入点的指定位置,称为织入-->
            <!--
                前置通知 before
                    在目标方法执行之前执行的通知。前置通知方法,可以没有参数。
                后置通知 after-returning

                环绕通知 around
                    在目标方法执行之前和之后都可以执行额外代码的通知
                    环绕通知需要返回返回值,否则真正调用者将拿不到返回值,只能得到一个null。

                异常通知 after-throwing
                最终通知 after 先走切入点方法,切面方法,切入点方法return
            -->
            <!--
            织入方式:
                aop:before
                aop:after
                aop:around
                aop:after-returning
                aop:after-throwing
            属性:
                pointcut-ref 关联切入点
                method 切面类,切入的方法
            -->
            <aop:before pointcut-ref="myPointCut" method="speakBefore" />
        </aop:aspect>
    </aop:config>
</beans>
  • 切面类
package com.st.util;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;

public class AopUtil {
    /*
    环绕通知 around
        1 作用:在目标方法执行之前和之后都可以执行额外代码的通知
        2 用法:
           环绕通知需要有返回值,否则真正调用者将拿不到返回值,只能得到一个null;
           在环绕通知中必须显式的调用目标方法,目标方法才会执行,这个显式调用是通过ProceedingJoinPoint来实现的;
           环绕通知有控制目标方法是否执行、有控制是否返回值、有改变返回值的能力。
        3 应用场景:控制事务 权限控制
     */
    public String speakAround(ProceedingJoinPoint joinPoint) {
        System.out.println("aop-----around-----before");
        //开启事务
        try {
            //执行目标方法
            Object obj = joinPoint.proceed();
            System.out.println(obj);
            //提交事务
        } catch (Throwable e) {
            e.printStackTrace();
            //回滚事务
        }
        System.out.println("aop-----around-----after");
        return "@@@@@aop@@@@@";
    }
    /*
    前置通知 before
        1 作用:在目标方法执行之前执行的通知
        2 用法:
            前置通知方法,可以没有参数;
            也可以额外接收一个JoinPoint类型参数,通过该对象可以获取目标对象 和 目标方法相关的信息;
            如果接收JoinPoint,必须保证其为方法的第一个参数,否则报错
        3 应用场景:日志
     */
    public void speakBefore(){
        System.out.println("aop-----before");
    }

    /*
    后置通知 after-returning
        1 作用:在目标方法执行成功(即不报异常)之后执行的通知。
        2 用法:
            后置通知方法,可以没有参数;
            也可以接收一个JoinPoint参数,通过该对象可以获取目标对象 和 目标方法相关的信息;
            如果接收JoinPoint,必须保证其为方法的第一个参数,否则报错
        3 应用场景:日志
     */
    public void speakAfterReturn(JoinPoint jp){
        //获取对象Class类
        Class clz = jp.getTarget().getClass();
        System.out.println(clz.getName());
        //获取切入点对象信息
        Signature signature = jp.getSignature();
        //获取切入点名称
        String name = signature.getName();
        System.out.println(name);
        System.out.println("aop-----after-returning");
    }

    /*
    最终通知 after
        1 作用:在目标方法执行之后(无论执行成功还是异常)执行的通知
        2 应用场景:日志
     */
    public void speakAfter(){
        System.out.println("aop-----after");
    }

    /*
    异常通知 after-throwing
        1 作用:在目标方法抛出异常时执行的通知
        2 用法:
            可以配置传入JoinPoint获取目标对象和目标方法相关信息,但必须处在参数列表第一位
            还可以配置参数,让异常通知可以接收到目标方法抛出的异常对象。
        3 应用场景:异常处理、控制事务
     */
    public void speakException(JoinPoint jp,Throwable e){
        Class clz = jp.getTarget().getClass();
        String name = jp.getSignature().getName();
        System.out.println("aop-----after-throwing..["+clz+"]..["+name+"].."+e.getMessage());
    }
}

注解实现

  • 自定义注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.METHOD})
public @interface MyAnnotation {

    /**
     * 注解属性
     */
    String myField();
}
  • 定义切面类
@Aspect
@Component
public class MyAspect {
	@Before(value = "@annotation(myAnnotation)")
    public void doBeforeMethodInvoked(JoinPoint joinPoint, MyAnnotation myAnnotation) {
    	String name = (String)joinPoint.getArgs()[0];	// 第一个参数
        LocalDate birth = (LocalDate) joinPoint.getArgs()[1];	// 第二个参数
        System.out.println(myAnnotation.myField());
		System.out.println(name + LocalDate.now());
    }

	@AfterReturning(pointcut = "@annotation(myAnnotation)", returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, MyAnnotation myAnnotation, String result) {
    	String name = (String)joinPoint.getArgs()[0];	// 第一个参数
        LocalDate birth = (LocalDate) joinPoint.getArgs()[1];	// 第二个参数
        result = "aspect success !!";
	}
}
  • 应用切面
public class Main1 {
	@MyAnnotation(myField="inMethod1")
    public static String method1(String name, LocalDate birthDate) {
    	return "native result";
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值