【Spring】Spring IoC注解式开发

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
云数据库 RDS MySQL Serverless,价值2615元额度,1个月
简介: 【Spring】Spring IoC注解式开发

根据 【动力节点】最新Spring框架教程,全网首套Spring6教程,跟老杜从零学spring入门到高级 以及老杜的原版笔记 https://www.yuque.com/docs/share/866abad4-7106-45e7-afcd-245a733b073f?# 《Spring6》 进行整理, 文档密码:mg9b


Spring 相关文章整理汇总归纳于:https://www.yuque.com/u27599042/zuisie


  • Spring6 倡导全注解开发
  • 注解的存在主要是为了简化 XML 的配置。
  • 注解其实就是一个标记

回顾自定义注解

  • 元注解:标注注解的注解
  • @Target注解:用来修饰注解可以出现的位置
  • @Target(value = {ElementType.TYPE, ElementType.FIELD}),Target标注的注解可以出现在类或字段上
  • @Target(value = {ElementType.TYPE}),Target标注的注解可以出现在类上
  • 使用某个注解的时候,如果注解的属性名是value的话,value可以省略。
  • @Target({ElementType.TYPE})
  • 使用某个注解的时候,如果注解的属性值是数组,并且数组中只有一个元素,大括号可以省略。
  • @Target(ElementType.TYPE)
  • @Retention:也是一个元注解,用来标注注解最终可以保留到什么时刻
  • 如果注解要保存到class文件当中,并且可以被反射机制读取,则使用@Retention(RetentionPolicy.RUNTIME)
  • @Retention(RetentionPolicy.SOURCE),表示Retention标注的注解只能保留到源文件中,字节码和运行时看不到Retention标注的注解
  • 自定义注解与定义注解属性:
// 标注该注解可以用在类上
@Target(ElementType.TYPE)
// 标注@Component注解最终保留在class文件当中,并且可以被反射机制读取。
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
    // 定义注解的属性
    // String是属性类型
    // value是属性名
    String value();
    // 其他的属性
    // 属性类型String
    // 属性名是name
    //String name();
    // 数组属性
    // 属性类型是:String[]
    // 属性名:names
    //String[] names();
    //int[] ages();
    //int age();
}

回顾自定义注解的使用

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
    String value();
}
@Component("user")
public class User {}

通过反射获取注解

@org.junit.Test
public void testGetAnnotationByReflect() throws Exception{
    // 将类加载到JVM中
    Class<?> clazz = Class.forName("cw.study.spring.bean.User");
    // 判断类上是否有自定义的Component注解
    if (clazz.isAnnotationPresent(Component.class)) {
        // 如果有,获取该注解
        Component component = clazz.getAnnotation(Component.class);
        // 获取该类上注解的属性值
        System.out.println(component.value());
    }
}

