Spring基础(下)

简介: Spring基础(下)

注解开发

创建一个配置类:

@Configuration
public class MainConfiguration {
}


配置Bean:

@Configuration
public class MainConfiguration {
    @Bean("student")
    public Student student(){
        return new Student();
    }
}


获取Bean对象:

ApplicationContext context = new AnnotationConfigApplicationContext(MainConfiguration.class);
Student student = context.getBean(Student.class);
System.out.println(student);


其他属性配置:

@Bean(name = "", initMethod = "", destroyMethod = "", autowireCandidate = false)
public Student student(){
    return new Student();
}


@Bean
@Lazy(true)     //对应lazy-init属性
@Scope("prototype")    //对应scope属性
@DependsOn("teacher")    //对应depends-on属性
public Student student(){
    return new Student();
}

通过构造方法或是Setter完成依赖注入的Bean:

@Configuration
public class MainConfiguration {
    @Bean
    public Teacher teacher(){
        return new Teacher();
    }
    @Bean
    public Student student(Teacher teacher){
        return new Student(teacher);
//        return new Student().setTeacher(teacher);
    }
}


直接到Bean对应的类中使用自动装配:

public class Student {
    @Autowired   //使用此注解来进行自动装配,由IoC容器自动为其赋值
    private Teacher teacher;
}


甚至连构造方法和Setter都不需要去编写了,就能直接完成自动装配

@Autowired并不是只能用于字段,对于构造方法或是Setter,它同样可以:

public class Student {
    private Teacher teacher;
    @Autowired
    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }
}


@Autowired默认采用byType的方式进行自动装配

发生歧义时配合@Qualifier进行名称匹配:

public class Student {
    @Autowired
    @Qualifier("a")   //匹配名称为a的Teacher类型的Bean
    private Teacher teacher;
}


@Resource,它的作用与@Autowired时相同的,也可以实现自动装配,但是在IDEA中并不推荐使用@Autowired注解对成员字段进行自动装配,而是推荐使用@Resource,如果需要使用这个注解,还需要额外导入包:

<dependency>
    <groupId>jakarta.annotation</groupId>
    <artifactId>jakarta.annotation-api</artifactId>
    <version>2.1.1</version>
</dependency>
  • @Resource默认ByName如果找不到则ByType,可以添加到set方法、字段上。
  • @Autowired默认是byType,只会根据类型寻找,可以添加在构造方法、set方法、字段、方法参数上。

@PostConstruct和@PreDestroy,它们效果和init-method和destroy-method是一样的:

@PostConstruct
public void init(){
    System.out.println("我是初始化方法");
}
@PreDestroy
public void destroy(){
    System.out.println("我是销毁方法");
}


使用@Bean来注册Bean,只是简单将一个类作为Bean的话,就是单纯的new一个对象出来,不能像之前一样让容器自己反射获取构造方法去生成这个对象

@Component注册Bean:

@Component("lbwnb")   //同样可以自己起名字
public class Student {
}


配置一下包扫描:

@Configuration
@ComponentScan("com.test.bean")   //包扫描,这样Spring就会去扫描对应包下所有的类
public class MainConfiguration {
}


Spring在扫描对应包下所有的类时,会自动将那些添加了@Component的类注册为Bean

无论是通过@Bean还是@Component形式注册的Bean,Spring都会为其添加一个默认的name属性

@Component默认名称生产规则依然是类名并按照首字母小写的驼峰命名法来的

@Component
public class Student {
}


Student student = (Student) context.getBean("student");   //这样同样可以获取到
System.out.println(student);

@Bean注册的默认名称是对应的方法名称

@Bean
public Student artStudent(){
    return new Student();
}


Student student = (Student) context.getBean("artStudent");
System.out.println(student);

@Component注册的Bean,如果其构造方法不是默认无参构造,那么默认会对其每一个参数都进行自动注入:

Spring也提供了接口,我们可以直接实现接口表示这个Bean是一个工厂Bean:

@Component
public class StudentFactory implements FactoryBean<Student> {
    @Override
    public Student getObject() {   //生产的Bean对象
        return new Student();
    }
    @Override
    public Class<?> getObjectType() {   //生产的Bean类型
        return Student.class;
    }
    @Override
    public boolean isSingleton() {   //生产的Bean是否采用单例模式
        return false;
    }
}


AOP面向切片


AOP(Aspect Oriented Programming)思想实际上就是:在运行时,动态地将代码切入到类的指定方法、指定位置上。

可以使用AOP来帮助我们在方法执行前或执行之后,做一些额外的操作,实际上,它就是代理。

通过AOP我们可以在保证原有业务不变的情况下,,在不改变源代码的基础上进行了增强处理。

aop环境建立:

<?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">
</beans>


<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>6.0.10</version>
</dependency>

配置实现AOP

实体类:

public class Student {
    public void study(){
        System.out.println("狠狠的学Java");
    }
}


代理类:

public class StudentAOP {
    public void afterStudy(){
        System.out.println("hahahaha");
    }
}