组件扫描原理

  • 需求:给定一个包名,将这个包下的所有的带@Component注解的类进行实例化
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
    String value();
}
@Component("user")
public class User {}
@Component("cat")
public class Cat {}
public class Person {}
@org.junit.Test
public void componentScan() {
    // 已知的包名如下,需要扫描包下所有的类,将有Component注解的类实例化
    String packageName = "cw.study.spring.bean";
    // 包名其实就是目录名,将包名转化为目录
    // . 在正则中代表任意字符,需要进行转义,而 \\ 表示 \ 所以使用 \\.
    String directoryName = packageName.replaceAll("\\.", "/");
    // 软件包是在类路径下的,所以可以使用系统加载器加载目录获取URL
    URL url = ClassLoader.getSystemClassLoader().getResource(directoryName);
    // 通过URL获取包对应的绝对路径
    String path = url.getPath();
    System.out.println("path = " + path);
    // 根据绝对路径创建目录对应的对象
    File file = new File(path);
    // 获取目录下的文件对象
    File[] files = file.listFiles();
    System.out.println(files.length);
    // 遍历每个文件对象
    Arrays.stream(files).forEach(f -> {
        try {
            // 获取文件名
            String fName = f.getName();
            // System.out.println("fName = " + fName);
            // 得到类名
            String className = packageName + "." + fName.split("\\.")[0];
            // 加载类到JVM
            Class<?> clazz = Class.forName(className);
            // 判断类上是否有Component注解
            if (clazz.isAnnotationPresent(Component.class)) {
                // 获取注解
                Component component = clazz.getAnnotation(Component.class);
                // 获取注解的属性(Bean的id)
                String id = component.value();
                System.out.println(id);
                // 实例化对象
                Object o = clazz.getDeclaredConstructor().newInstance();
                System.out.println(o);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    });
}

声明 Bean 的注解

  • 负责声明Bean的注解,常见的包括四个:
  • @Component:组件
  • @Controller:控制器
  • @Service:业务
  • @Repository:仓库(Dao)
  • 上面四个注解中,@Controller,@Service,@Repository 都为 @Component 的别名(@AliasFor),其实这四个注解的功能都一样,用哪个都可以,但是在不同用途的 Bean 上使用不同的注解可以增强程序的可读性:
  • 普通 bean:Component
  • 控制器类上使用:Controller(控制层)
  • service类上使用:Service(业务层)
  • dao类上使用:Repository(持久层)
  • 上面四个注解中,都是只有一个value属性,用来指定 bean 的 id
@Target({ElementType.TYPE}) // 只能使用在类上
@Retention(RetentionPolicy.RUNTIME) // 可以通过反射获取该注解
@Documented
@Indexed
public @interface Component {
    String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
    // Component的别名
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
    // Component的别名
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {
    // Component的别名
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

Spring 注解的使用

第一步:加入aop的依赖

  • 当加入spring-context依赖之后,会自动关联加入aop的依赖

第二步:在配置文件中添加context命名空间

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

第三步:在配置文件中指定扫描的包

  • 在配置文件中指定扫描的包,告诉Spring需要进行扫描的包在什么位置
  • Spring会扫描指定的包中的类,及其子包中的类,如果类被声明Bean的注解标记,则会创建该类的实例并将其放到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:context="http://www.springframework.org/schema/context"
       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">
  <!-- 指定扫描的包,告诉Spring需要进行扫描的包在什么位置 -->
  <context:component-scan base-package="cw.study.spring"/>
</beans>

第四步:在Bean类上使用注解

@Component("user")
public class User {}
@Controller("userController")
public class UserController {}

测试

@org.junit.Test
public void test01() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    Object user = applicationContext.getBean("user");
    Object userController = applicationContext.getBean("userController");
    System.out.println(user);
    System.out.println(userController);
}

注意点

  • 如果注解的属性名是value,那么value是可以省略的。
  • 如果把value属性彻底去掉,spring会为Bean自动取名,默认名字的规律是:Bean类名首字母小写即可。
@Component
public class User {}
@Controller
public class UserController {}
@org.junit.Test
public void test02() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    Object user = applicationContext.getBean("user");
    Object userController = applicationContext.getBean("userController");
    System.out.println(user);
    System.out.println(userController);
}

  • 如果要对多个包进行扫描,有两种解决方案:
  • 第一种:在配置文件中指定多个包,用逗号隔开。
<context:component-scan base-package="cw.study.spring.bean, cw.study.spring.controller"/>
  • 第二种:指定多个包的共同父包。
  • Spring会扫描指定的包中的类,及其子包中的类
  • 使用此方式会牺牲一些效率
<context:component-scan base-package="cw.study.spring"/>

选择性实例化 Bean

  • 选择性实例化 Bean,就是只选择满足某些条件的类进行 bean 的实例化,或者排除满足某些条件的类,对这些类不进行 Bean 的实例化
package cw.spring.bean;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
/**
 * ClassName: A
 * Package: cw.spring.bean
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-14 11:42
 * @Version 1.0
 */
@Component
public class A {
    public A() {
        System.out.println("A的无参数构造方法执行");
    }
}
@Controller
class B {
    public B() {
        System.out.println("B的无参数构造方法执行");
    }
}
@Service
class C {
    public C() {
        System.out.println("C的无参数构造方法执行");
    }
}
@Repository
class D {
    public D() {
        System.out.println("D的无参数构造方法执行");
    }
}
@Controller
class E {
    public E() {
        System.out.println("E的无参数构造方法执行");
    }
}

方式一:**use-default-filters=“false” + **context:include-filter

  • use-default-filters=“false”:不使用 spring 默认的实例化规则,即所有带有声明 bean 的注解全部失效,用注解 Component、Controller、Service、Repository 标注的 bean 都不进行实例化(让所有的声明 bean 的注解失效)
  • use-default-filters 的默认值为 true
  • <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
  • 表示被 Component 注解标注的 bean 进行实例化
  • type 属性用于指定过滤类的条件的类型,即根据什么进行过滤,type=“annotation”,根据类的注解进行过滤
  • expression 属性用于指定过滤的条件,expression=“org.springframework.stereotype.Component”,只有被Component注解标注的类才进行实例化
  • 由于其他三个注解只是 Component 的别名,所以包含 Component 也就包含其他三种注解
  • 注意:使用context:include-filter,use-default-filters的属性值必须为false,否则context:include-filter无效
<!-- 指定要进行扫描的包 -->
<context:component-scan base-package="cw.spring.bean" use-default-filters="false">
  <!-- 被 Component 注解标注的 bean 进行实例化 -->
  <!-- 由于其他三个注解只是 Component 的别名,所以包含 Component 也就包含其他三种注解 -->
  <!-- <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/> -->
  <!-- 被 Controller 注解标注的 bean 进行实例化 -->
  <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

方式二:**use-default-filters=“true” + **context:exclude-filter

  • use-default-filters=“true”:使用 spring 默认的实例化规则,即所有带有声明 bean 的注解全部生效,用注解 Component、Controller、Service、Repository 标注的 bean 都进行实例化
  • use-default-filters 的默认值为 true
  • <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
  • 表示被 Component 注解标注的 bean 不进行实例化,排除被 Component 注解标注的 bean
  • 由于其他三个注解只是 Component 的别名,所以排除 Component 也就排除了其他三种注解
<!-- 指定要进行扫描的包 -->
<context:component-scan base-package="cw.spring.bean">
  <!-- 排除被 Component 注解标注的 bean,这些 bean 不参与实例化 -->
  <!-- 由于其他三个注解只是 Component 的别名,所以排除 Component 也就排除了其他三种注解 -->
  <!-- <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/> -->
  <!-- 排除被 Controller 注解标注的 bean,这些 bean 不参与实例化 -->
  <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

负责注入的注解

  • @Component @Controller @Service @Repository 这四个注解只是用来声明 Bean 的,声明后,这些 Bean 将会被实例化。
  • 给 Bean 的属性赋值,使用如下注解:
  • @Value
  • @Autowired
  • @Qualifier
  • @Resource

@Value

  • 用于简单类型注入
  • 当属性的类型是简单类型时,可以使用@Value注解进行注入。
  • @Value注解用于代替<property name="" value=""/>
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value {
  String value();
}
用在属性上
  • @value 可以直接写在属性上,可以不用提供对应的 set 方法
package cw.spring.bean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
 * ClassName: MyDataSource
 * Package: cw.spring.bean
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-14 12:13
 * @Version 1.0
 */
@Component
public class MyDataSource {
    @Value("com.mysql.cj.jdbc.Driver")
    private String driver;
    @Value("jdbc:mysql://localhost:3306/spring")
    private String url;
    @Value("root")
    private String username;
    @Value("123456")
    private String password;
    @Override
    public String toString() {
        return "MyDataSource{" + "driver='" + driver + '\'' + ", url='" + url + '\'' + ", username='" + username + '\'' + ", password='" + password + '\'' + '}';
    }
}
用在 set 方法上
  • @value 也可以写在属性对应的 set 方法上,实现属性值的注入
  • 为了简化代码,一般不提供 set 方法,直接在属性上使用@Value 注解完成属性赋值。
package cw.spring.bean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
 * ClassName: MyDataSource
 * Package: cw.spring.bean
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-14 12:13
 * @Version 1.0
 */
@Component
public class MyDataSource {
    private String driver;
    private String url;
    private String username;
    private String password;
    @Value("com.mysql.cj.jdbc.Driver")
    public void setDriver(String driver) {
        this.driver = driver;
    }
    @Value("jdbc:mysql://localhost:3306/spring")
    public void setUrl(String url) {
        this.url = url;
    }
    @Value("root")
    public void setUsername(String username) {
        this.username = username;
    }
    @Value("123456")
    public void setPassword(String password) {
        this.password = password;
    }
    @Override
    public String toString() {
        return "MyDataSource{" + "driver='" + driver + '\'' + ", url='" + url + '\'' + ", username='" + username + '\'' + ", password='" + password + '\'' + '}';
    }
}
用在构造方法的形参上
  • @Value 注解也可以用在构造方法的形参上
package cw.spring.bean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
 * ClassName: MyDataSource
 * Package: cw.spring.bean
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-14 12:13
 * @Version 1.0
 */
@Component
public class MyDataSource {
    private String driver;
    private String url;
    private String username;
    private String password;
    public MyDataSource(
            @Value("com.mysql.cj.jdbc.Driver") String driver, 
            @Value("jdbc:mysql://localhost:3306/spring") String url, 
            @Value("root") String username, 
            @Value("123123") String password
    ) {
        this.driver = driver;
        this.url = url;
        this.username = username;
        this.password = password;
    }
    @Override
    public String toString() {
        return "MyDataSource{" + "driver='" + driver + '\'' + ", url='" + url + '\'' + ", username='" + username + '\'' + ", password='" + password + '\'' + '}';
    }
}

@Autowired

  • @Autowired 注解用来注入非简单类型
  • Autowired:自动连线,或自动装配
  • @Autowired,通过注解的方式进行自动装配
  • @Autowired 注解不需要 set 方法
  • 单独使用 @Autowired 注解,默认根据类型进行装配,即默认是 byType,如果需要根据名字进行自动装配则需要配合 @Qualifier 注解
  • @Autowired 注解是根据类型进行自动装配,如果只使用@Autowired 注解的话,Spring容器中不能存在两个相同类型的实例,如果要使用@Autowired 注解并且Spring容器中存在两个相同类型的实例,则需要配合 @Qualifier 注解,根据名称进行装配
  • 该注解可以标注在构造方法上、方法上、形参上、属性上、注解上,用法书写位置和@Value一样
  • 该注解有一个 required 属性,默认值是 true,表示在注入的时候要求被注入的 Bean 必须是存在的,如果不存在则报错。如果 required 属性设置为 false,表示注入的 Bean 存在或者不存在都没关系,存在的话就注入,不存在的话,也不报错。
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    boolean required() default true;
}
  • @Autowired 注解使用的时候,可以不指定任何属性,直接使用
/**
 * ClassName: OrderDao
 * Package: cw.spring.dao
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-14 12:50
 * @Version 1.0
 */
public interface OrderDao {
    void insert();
}
/**
 * ClassName: OrderDaoImplForMySQL
 * Package: cw.spring.dao.impl
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-14 12:50
 * @Version 1.0
 */
@Repository
public class OrderDaoImplForMySQL implements OrderDao {
    @Override
    public void insert() {
        System.out.println("MySQL数据库正在保存订单信息...");
    }
}
import cw.spring.dao.OrderDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
 * ClassName: OrderService
 * Package: cw.spring.service
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-14 12:49
 * @Version 1.0
 */
@Service("orderService")
public class OrderService {
    @Autowired
    private OrderDao orderDao;
    public void save() {
        orderDao.insert();
    }
}
  • 当类中构造方法只有一个时,并且构造方法上的参数和需要注入的属性能够对应上,@Autowired 注解可以省略,如果有多个构造方法,@Autowired 不能省略的。
  • 最好不要省略,程序的可读性更高
/**
 * ClassName: OrderService
 * Package: cw.spring.service
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-14 12:49
 * @Version 1.0
 */
@Service("orderService")
public class OrderService {
    // @Autowired
    // @Qualifier("orderDaoImplForOracle")
    private OrderDao orderDao;
    public OrderService(OrderDao orderDao) {
        this.orderDao = orderDao;
    }
    public void save() {
        orderDao.insert();
    }
}

@Qualifier

  • @Autowired 注解和 @Qualifier 注解联合起来才可以根据名称进行装配,在@Qualifier注解中指定Bean名称。
/**
 * ClassName: OrderDaoImplForOracle
 * Package: cw.spring.dao.impl
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-14 12:59
 * @Version 1.0
 */
@Repository
public class OrderDaoImplForOracle implements OrderDao {
    @Override
    public void insert() {
        System.out.println("Oracle数据库正在保存订单信息...");
    }
}
/**
 * ClassName: OrderService
 * Package: cw.spring.service
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-14 12:49
 * @Version 1.0
 */
@Service("orderService")
public class OrderService {
    @Autowired
    @Qualifier("orderDaoImplForOracle")
    private OrderDao orderDao;
    public void save() {
        orderDao.insert();
    }
}

@Resource

  • @Resource注解可以完成非简单类型注入。
  • @Resource注解是JDK扩展包中的,也就是说属于JDK的一部分,而@Autowired注解是Spring框架自己的。
  • @Resource是JDK标准规范中的,更具有通用性,更推荐使用
  • @Resource注解默认根据名称装配byName,未指定name时,使用属性名作为name。通过name找不到的话会自动启动通过类型byType装配。
  • @Resource注解用在属性上、setter方法上、方法上
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Resources.class)
public @interface Resource {
    String name() default "";
    String lookup() default "";
    Class<?> type() default Object.class;
    AuthenticationType authenticationType() default Resource.AuthenticationType.CONTAINER;
    boolean shareable() default true;
    String mappedName() default "";
    String description() default "";
    public static enum AuthenticationType {
        CONTAINER,
        APPLICATION;
        private AuthenticationType() {
        }
    }
}
  • @Resource注解属于JDK扩展包,所以不在JDK当中,需要额外引入以下依赖
  • 引入扩展依赖
  • 如果是JDK8的话不需要额外引入依赖,高于JDK11或低于JDK8需要引入以下依赖。