Bean注册:

    <bean class="com.test.entity.Student"/>
    <bean id="studentAop" class="com.test.entity.StudentAOP"/>
    <aop:config>
        <aop:pointcut id="test" expression="execution(public void com.test.entity.Student.study())"/>
        <aop:aspect ref="studentAop">
            <aop:after method="afterStudy" pointcut-ref="test"/>
        </aop:aspect>
    </aop:config>


调用效果:

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("test.xml");
        Student bean = context.getBean(Student.class);
        bean.study();
    }
}


b8cb8d7fae6332b59836e19114a543ed.png

说明:

实体和代理类都得注册为Bean,然后进行aop配置

添加一个新的切点,首先填写ID,再通过后面的expression表达式来选择到我们需要切入的方法

Spring AOP支持以下AspectJ切点指示器(PCD)用于表达式:

  • execution:用于匹配方法执行连接点。这是使用Spring AOP时使用的主要点切割指示器。
  • within:限制匹配到某些类型的连接点(使用Spring AOP时在匹配类型中声明的方法的执行)。
  • this:限制与连接点匹配(使用Spring AOP时方法的执行),其中bean引用(Spring AOP代理)是给定类型的实例。
  • target:限制匹配连接点(使用Spring AOP时方法的执行),其中目标对象(正在代理的应用程序对象)是给定类型的实例。
  • args:限制与连接点匹配(使用Spring AOP时方法的执行),其中参数是给定类型的实例。
  • @target:限制匹配连接点(使用Spring AOP时方法的执行),其中执行对象的类具有给定类型的注释。
  • @args:限制匹配到连接点(使用Spring AOP时方法的执行),其中传递的实际参数的运行时类型具有给定类型的注释。
  • @within:限制与具有给定注释的类型中的连接点匹配(使用Spring AOP时在带有给定注释的类型中声明的方法的执行)。
  • @annotation:与连接点主体(在Spring AOP中运行的方法)具有给定注释的连接点匹配的限制。


execution填写格式如下:

修饰符 包名.类名.方法名称(方法参数)


  • 修饰符:public、protected、private、包括返回值类型、static等等(使用*代表任意修饰符)
  • 包名:如com.test(* 代表全部,比如com.*代表com包下的全部包)
  • 类名:使用*也可以代表包下的所有类
  • 方法名称:可以使用*代表全部方法
  • 方法参数:填写对应的参数即可,比如(String, String),也可以使用*来代表任意一个参数,使用…代表所有参数。


添加aop:aspect标签,并使用ref属性将其指向我们刚刚注册的AOP类Bean

添加后续动作了,当然,官方支持的有多种多样的,比如执行前、执行后、抛出异常后、方法返回后等等

Spring通过CGLib为我们生成的动态代理类,使得调用方法会直接得到增强

JoinPoint信息获取:

对于切入的方法添加一个JoinPoint参数,通过此参数就可以快速获取切点位置的一些信息:

public class Student {
    public void study(String str){
        System.out.println("狠狠的学"+str);
    }
}
public class StudentAOP {
    public void afterStudy(JoinPoint point){
        System.out.println("hahahaha"+point.getArgs()[0]+"无敌");
    }
}
public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("test.xml");
        Student bean = context.getBean(Student.class);
        bean.study("java");
    }
}


<aop:pointcut id="test" expression="execution(public void com.test.entity.Student.study(String))"/>//指定具体参数类型
<aop:pointcut id="test" expression="execution(public void com.test.entity.Student.study(..))"/>//模糊代指

环绕方法:

环绕方法相当于完全代理了此方法,它完全将此方法包含在中间,需要我们手动调用才可以执行此方法,并且我们可以直接获取更多的参数

通过proceed方法来执行代理的方法,也可以修改参数之后调用proceed(Object[]),在调用之后对返回值结果也进行处理

    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("before");
        String arg = joinPoint.getArgs()[0]+"666";
        Object ret = joinPoint.proceed(new Object[]{arg});
        System.out.println("after");
        return ret;
    }


        <aop:aspect ref="studentAop">
            <aop:around method="around" pointcut-ref="test"/>
        </aop:aspect>

908d55d5db0c0b961674cbfc059945c3.png

AOP 领域中的特性术语:

  • 通知(Advice): AOP 框架中的增强处理,通知描述了切面何时执行以及如何执行增强处理,也就是我们上面编写的方法实现。
  • 连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出,实际上就是我们在方法执行前或是执行后需要做的内容。
  • 切点(PointCut): 可以插入增强处理的连接点,可以是方法执行之前也可以方法执行之后,还可以是抛出异常之类的。
  • 切面(Aspect): 切面是通知和切点的结合,我们之前在xml中定义的就是切面,包括很多信息。
  • 引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。
  • 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,我们之前都是在将我们的增强处理添加到目标对象,也就是织入


接口实现AOP

将一个类实现Advice接口,只有实现此接口,才可以被通知

使用MethodBeforeAdvice表示是一个在方法执行之前的动作;AfterReturningAdvice就需要实现一个方法执行之后的操作