<dependency>
  <groupId>jakarta.annotation</groupId>
  <artifactId>jakarta.annotation-api</artifactId>
  <version>2.1.1</version>
</dependency>
<dependency>
  <groupId>javax.annotation</groupId>
  <artifactId>javax.annotation-api</artifactId>
  <version>1.3.2</version>
</dependency>
/**
 * ClassName: StudentDao
 * Package: cw.spring.dao
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-14 16:03
 * @Version 1.0
 */
public interface StudentDao {
    void deleteById();
}
/**
 * ClassName: StudentDaoImplForMySQL
 * Package: cw.spring.dao.impl
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-14 16:04
 * @Version 1.0
 */
@Repository
public class StudentDaoImplForMySQL implements StudentDao {
    @Override
    public void deleteById() {
        System.out.println("MySQL数据库正在删除学生信息...");
    }
}
/**
 * ClassName: StudentService
 * Package: cw.spring.service
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-14 16:05
 * @Version 1.0
 */
@Service
public class StudentService {
    // name属性用于指定将要被注入到该属性的Bean的名字
    @Resource(name = "studentDaoImplForMySQL")
    private StudentDao studentDao;
    public void delete() {
        studentDao.deleteById();
    }
}

全注解开发

  • 全注解开发就是不再使用 spring 配置文件,写一个配置类来代替配置文件。