public class StudentAOP implements MethodBeforeAdvice, AfterReturningAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("before:");
    }
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("after-return");
    }
}


        <aop:advisor advice-ref="studentAop" pointcut-ref="test"/>

使用MethodInterceptor(同样也是Advice的子接口)进行更加环绕那样的自定义的增强

public class StudentAOP implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("haha");
        Object ret = invocation.proceed();
        System.out.println("wuwu");
        return ret;
    }
}


注解实现AOP

实体类:

@Component//注册为Bean
public class Student {
    public void study(String str){
        System.out.println("狠狠的学"+str);
    }
}


代理类:

@Aspect//声明为切面
@Component//注册为Bean
public class StudentAOP {
    @Before("execution(* com.test.entity.Student.study(..))")
    public void before(JoinPoint point){
        System.out.println("before");
        System.out.println("args:"+Arrays.toString(point.getArgs()));
    }
    @After(value = "execution(* com.test.entity.Student.study(..)) && args(str)",argNames = "str")
    //命名绑定模式就是根据下面的方法参数列表进行匹配
//这里args指明参数,注意需要跟原方法保持一致,然后在argNames中指明
    public void after(String str){//获取指定参数
        System.out.println("get arg str:" + str);
        System.out.println("after");
    }
}


配置类:

@EnableAspectJAutoProxy//开启aop代理
@ComponentScan("com.test.entity")//扫描bean实体类
@Configuration//声明配置类
public class MainConfiguration {
}


调用:

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfiguration.class);
        Student bean = context.getBean(Student.class);
        bean.study("java");
    }
}


除了@Before,还有很多可以直接使用的注解,比如@AfterReturning、@AfterThrowing等

环绕注解:

@Around("execution(* com.test.bean.Student.test(..))")
public Object around(ProceedingJoinPoint point) throws Throwable {
    System.out.println("方法执行之前!");
    Object val = point.proceed();
    System.out.println("方法执行之后!");
    return val;
}


void after(String str){//获取指定参数

System.out.println(“get arg str:” + str);

System.out.println(“after”);

}

}

配置类:
~~~java
@EnableAspectJAutoProxy//开启aop代理
@ComponentScan("com.test.entity")//扫描bean实体类
@Configuration//声明配置类
public class MainConfiguration {
}


调用:

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfiguration.class);
        Student bean = context.getBean(Student.class);
        bean.study("java");
    }
}


除了@Before,还有很多可以直接使用的注解,比如@AfterReturning、@AfterThrowing等

环绕注解:

@Around("execution(* com.test.bean.Student.test(..))")
public Object around(ProceedingJoinPoint point) throws Throwable {
    System.out.println("方法执行之前!");
    Object val = point.proceed();
    System.out.println("方法执行之后!");
    return val;
}


相关文章
|
6月前
|
开发框架 前端开发 Java
Spring和Spring Boot:两者的区别与应用场景
Spring和Spring Boot:两者的区别与应用场景
173 1
|
6月前
|
前端开发 Java 数据库连接
【Spring-01】Spring简介
【Spring-01】Spring简介
|
6月前
|
前端开发 Java 数据库连接
|
11月前
|
XML 设计模式 Java
Spring基础(上)
Spring基础(上)
|
Java 数据库连接 API
01Spring - Spring简介
01Spring - Spring简介
37 0
|
安全 Java 程序员
【Spring】Spring 框架的概述
【Spring】Spring 框架的概述
|
前端开发 Java 数据库连接
Spring-Spring4.X 概述
Spring-Spring4.X 概述
108 0
|
缓存 监控 安全
Spring Boot框架基础介绍
Spring Boot 是一款基于 Spring 框架的开源应用程序开发工具,它旨在简化 Spring 应用程序的配置和开发过程。Spring Boot 提供了一种简单的方式来创建可独立运行的、生产级别的应用程序,并在需要时进行部署。Spring Boot 在微服务架构和云计算环境下得到了广泛应用,本文将介绍 Spring Boot 的特性、优势以及使用方法。
2288 0
|
Java Spring
spring学习5-spring简介
spring学习5-spring简介
87 0
spring学习5-spring简介
|
XML 缓存 Java
Spring系列二:基础篇(2)
​ 目录 IOC IOC 5.说一说什么是IOC?什么是DI? Java 是面向对象的编程语言,一个个实例对象相互合作组成了业务逻辑,原来,我们都是在代码里创建对象和对象的依赖。 所谓的IOC(控制反转):就是由容器来负责控制对象的生命周期和对象间的关系。以前是我们想要什么,就自己创建什么,现在是我们需要什么,容器就给我们送来什么。 引入IOC之前和引入IOC之后 也就是说,控制对象生命周期的不再是引用它的对象,而是容器。对具体对象,以前是它控制其它对象,现在所有对象都被容器控制,所以这就叫控制反转。 控制反转示意图 DI(依赖注入):指的是容器在实例化对象的时候把
104 0
Spring系列二:基础篇(2)