@Configuration 注解标注配置类

  • 配置类使用 @Configuration 注解进行标注
@Configuration
public class Spring6Config {
}

@ComponentScan 注解配置扫描的包

  • 通过 @ComponentScan 注解配置要扫描的包
@Configuration
@ComponentScan({"cw.spring.dao", "cw.spring.service"})
public class Spring6Config {
}

全注解开发下获取 Spring 容器

  • 获取 spring 容器不再 new ClassPathXmlApplicationContext() 对象了,而是 new AnnotationConfigApplicationContext()
@org.junit.Test
public void test03() {
    // 获取Spring容器对象时,需要传配置类为参数
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring6Config.class);
    StudentService studentService = applicationContext.getBean("studentService", StudentService.class);
    studentService.delete();
}


相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
4天前
|
运维 Java 程序员
Spring5深入浅出篇:基于注解实现的AOP
# Spring5 AOP 深入理解:注解实现 本文介绍了基于注解的AOP编程步骤,包括原始对象、额外功能、切点和组装切面。步骤1-3旨在构建切面,与传统AOP相似。示例代码展示了如何使用`@Around`定义切面和执行逻辑。配置中,通过`@Aspect`和`@Around`注解定义切点,并在Spring配置中启用AOP自动代理。 进一步讨论了切点复用,避免重复代码以提高代码维护性。通过`@Pointcut`定义通用切点表达式,然后在多个通知中引用。此外,解释了AOP底层实现的两种动态代理方式:JDK动态代理和Cglib字节码增强,默认使用JDK,可通过配置切换到Cglib
|
10天前
|
Java 测试技术 开发者
Spring IoC容器通过依赖注入机制实现控制反转
【4月更文挑战第30天】Spring IoC容器通过依赖注入机制实现控制反转
21 0
|
1天前
|
前端开发 安全 Java
使用Spring框架加速Java开发
使用Spring框架加速Java开发
5 0
|
2天前
|
JavaScript Java 开发者
Spring Boot中的@Lazy注解:概念及实战应用
【4月更文挑战第7天】在Spring Framework中,@Lazy注解是一个非常有用的特性,它允许开发者控制Spring容器的bean初始化时机。本文将详细介绍@Lazy注解的概念,并通过一个实际的例子展示如何在Spring Boot应用中使用它。
14 2
|
3天前
|
传感器 人工智能 前端开发
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
智慧校园电子班牌,坐落于班级的门口,适合于各类型学校的场景应用,班级学校日常内容更新可由班级自行管理,也可由学校统一管理。让我们一起看看,电子班牌有哪些功能呢?
43 4
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
|
4天前
|
前端开发 Java
SpringBoot之自定义注解参数校验
SpringBoot之自定义注解参数校验
14 2
|
10天前
|
安全 Java 开发者
在Spring框架中,IoC和AOP是如何实现的?
【4月更文挑战第30天】在Spring框架中,IoC和AOP是如何实现的?
21 0
|
10天前
|
XML Java 程序员
什么是Spring的IoC容器?
【4月更文挑战第30天】什么是Spring的IoC容器?
19 0
|
10天前
|
Java 测试技术 开发者
【亮剑】通过自定义注解实现Spring AOP,可以更灵活地控制方法拦截和增强
【4月更文挑战第30天】通过自定义注解实现Spring AOP,可以更灵活地控制方法拦截和增强。首先定义自定义注解,如`@MyCustomAnnotation`,然后创建切面类`MyCustomAspect`,使用`@Pointcut`和`@Before/@After`定义切点及通知。配置AOP代理,添加`@EnableAspectJAutoProxy`到配置类。最后,在需拦截的方法上应用自定义注解。遵循保持注解职责单一、选择合适保留策略等最佳实践,提高代码可重用性和可维护性。记得测试AOP逻辑。
|
10天前
|
Java Spring
springboot自带的@Scheduled注解开启定时任务
springboot自带的@Scheduled注解开启定时任务